Wikipedia A * yol bulma algoritması çok zaman alıyor


9

C # 'da A * pathfinding başarıyla uyguladım ama çok yavaş ve neden anlamıyorum. Hatta openNodes listesini sıralamamayı denedim ama yine de aynı.

Harita 80x80 ve 10-11 düğüm var.

Sahte kodu buradan aldım Wikipedia

Ve bu benim uygulamam:

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

İşte kullandığım buluşsal yöntem:

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

Neyi yanlış yapıyorum? Bütün bir gün aynı koda bakmaya devam ediyorum.


2
Sezgisel olmadan, sonu bulana kadar daha fazla düğümden geçerken (genellikle) daha uzun sürmelidir. Ayrıca, sıralı kalan bir sıralı liste kullanmayı deneyin (tercihen sıralı bir küme, bu şekilde listede bir öğenin olup olmadığını kontrol etmek zorunda kalmazsınız)
Elva

Yanıtlar:


10

Biri yanlış, ikisi şüpheli olan üç şey görüyorum.

1) Her yinelemede sıralama yapıyorsunuz. Yapma. Bir öncelik kuyruğu kullanın veya en azından minimum değeri bulmak için doğrusal bir arama yapın. Aslında listenin her zaman sıralanması gerekmez!

2) openNodes.Contains () muhtemelen yavaştır (C # List'in özelliklerinden emin değilim, ancak doğrusal bir arama yapar). Her düğüme bir bayrak ekleyebilir ve bunu O (1) 'de yapabilirsiniz.

3) GetNeighborNodes () yavaş olabilir.


2
2) Evet, içerir () oldukça yavaş olacaktır. Tüm düğümlerinizi listelerde saklamak yerine, bir Sözlük <int, PGNode> kullanın. Sonra O (1) arama süresi alırsınız ve yine de bir liste listeleyebilir. Düğümlerin bir kimlik alanı varsa, anahtar için bunu kullanın, aksi takdirde PGNode.GetHashCode () çalışır.
Leniency

2
@Leniency: Sözlük <PGNode, PGNode> daha iyi olmaz mıydı? İki nesne aynı karma koduna sahip olabilir ancak eşit olmayabilir. "Sonuç olarak, bu yöntemin varsayılan uygulaması karma amaç için benzersiz bir nesne tanımlayıcısı olarak kullanılmamalıdır." msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx - .NET 3.5, daha iyi olan HashSet sağlar - msdn.microsoft.com/en-us/library/bb359438.aspx .

İyi nokta, HashSet'i unuttum.
Leniency

9

Öncelikli bir yığın kullanmanız gereken noktaya ek olarak, buluşsal yöntemi yanlış anladınız. Var

if (isCostBetter)
{
    ...
    neighbor.H = GetManhattanHeuristic (şimdiki, komşu);
}
Ancak sezgisel yöntemin hedefe olan uzaklık için bir tahmin olması gerekiyordu. Komşuyu ilk kez eklediğinizde bir kez ayarlamanız gerekir:
if (openNodes.Contains (komşu) == yanlış)
{
    neighbor.H = GetHeuristic (komşu, mEnd);
    ...
}

Ve daha küçük bir nokta olarak, içindeki geçilmez düğümleri filtreleyerek A * 'yı basitleştirebilirsiniz GetNeighbourNodes().


+1, algoritmik karmaşıklığa odaklandım ve sezginin yanlış kullanımını tamamen kaçırdım!
ggambett

4

Meta-cevap: Asla sadece bir günü performans sorunlarına bakmak için koda bakarak geçirmemelisiniz. Bir profiler ile beş dakika size darboğazların tam olarak nerede olduğunu gösterir. Çoğu profilerin ücretsiz bir izini indirebilir ve birkaç dakika içinde uygulamanıza bağlayabilirsiniz.


3

Farklı düğümlerin F'lerini karşılaştırdığınızda ne karşılaştırdığınız açık değildir. F bir özellik G + H olarak tanımlanmış mı? Olmalı. (Yan sıra: Bu, tek tip erişim ilkesinin neden boktan olduğuna bir örnektir.)

Daha da önemlisi, düğümleri her karede yeniden sıralıyorsunuz. A * , tek bir öğenin verimli bir şekilde O-l (l - n) sıralı eklenmesine izin veren bir öncelik kuyruğunun ve kapalı düğümler için hızlı kontrollere izin veren bir kümenin kullanılmasını gerektirir. Algoritmayı yazdıkça, çalışma zamanını gereksiz oranlara yükselten O (n lg n) ekleme + sıralamanız vardır.

(C # iyi bir sıralama algoritmasına sahipse O (n) ekleme + sıralama alabilirsiniz. Hala çok fazla. Gerçek öncelik sırası kullanın.)


2

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

  • Bir uçta, h (n) 0 ise, sadece g (n) bir rol oynar ve A *, en kısa yolu bulması garanti edilen Dijkstra'nın algoritmasına dönüşür.
  • H (n) her zaman n'den hedefe geçme maliyetinden daha düşükse (veya ona eşitse), A * 'nın en kısa yolu bulması garanti edilir. Daha düşük h (n), daha fazla A * düğümü genişleyerek yavaşlar.
  • H (n), n'den hedefe geçme maliyetine tam olarak eşitse, A * sadece en iyi yolu izleyecek ve asla başka bir şeyi genişletmeyecek ve çok hızlı hale getirecektir. Bunu her durumda yapamasanız da, bazı özel durumlarda tam olarak yapabilirsiniz. Kusursuz bilgi verildiğinde, A * 'nın mükemmel davranacağını bilmek güzel.
  • H (n) bazen n'den hedefe geçme maliyetinden daha büyükse, A * 'nın en kısa yolu bulması garanti edilmez, ancak daha hızlı çalışabilir.
  • Diğer uçta, h (n) g (n) 'ye göre çok yüksekse, sadece h (n) bir rol oynar ve A * En İyi İlk Arama'ya dönüşür.

'Manhatten mesafe' kullanıyorsunuz. Bu neredeyse her zaman kötü bir buluşsal yöntemdir. Ayrıca, bu bilgilere bağlı sayfadan bakıldığında, sezgisel yönteminizin gerçek maliyetten daha düşük olduğunu tahmin edebilirsiniz.


-1, sorun sezgisel değil uygulamadır.

2

Diğer üst yanıtlara (şüphesiz bu öneriye göre daha önemli olan) ek olarak, başka bir optimizasyon da kapalı 'liste'yi bir tür karma tabloya dönüştürmektir. Sıralı bir koleksiyon olmanıza gerek yok, sadece hızlı bir şekilde değerler ekleyebilmek ve koleksiyonda var olup olmadığını hızlı bir şekilde görmek için.


1

Maliyetiniz ve Sezgisel gereksiniminizin bir ilişkisi olması. H'nin iki farklı noktada hesaplandığı ancak asla erişilemediğine dair bir ipucu olmalıdır.


Bu, özelliğin yanlış uygulandığını varsayar; bu, tanımının gösterilmemesi nedeniyle mümkündür, ancak kodla ilgili iki sorun daha vardır.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.