วิกิพีเดียอัลกอริธึมการนำทาง * ใช้เวลานาน


9

ฉันติดตั้ง A * pathfinding ใน C # สำเร็จแล้ว แต่ช้ามากและฉันไม่เข้าใจว่าทำไม ฉันพยายามไม่เรียงลำดับรายการ openNodes แต่ก็ยังเหมือนเดิม

แผนที่ 80x80 และมี 10-11 โหนด

ฉันเอา pseudocode จากที่นี่Wikipedia

และนี่คือการดำเนินการของฉัน:

 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;
    }

นี่คือฮิวริสติกที่ฉันใช้:

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

ผมทำอะไรผิดหรือเปล่า? มันเป็นทั้งวันที่ฉันดูรหัสเดิมต่อไป


2
หากไม่มีการเรียนรู้แบบฮิวริสติกก็จะใช้เวลานานกว่านั้นเมื่อคุณผ่านหลาย ๆ โหนดจนกว่าคุณจะพบจุดจบ นอกจากนี้ลองใช้รายการที่เรียงลำดับซึ่งยังคงเรียง (โดยเฉพาะชุดที่เรียงลำดับว่าทางคุณไม่ต้องตรวจสอบว่ารายการที่มีอยู่ในรายการที่คุณก็สามารถเพิ่มได้)
Elva

คำตอบ:


10

ฉันเห็นสามสิ่งหนึ่งผิดสองอย่างที่น่าสงสัย

1) คุณกำลังเรียงลำดับซ้ำทุกครั้ง อย่า ใช้คิวลำดับความสำคัญหรืออย่างน้อยที่สุดค้นหาเชิงเส้นอย่างน้อยที่สุดเพื่อค้นหาค่าต่ำสุด คุณไม่จำเป็นต้องเรียงลำดับรายการทั้งหมดตลอดเวลา!

2) openNodes.Contains () อาจช้า (ไม่แน่ใจเกี่ยวกับรายละเอียดเฉพาะของรายการ C # แต่ฉันคิดว่ามันเป็นการค้นหาเชิงเส้น) คุณสามารถเพิ่มการตั้งค่าสถานะให้กับแต่ละโหนดและทำสิ่งนี้ใน O (1)

3) GetNeighborNodes () อาจช้า


2
2) ใช่ประกอบด้วย () จะค่อนข้างช้า แทนที่จะเก็บโหนดทั้งหมดของคุณไว้ในรายการให้ใช้ Dictionary <int, PGNode> จากนั้นคุณจะได้ O (1) เวลาค้นหาและยังสามารถย้ำรายการ หากโหนดมีฟิลด์ id ให้ใช้สำหรับคีย์มิฉะนั้น PGNode.GetHashCode () จะทำงาน
ผ่อนปรน

2
@ การใช้ประโยชน์: จะไม่ใช้พจนานุกรม <PGNode, PGNode> หรือเปล่า วัตถุสองชิ้นอาจมีรหัสแฮชเดียวกัน แต่ไม่เท่ากัน "ดังนั้นการใช้งานเริ่มต้นของวิธีนี้จะต้องไม่ถูกใช้เป็นตัวระบุวัตถุที่ไม่ซ้ำเพื่อการแฮช" msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx - NET 3.5 ให้ HashSet ซึ่งจะดีกว่า - msdn.microsoft.com/en-us/library/bb359438.aspx

จุดดีลืมเกี่ยวกับ HashSet
ผ่อนปรน

9

นอกจากประเด็นที่ทำให้คุณควรใช้ลำดับความสำคัญฮีปคุณเข้าใจผิดฮิวริสติก คุณมี

