Dijkstra Algoritması ve A-Star nasıl karşılaştırılır?


154

Mario AI Yarışması'ndaki adamların neler yaptığını inceliyordum ve bazıları A * (A-Star) Pathing Algorithm'u kullanarak oldukça düzgün Mario botları inşa etti.

alternatif metin
( Mario A * Bot Eylem Videosu )

Sorum şu: A-Star Dijkstra ile nasıl kıyaslanıyor? Onlara baktığında benzer görünüyorlar.

Biri neden birini diğerinin üzerinde kullansın ki? Özellikle oyunlarda yol bulma bağlamında?



@SLaks A * dijkstra'dan daha fazla bellek kullanıyor mu? Dijkstra hepsini denerken sadece * umut vaat eden düğümler nasıl olur?
Poutrathor

Yanıtlar:


177

Dijkstra A * için özel bir durumdur (sezgisel tarama sıfır olduğunda).


1
Dijkstra'da, sadece kaynaktan mesafeyi düşünüyoruz değil mi? Ve minimum tepe noktası dikkate alınır?
Kraken

4
Ben A * 'nın sezgisel kullandıkları Dijkstra için özel bir durum olduğunu düşündüm. Dijkstra ilk afaik olduğundan beri.
Madmenyo

46
@MennoGouw: Evet ilk önce Dijkstra'nın algoritması geliştirildi; ancak daha genel A * algoritmasının özel bir halidir. İlk önce özel vakaların keşfedilmesi ve daha sonra genelleştirilmesi olağandışı (aslında muhtemelen norm) değildir.
Pieter Geerkens

1
Sezgiselliği bilen herkes için harika bir cevap;)
lindhe

1
A * ve buluşsal yöntemlerin kullanımı Norvig ve Russel'in AI kitabında
BoltzmannBrain

113

Dijkstra:

Her bir düğüm bir kaynaktan gerçek maliyet değeri olan bir maliyet fonksiyonu, var f(x)=g(x).
Sadece gerçek maliyeti göz önünde bulundurarak kaynaktan diğer düğümlere giden en kısa yolu bulur.

Arama:

İki maliyet işlevi vardır.

  1. g(x): Dijkstra ile aynı. Bir düğüme ulaşmak için gerçek maliyet x.
  2. h(x): düğümden xhedef düğüme yaklaşık maliyet . Buluşsal bir işlevdir. Bu buluşsal işlev hiçbir zaman maliyeti abartmamalıdır. Bu, düğümden hedef düğüme ulaşmak için gerçek maliyetin xdaha büyük veya eşit olması gerektiği anlamına gelir h(x). Buna kabul edilebilir sezgisel denir.

Her düğümün toplam maliyeti şu şekilde hesaplanır: f(x)=g(x)+h(x)

A * araması yalnızca ümit verici görünüyorsa düğümü genişletir. Yalnızca diğer düğümlere ulaşmak için değil, geçerli düğümden hedef düğüme ulaşmak için odaklanır. Sezgisel işlev kabul edilebilirse en uygunudur.

Sezgisel işleviniz gelecekteki maliyeti yaklaşık olarak tahmin etmekte fayda varsa, Dijkstra'dan çok daha az sayıda düğüm keşfetmeniz gerekecek.


20

Önceki posterin söyledikleri, artı Dijkstra'nın sezgisel olması ve her adımda en düşük maliyetle kenarları seçmesi, grafiğinizin daha fazlasını "örtme" eğilimindedir. Bu nedenle Dijkstra A * 'dan daha yararlı olabilir. Birkaç örnek hedef düğümünüz olduğunda iyi bir örnek vardır, ancak hangisinin en yakın olduğunu bilmezsiniz (A * durumunda birden çok kez çalıştırmanız gerekir: her aday düğümü için bir kez).


17
Birkaç potansiyel hedef düğümü varsa, hedef test işlevini hepsini içerecek şekilde değiştirebilirsiniz. Bu şekilde, A * 'nın yalnızca bir kez çalıştırılması gerekir.
Brad Larsen

9

Dijkstra'nın algoritması asla yol bulma için kullanılmaz. Eğer iyi bir sezgisel (eğer özellikle 2D dünyalarda oyunlar için genellikle kolay) bulursanız, A * kullanmak zekice olmaz. Arama alanına bağlı olarak, daha az bellek kullandığı için Yinelemeli Derinleşme A * bazen tercih edilir.


5
Neden Dijkstra asla yol bulma için kullanılmaz? Detaylandırabilir misin?
KingNestor

