Wikipedia Thuật toán tìm đường * mất rất nhiều thời gian


9

Tôi đã thực hiện thành công tìm đường dẫn A * trong C # nhưng nó rất chậm và tôi không hiểu tại sao. Tôi thậm chí đã cố gắng không sắp xếp danh sách openNodes nhưng nó vẫn giống nhau.

Bản đồ có kích thước 80x80 và có 10-11 nút.

Tôi lấy mã giả từ đây Wikipedia

Và đây là triển khai của tôi:

 public static List<PGNode> Pathfind(PGMap mMap, PGNode mStart, PGNode mEnd)
    {
        mMap.ClearNodes();

        mMap.GetTile(mStart.X, mStart.Y).Value = 0;
        mMap.GetTile(mEnd.X, mEnd.Y).Value = 0;

        List<PGNode> openNodes = new List<PGNode>();
        List<PGNode> closedNodes = new List<PGNode>();
        List<PGNode> solutionNodes = new List<PGNode>();

        mStart.G = 0;
        mStart.H = GetManhattanHeuristic(mStart, mEnd);

        solutionNodes.Add(mStart);
        solutionNodes.Add(mEnd);

        openNodes.Add(mStart); // 1) Add the starting square (or node) to the open list.

        while (openNodes.Count > 0) // 2) Repeat the following:
        {
            openNodes.Sort((p1, p2) => p1.F.CompareTo(p2.F));

            PGNode current = openNodes[0]; // a) We refer to this as the current square.)

            if (current == mEnd)
            {
                while (current != null)
                {
                    solutionNodes.Add(current);
                    current = current.Parent;
                }

                return solutionNodes;
            }

            openNodes.Remove(current);
            closedNodes.Add(current); // b) Switch it to the closed list.

            List<PGNode> neighborNodes = current.GetNeighborNodes();
            double cost = 0;
            bool isCostBetter = false;

            for (int i = 0; i < neighborNodes.Count; i++)
            {
                PGNode neighbor = neighborNodes[i];
                cost = current.G + 10;
                isCostBetter = false;

                if (neighbor.Passable == false || closedNodes.Contains(neighbor))
                    continue; // If it is not walkable or if it is on the closed list, ignore it.

                if (openNodes.Contains(neighbor) == false)
                {
                    openNodes.Add(neighbor); // If it isn’t on the open list, add it to the open list.
                    isCostBetter = true;
                }
                else if (cost < neighbor.G)
                {
                    isCostBetter = true;
                }

                if (isCostBetter)
                {
                    neighbor.Parent = current; //  Make the current square the parent of this square. 
                    neighbor.G = cost;
                    neighbor.H = GetManhattanHeuristic(current, neighbor);
                }
            }
        }

        return null;
    }

Đây là heuristic tôi đang sử dụng:

    private static double GetManhattanHeuristic(PGNode mStart, PGNode mEnd)
    {
        return Math.Abs(mStart.X - mEnd.X) + Math.Abs(mStart.Y - mEnd.Y);
    }

Tôi đang làm gì sai? Đó là cả một ngày tôi cứ nhìn vào cùng một mã.


2
Nếu không có heuristic, nó sẽ (thường) mất nhiều thời gian hơn khi bạn đi qua nhiều nút hơn cho đến khi bạn tìm thấy kết thúc. Ngoài ra, hãy thử sử dụng một danh sách được sắp xếp vẫn được sắp xếp (tốt nhất là một bộ đã được sắp xếp, theo cách đó bạn không phải kiểm tra xem một mục có tồn tại trong danh sách mà bạn có thể chỉ cần thêm nó không
Elva

Câu trả lời:


10

Tôi thấy ba điều, một sai, hai nghi ngờ.

1) Bạn đang sắp xếp trên mỗi lần lặp. Đừng. Hoặc sử dụng hàng đợi ưu tiên hoặc ít nhất là thực hiện tìm kiếm tuyến tính để tìm mức tối thiểu. Bạn thực sự không cần toàn bộ danh sách để được sắp xếp mọi lúc!

2) openNodes.Contains () có thể chậm (không chắc chắn về các chi tiết cụ thể của Danh sách của C #, nhưng tôi cá là nó thực hiện tìm kiếm tuyến tính). Bạn có thể thêm một cờ vào mỗi nút và thực hiện điều này trong O (1).

3) GetNeighborNodes () có thể chậm.


2
2) Yeah, Chứa () sẽ khá chậm. Thay vì lưu trữ tất cả các nút của bạn trong danh sách, hãy sử dụng Từ điển <int, PGNode>. Sau đó, bạn nhận được thời gian tra cứu O (1) và vẫn có thể lặp lại danh sách một danh sách. Nếu các nút có trường id, hãy sử dụng khóa đó cho khóa, nếu không PGNode.GetHashCode () sẽ hoạt động.
khoan hồng

2
@Lenuity: Từ điển <PGNode, PGNode> sẽ không tốt hơn? Hai đối tượng có thể có cùng mã băm nhưng không bằng nhau. "Do đó, việc triển khai mặc định của phương thức này không được sử dụng làm định danh đối tượng duy nhất cho mục đích băm." msdn.microsoft.com/en-us/l Library / system.object.gethashcode.aspx - .NET 3.5 cung cấp Hashset, tốt hơn - msdn.microsoft.com/en-us/l Library / bb359438.aspx .