ถ้า (isCostBetter)
{
    ...
    เพื่อนบ้าน H = GetManhattanHeuristic (ปัจจุบันเพื่อนบ้าน);
}
แต่ฮิวริสติกควรจะประมาณระยะทางไปยังปลายทาง คุณควรตั้งค่าหนึ่งครั้งเมื่อคุณเพิ่มเพื่อนบ้าน:
if (openNodes.Contains (เพื่อนบ้าน) == false
{
    เพื่อนบ้าน H = GetHeuristic (เพื่อนบ้าน mEnd);
    ...
}

และเป็นจุดเล็ก ๆ น้อย ๆ ต่อไปคุณสามารถลดความซับซ้อนของ A * GetNeighbourNodes()โดยการกรองโหนดที่ไม่สามารถใช้ได้ใน


+1 ฉันมุ่งเน้นไปที่ความซับซ้อนของอัลกอริทึมและพลาดการใช้ฮิวริสติกอย่างสมบูรณ์!
ggambett

4

meta-answer: คุณไม่ควรใช้เวลาทั้งวันในการดูโค้ดเพื่อค้นหาปัญหาด้านประสิทธิภาพ ห้านาทีโดยผู้สร้างโปรไฟล์จะแสดงให้คุณเห็นว่าคอขวดอยู่ตรงไหน คุณสามารถดาวน์โหลดตัวรวบรวมข้อมูลส่วนใหญ่ฟรีและนำไปติดตั้งในแอพของคุณในไม่กี่นาที


3

ยังไม่ชัดเจนว่าคุณเปรียบเทียบอะไรเมื่อเปรียบเทียบ F ของโหนดต่าง ๆ F กำหนดคุณสมบัติเป็น G + H หรือไม่ มันควรจะเป็น. (พูดจาโผงผาง: นี่เป็นตัวอย่างว่าทำไมหลักการเข้าถึงแบบเดียวกันคืออึ)

ที่สำคัญกว่านั้นคือคุณกำลังเรียงลำดับโหนดใหม่ทุกเฟรม A * เรียกใช้คิวลำดับความสำคัญซึ่งอนุญาตให้มีประสิทธิภาพ - O (lg n) - การแทรกการเรียงลำดับขององค์ประกอบเดียวและชุดซึ่งช่วยให้ตรวจสอบอย่างรวดเร็วสำหรับโหนดที่ปิด เมื่อคุณเขียนอัลกอริทึมแล้วคุณจะมีการเรียงลำดับการแทรก O (n lg n) ซึ่งทำให้เวลาทำงานเป็นสัดส่วนที่ไร้ประโยชน์

(คุณอาจได้รับการแทรก O + n + เรียงถ้า C # มีอัลกอริทึมการเรียงลำดับที่ดียังคงมากเกินไปใช้คิวลำดับความสำคัญจริง)


2

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

  • ที่สุดขั้วหนึ่งหาก h (n) เป็น 0 ดังนั้น g (n) เท่านั้นจึงมีบทบาทและ A * จะเปลี่ยนเป็นอัลกอริทึมของ Dijkstra ซึ่งรับประกันว่าจะหาเส้นทางที่สั้นที่สุด
  • หาก h (n) ต่ำกว่า (หรือเท่ากับ) ค่าใช้จ่ายในการย้ายจาก n ไปยังเป้าหมายเสมอ A * จะรับประกันว่าจะหาเส้นทางที่สั้นที่สุด h (n) ที่ต่ำกว่าคือยิ่งโหนด A * ขยายออกทำให้ช้าลง
  • หาก h (n) เท่ากับค่าใช้จ่ายในการย้ายจาก n ไปยังเป้าหมายแน่นอน A * จะเป็นไปตามเส้นทางที่ดีที่สุดเท่านั้นและจะไม่ขยายสิ่งอื่นใดอีกทำให้รวดเร็วมาก แม้ว่าคุณจะไม่สามารถทำให้สิ่งนี้เกิดขึ้นได้ในทุกกรณีคุณสามารถทำให้เป็นจริงได้ในบางกรณี เป็นเรื่องดีที่รู้ว่าได้รับข้อมูลที่สมบูรณ์แบบ A * จะทำงานได้อย่างสมบูรณ์แบบ
  • หากบางครั้ง h (n) มากกว่าค่าใช้จ่ายในการเคลื่อนย้ายจาก n ไปยังเป้าหมายระบบจะไม่รับประกันว่า A * จะหาเส้นทางที่สั้นที่สุด แต่สามารถวิ่งได้เร็วขึ้น
  • อีกด้านหนึ่งหาก h (n) มีค่าสูงมากเมื่อเทียบกับ g (n) ดังนั้น h (n) จึงมีบทบาทเท่านั้นและ A * เปลี่ยนเป็นการค้นหาที่ดีที่สุดครั้งแรก

คุณกำลังใช้ 'ระยะทาง manhatten' นี่เป็นวิธีแก้ปัญหาที่ไม่ดีอยู่เสมอ นอกจากนี้จากการดูข้อมูลนั้นจากหน้าที่เชื่อมโยงคุณสามารถเดาได้ว่าฮิวริสติกของคุณต่ำกว่าราคาจริง


-1 ปัญหาไม่ใช่การแก้ปัญหา แต่เป็นการดำเนินการ

2

นอกจากคำตอบยอดนิยมอื่น ๆ (ซึ่งมีความสำคัญมากกว่าคำแนะนำนี้อย่างไม่ต้องสงสัย) การเพิ่มประสิทธิภาพอื่นคือการเปลี่ยน 'รายการ' ปิดเป็นตารางแฮชบางประเภท คุณไม่จำเป็นต้องเป็นคอลเล็กชันที่ได้รับคำสั่งเพียงเพื่อให้สามารถเพิ่มค่าได้อย่างรวดเร็วและเพื่อดูว่ามีอยู่ในคอลเลกชันหรือไม่


1

ค่าใช้จ่ายและการเรียนรู้ของคุณจำเป็นต้องมีความสัมพันธ์ ควรเป็นเบาะแสที่ H ถูกคำนวณในสองจุดที่แตกต่างกัน แต่ไม่เคยเข้าถึง


สิ่งนี้ถือว่าคุณสมบัติถูกนำไปใช้อย่างไม่ถูกต้องซึ่งเป็นไปได้เนื่องจากคำจำกัดความไม่ปรากฏ แต่มีปัญหาอีกสองประการเกี่ยวกับโค้ดในทันที
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.