2
Çünkü berbat bir sezgisel bulsanız bile Dijkstra'dan daha iyisini yaparsınız. Bazen kabul edilemez olsa bile. Alan adına bağlıdır. Dijkstra ayrıca düşük bellek durumlarında çalışmazken, IDA * çalışacaktır.
Shaggy Frog


7

Dijkstra A * için özel bir durumdur.

Dijkstra, başlangıç ​​düğümünden diğerlerine kadar minimum maliyetleri bulur. A * başlangıç ​​düğümünden hedef düğüme kadar minimum maliyeti bulur.

Dijkstra'nın algoritması asla yol bulmak için kullanılmaz. A * kullanmak iyi bir sezgisel gelebilir. Arama alanına bağlı olarak, daha az bellek kullandığı için yinelemeli A * tercih edilir.

Dijkstra algoritmasının kodu:

// A C / C++ program for Dijkstra's single source shortest path algorithm.
// The program is for adjacency matrix representation of the graph

#include <stdio.h>
#include <limits.h>

// Number of vertices in the graph
#define V 9

// A utility function to find the vertex with minimum distance value, from
// the set of vertices not yet included in shortest path tree
int minDistance(int dist[], bool sptSet[])
{
 // Initialize min value
 int min = INT_MAX, min_index;

  for (int v = 0; v < V; v++)
   if (sptSet[v] == false && dist[v] <= min)
     min = dist[v], min_index = v;

   return min_index;
}

 int printSolution(int dist[], int n)
 {
  printf("Vertex   Distance from Source\n");
  for (int i = 0; i < V; i++)
     printf("%d \t\t %d\n", i, dist[i]);
  }

void dijkstra(int graph[V][V], int src)
{
 int dist[V];     // The output array.  dist[i] will hold the shortest
                  // distance from src to i

 bool sptSet[V]; // sptSet[i] will true if vertex i is included in shortest
                 // path tree or shortest distance from src to i is finalized

 // Initialize all distances as INFINITE and stpSet[] as false
 for (int i = 0; i < V; i++)
    dist[i] = INT_MAX, sptSet[i] = false;

 // Distance of source vertex from itself is always 0
 dist[src] = 0;

 // Find shortest path for all vertices
 for (int count = 0; count < V-1; count++)
 {
   // Pick the minimum distance vertex from the set of vertices not
   // yet processed. u is always equal to src in first iteration.
   int u = minDistance(dist, sptSet);

   // Mark the picked vertex as processed
   sptSet[u] = true;

   // Update dist value of the adjacent vertices of the picked vertex.
   for (int v = 0; v < V; v++)

     // Update dist[v] only if is not in sptSet, there is an edge from 
     // u to v, and total weight of path from src to  v through u is 
     // smaller than current value of dist[v]
     if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX 
                                   && dist[u]+graph[u][v] < dist[v])
        dist[v] = dist[u] + graph[u][v];
 }

 // print the constructed distance array
 printSolution(dist, V);
 }

// driver program to test above function
int main()
 {
 /* Let us create the example graph discussed above */
 int graph[V][V] = {{0, 4, 0, 0, 0, 0, 0, 8, 0},
                  {4, 0, 8, 0, 0, 0, 0, 11, 0},
                  {0, 8, 0, 7, 0, 4, 0, 0, 2},
                  {0, 0, 7, 0, 9, 14, 0, 0, 0},
                  {0, 0, 0, 9, 0, 10, 0, 0, 0},
                  {0, 0, 4, 14, 10, 0, 2, 0, 0},
                  {0, 0, 0, 0, 0, 2, 0, 1, 6},
                  {8, 11, 0, 0, 0, 0, 1, 0, 7},
                  {0, 0, 2, 0, 0, 0, 6, 7, 0}
                 };

dijkstra(graph, 0);

return 0;
}

A * algoritmasının kodu:

class Node:
def __init__(self,value,point):
    self.value = value
    self.point = point
    self.parent = None
    self.H = 0
    self.G = 0
def move_cost(self,other):
    return 0 if self.value == '.' else 1