Điểm tốt, quên về Hashset.
khoan hồng

9

Ngoài điểm đã được đưa ra là bạn nên sử dụng một đống ưu tiên, bạn đã hiểu sai về heuristic. Bạn có

if (isCostBetter)
{
    ...
    hàng xóm.H = GetManhattanHeuristic (hiện tại, hàng xóm);
}
Nhưng heuristic được coi là một ước tính cho khoảng cách đến đích. Bạn nên đặt nó một lần, khi bạn thêm hàng xóm lần đầu tiên:
if (openNodes.Contains (hàng xóm) == false)
{
    hàng xóm.H = GetHeuristic (hàng xóm, mEnd);
    ...
}

Và như một điểm nhỏ nữa, bạn có thể đơn giản hóa A * bằng cách lọc ra các nút không thể vượt qua trong GetNeighbourNodes().


+1, tôi tập trung vào sự phức tạp của thuật toán và hoàn toàn bỏ lỡ việc sử dụng sai heuristic!
ggambett

4

Câu trả lời meta: bạn không bao giờ nên dành một ngày nhìn chằm chằm vào mã để tìm kiếm các vấn đề về hiệu suất. Năm phút với một trình hồ sơ sẽ cho bạn thấy chính xác nơi tắc nghẽn. Bạn có thể tải xuống một bản miễn phí của hầu hết các trình biên dịch và làm cho nó được nối với ứng dụng của bạn trong vài phút.


3

Không rõ ràng những gì bạn đang so sánh khi bạn so sánh F của các nút khác nhau. F có phải là một thuộc tính được định nghĩa là G + H không? Nó nên (Side-rant: Đây là một ví dụ về lý do tại sao nguyên tắc truy cập thống nhất là tào lao.)

Quan trọng hơn, bạn đang sắp xếp lại các nút mỗi khung. A * yêu cầu sử dụng hàng đợi ưu tiên , cho phép chèn hiệu quả - O (lg n) - được sắp xếp của một phần tử và một tập hợp, cho phép kiểm tra nhanh các nút đóng. Khi bạn đã viết thuật toán, bạn có chèn (sắp xếp O (n lg n) +, điều này làm tăng thời gian chạy lên tỷ lệ vô dụng.

(Bạn có thể nhận được chèn O (n) + sắp xếp nếu C # có thuật toán sắp xếp tốt. Nó vẫn còn quá nhiều. Sử dụng hàng đợi ưu tiên thực sự.)


2

http://theory.stanford.edu/~amitp/GameProgramming/Heuristic.html

  • Ở một thái cực, nếu h (n) bằng 0, thì chỉ g (n) đóng vai trò và A * biến thành thuật toán của Dijkstra, được đảm bảo để tìm ra con đường ngắn nhất.
  • Nếu h (n) luôn thấp hơn (hoặc bằng) chi phí di chuyển từ n đến mục tiêu, thì A * được đảm bảo để tìm ra con đường ngắn nhất. H (n) càng thấp, nút A * càng mở rộng, làm cho nó chậm hơn.
  • Nếu h (n) chính xác bằng chi phí di chuyển từ n đến mục tiêu, thì A * sẽ chỉ đi theo con đường tốt nhất và không bao giờ mở rộng bất cứ điều gì khác, làm cho nó rất nhanh. Mặc dù bạn không thể thực hiện điều này trong mọi trường hợp, nhưng bạn có thể làm cho nó chính xác trong một số trường hợp đặc biệt. Thật tuyệt khi biết rằng với thông tin hoàn hảo, A * sẽ hành xử hoàn hảo.
  • Nếu h (n) đôi khi lớn hơn chi phí di chuyển từ n đến mục tiêu, thì A * không được đảm bảo để tìm một con đường ngắn nhất, nhưng nó có thể chạy nhanh hơn.
  • Ở một thái cực khác, nếu h (n) rất cao so với g (n), thì chỉ h (n) đóng vai trò và A * biến thành Tìm kiếm đầu tiên tốt nhất.

Bạn đang sử dụng 'khoảng cách manhatten'. Điều này gần như luôn luôn là một heuristic xấu. Ngoài ra, từ việc xem thông tin đó từ trang được liên kết, bạn có thể đoán rằng heuristic của bạn thấp hơn chi phí thực.


-1, vấn đề không phải là heuristic, mà là việc thực hiện.

2

Ngoài các câu trả lời hàng đầu khác (chắc chắn có ý nghĩa hơn so với đề xuất này), một tối ưu hóa khác là thay đổi 'danh sách' đã đóng thành một loại bảng băm. Bạn không cần nó là một bộ sưu tập theo thứ tự, chỉ để có thể nhanh chóng thêm các giá trị và nhanh chóng xem chúng có tồn tại trong bộ sưu tập hay không.


1

Chi phí của bạn và Heuristic của bạn cần phải có một mối quan hệ. Nó sẽ là một đầu mối mà H được tính ở hai điểm khác nhau nhưng không bao giờ được truy cập.


Điều này giả định tài sản được triển khai không chính xác, điều này có thể xảy ra do định nghĩa của nó không được hiển thị, nhưng có hai vấn đề tức thời hơn với mã.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.