def children(point,grid):
x,y = point.point
links = [grid[d[0]][d[1]] for d in [(x-1, y),(x,y - 1),(x,y + 1),(x+1,y)]]
return [link for link in links if link.value != '%']
def manhattan(point,point2):
return abs(point.point[0] - point2.point[0]) + abs(point.point[1]-point2.point[0])
def aStar(start, goal, grid):
#The open and closed sets
openset = set()
closedset = set()
#Current point is the starting point
current = start
#Add the starting point to the open set
openset.add(current)
#While the open set is not empty
while openset:
    #Find the item in the open set with the lowest G + H score
    current = min(openset, key=lambda o:o.G + o.H)
    #If it is the item we want, retrace the path and return it
    if current == goal:
        path = []
        while current.parent:
            path.append(current)
            current = current.parent
        path.append(current)
        return path[::-1]
    #Remove the item from the open set
    openset.remove(current)
    #Add it to the closed set
    closedset.add(current)
    #Loop through the node's children/siblings
    for node in children(current,grid):
        #If it is already in the closed set, skip it
        if node in closedset:
            continue
        #Otherwise if it is already in the open set
        if node in openset:
            #Check if we beat the G score 
            new_g = current.G + current.move_cost(node)
            if node.G > new_g:
                #If so, update the node to have a new parent
                node.G = new_g
                node.parent = current
        else:
            #If it isn't in the open set, calculate the G and H score for the node
            node.G = current.G + current.move_cost(node)
            node.H = manhattan(node, goal)
            #Set the parent to our current item
            node.parent = current
            #Add it to the set
            openset.add(node)
    #Throw an exception if there is no path
    raise ValueError('No Path Found')
def next_move(pacman,food,grid):
#Convert all the points to instances of Node
for x in xrange(len(grid)):
    for y in xrange(len(grid[x])):
        grid[x][y] = Node(grid[x][y],(x,y))
#Get the path
path = aStar(grid[pacman[0]][pacman[1]],grid[food[0]][food[1]],grid)
#Output the path
print len(path) - 1
for node in path:
    x, y = node.point
    print x, y
pacman_x, pacman_y = [ int(i) for i in raw_input().strip().split() ]
food_x, food_y = [ int(i) for i in raw_input().strip().split() ]
x,y = [ int(i) for i in raw_input().strip().split() ]

grid = []
for i in xrange(0, x):
grid.append(list(raw_input().strip()))

next_move((pacman_x, pacman_y),(food_x, food_y), grid)

zaten kapalı sette olan komşu atlamak yetersizdir. Bu grafikte denemek (Bir youtube video örneği, dili yoksay) yanlış cevap verecektir.
itsjwala

5

Dijkstra, başlangıç ​​düğümünden diğerlerine kadar minimum maliyetleri bulur. A * başlangıç ​​düğümünden hedef düğüme kadar minimum maliyeti bulur.

Bu nedenle, ihtiyacınız olan tek şey bir düğümden diğerine minimum mesafe olduğunda Dijkstra'nın daha az verimli olacağı görülüyor.


2
Bu doğru değil. Standart Dijkstra, iki nokta arasındaki en kısa yolu vermek için kullanılır.
Emil

3
Lütfen yanıltmayın, Dijkstra's s'den diğer tüm köşelere sonuç verir. Böylece daha yavaş çalışır.
Ivan Voroshilin

@Emil yorumu ikinci olarak. Tek yapmanız gereken, hedef düğümü öncelik kuyruğundan kaldırırken durmaktır ve kaynaktan hedefe giden en kısa yola sahip olursunuz. Bu aslında orijinal algoritmaydı.
seteropere

Daha kesin olarak: bir hedef belirtilirse, Dijkstra's belirtilen hedefe giden yoldan daha kısa yollarda bulunan tüm düğümlere giden en kısa yolu bulur. A * 'daki sezginin amacı bu yollardan bazılarını budamaktır. Buluşsal yöntemlerin etkinliği, kaç kişinin budanacağını belirler.
Waylon Flinn

@seteropere, ancak hedef düğümünüz aranan son düğümse ne olur? Kesinlikle daha az verimlidir, çünkü A * 's sezgisel tarama ve öncelik düğümleri seçilmesi, aranan hedef düğümün listedeki son düğüm olmadığından emin
olmanıza yardımcı olan şeydir

5

A * 'yı Dijkstra'nın rehberli versiyonu olarak düşünebilirsiniz. Yani, tüm düğümleri keşfetmek yerine, bir yön seçmek için bir buluşsal yöntem kullanacaksınız.

Daha somut bir ifadeyle, algoritmaları öncelik kuyruğuyla uyguluyorsanız, ziyaret ettiğiniz düğümün önceliği maliyetin (önceki düğümlerin maliyeti + buraya gelme maliyeti) ve sezgisel tahminin bir işlevi olacaktır. hedefe. Dijkstra'da iken, öncelik yalnızca düğümlerin gerçek maliyetinden etkilenir. Her iki durumda da, durdurma kriteri hedefe ulaşıyor.


2

Dijkstra'nın algoritması kesinlikle en kısa yolu bulur. Öte yandan A * buluşsal yöntemlere bağlıdır. Bu nedenle A *, Dijkstra'nın algoritmasından daha hızlıdır ve iyi bir buluşsal yönteminiz varsa iyi sonuçlar verecektir.


4
A *, Dijkstra ile aynı sonuçları verir, ancak iyi bir buluşsal yöntem kullandığınızda daha hızlıdır. Bir * algoritması, doğru çalışmak için mevcut düğüm ile son düğüm arasındaki tahmini mesafenin gerçek mesafeden daha düşük olması gibi bazı koşullar getirir.
Alexandru

4
A * buluşsal yöntem kabul edilebilir olduğunda en kısa yolu verecektir (her zaman hafife alır)
Robert

1

Astar için psuedocode'a bakarsanız :

foreach y in neighbor_nodes(x)
             if y in closedset
                 continue

Oysa Dijkstra için aynı şekilde bakarsanız :

for each neighbor v of u:         
             alt := dist[u] + dist_between(u, v) ;

Mesele şu ki, Astar bir düğümü bir kereden fazla değerlendirmeyecek,
çünkü bir düğüme bir kez bakmanın yeterli olduğuna inanıyor , çünkü
sezgisel tarama .

OTOH, Dijkstra'nın algoritması,
bir düğümün tekrar ortaya çıkması durumunda kendini düzeltmekten çekinmiyor .

Hangi gerektiğini daha hızlı ve daha uygun yol bulma için Astar olun.


7
Bu doğru değil: A * düğümlere bir kereden fazla bakabilir. Aslında, Dijkstra A * ... özel bir vaka
Emil

2
Açıklamak için bunu kontrol edin: stackoverflow.com/questions/21441662/…
spiralmoon

Tüm arama algoritmalarının bir "sınır" ve bir "ziyaret edilen küme" vardır. Her iki algoritma da ziyaret edilen kümede bir düğümün yolunu düzeltir: tasarım gereği, düğümleri sınırdan ziyaret edilen kümeye öncelik sırasına göre taşırlar. Düğümlere en az bilinen mesafeler, yalnızca sınırdayken güncellenebilir. Dijkstra en iyi ilk arama biçimidir ve bir düğüm "ziyaret edilen" setine yerleştirildikten sonra asla tekrar gözden geçirilmez. A * bu özelliği paylaşır ve sınırdaki hangi düğümlere öncelik verileceğini seçmek için yardımcı bir tahminci kullanır. en.wikipedia.org/wiki/Dijkstra%27s_algorithm
pygosceles

0

A * 'da her düğüm için giden bağlantıları kontrol edin.
Her yeni düğüm için, bu düğüme bağlantıların ağırlıklarına ve önceki düğüme ulaşmak zorunda olduğunuz maliyetlere bağlı olarak şu ana kadarki en düşük maliyeti (csf) hesaplarsınız.
Ek olarak, yeni düğümden hedef düğüme olan maliyeti tahmin edersiniz ve bunu csf'ye eklersiniz. Artık tahmini toplam maliyetiniz var (vb.). (vs = csf + tahmini hedefe uzaklık) Sonraki vb düşük olan bir yeni düğümler arasından seçim
biri kadar önce aynı mı yeni düğümler hedef olacaktır.

Dijkstra neredeyse aynı şekilde çalışır. Hedefe tahmini mesafe her zaman 0'dır ve algoritma önce hedef yalnızca yeni düğümlerden biri değil , aynı zamanda en düşük csf değerine sahip olduğunda durur .

A * genellikle dijstra'dan daha hızlıdır, ancak bu her zaman böyle olmayacaktır. Video oyunlarında genellikle "bir oyun için yeterince yakın" yaklaşımını tercih edersiniz. Bu nedenle A * 'dan “yeterince yakın” optimal yol genellikle yeterlidir.


-1

Dijkstra'nın algoritması her zaman en kısa yolu bulacağınız kesinlikle eksiksiz ve optimaldir. Bununla birlikte, esas olarak birden çok hedef düğümünü tespit etmek için kullanıldığından daha uzun sürer.

A* searchÖte yandan, hedefe daha yakın ulaşmak için tanımlayabileceğiniz sezgisel değerlerle önemlidir. Sezgisel faktörlere bağlı olarak optimal veya tam olabilir. tek bir hedef düğümünüz varsa kesinlikle daha hızlıdı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.