Як порівнюються алгоритм і зірка Діжкстри?


154

Я дивився на те, що роблять хлопці в змаганні Mario AI , і деякі з них створили кілька акуратних ботів Маріо, використовуючи алгоритм A * (A-Star) Pathing Algorithm.

alt текст
( Відео Маріо A * Bot в дії )

Моє запитання: як A-Star порівнюється з Dijkstra? Оглядаючи їх, вони здаються схожими.

Навіщо хтось використовувати один над іншим? Особливо в контексті шляху в іграх?



@SLaks A * використовує більше пам'яті, ніж dijkstra? Як прийти, якщо вузли, що обіцяють лише шлях *, коли dijkstra їх випробовує?
Путратор

Відповіді:


177

Dijkstra - це особливий випадок для A * (коли евристика дорівнює нулю).


1
У dijkstra ми вважаємо лише відстань від джерела, правда? І мінімальну вершину враховують?
Кракен

4
Я вважав, що A * є особливим випадком для Dijkstra, де вони використовують евристику. Оскільки Дайкстра був там першим афаїком.
Madmenyo

46
@MennoGouw: Так, алгоритм Діккстри був розроблений першим; але це особливий випадок більш загального алгоритму A *. Це не зовсім незвично (насправді, мабуть, норма), щоб спеціальні випадки спочатку були виявлені, а потім згодом узагальнені.
Пітер Геркенс

1
Прекрасна відповідь для всіх, хто знає евристику;)
20

1
A * та використання евристики добре обговорюються в книзі AI Норвіга та Русселя
BoltzmannBrain

113

Dijkstra:

Він має одну функцію вартості, яка становить реальну вартість витрат від джерела до кожного вузла: f(x)=g(x).
Він знаходить найкоротший шлях від джерела до кожного іншого вузла, враховуючи лише реальні витрати.

* Пошук:

Він має дві функції витрат.

  1. g(x): те саме, що і Dijkstra. Реальна вартість досягнення вузла x.
  2. h(x): приблизна вартість від вузла xдо вузла цілі. Це евристична функція. Ця евристична функція ніколи не повинна завищувати вартість. Це означає, що реальна вартість для досягнення вузла цілі з вузла xповинна бути більшою або рівною h(x). Його називають допустимим евристичним.

Загальна вартість кожного вузла обчислюється за f(x)=g(x)+h(x)

Пошук * розширює вузол лише тоді, коли він здається перспективним. Він фокусується лише на тому, щоб досягти цільового вузла з поточного вузла, а не досягти всіх інших вузлів. Оптимально, якщо допустима евристична функція.

Тож якщо ваша евристична функція добре орієнтуватись на майбутні витрати, то вам знадобиться дослідити набагато менше вузлів, ніж Dijkstra.


20

Те, що було сказано на попередньому плакаті, плюс тому, що Dijkstra не має евристики, і на кожному кроці вибирає краї з найменшими витратами, воно, як правило, «охоплює» більше вашого графіка. Через те Dijkstra може бути кориснішим за A *. Хороший приклад - коли у вас є декілька кандидатних цільових вузлів, але ви не знаєте, який з них найближчий (у випадку A * вам доведеться запустити його кілька разів: один раз для кожного вузла-кандидата).


17
Якщо існує кілька потенційних вузлів цілі, можна просто змінити функцію тестування цілі, щоб включити їх усі. Таким чином, A * потрібно буде запустити лише один раз.
Бред Ларсен

9

Алгоритм Dijkstra ніколи не буде використовуватися для наведення маршруту. Використання A * не вимагає, якщо ви можете придумати гідну евристику (як правило, це легко для ігор, особливо в 2D світах). Залежно від місця пошуку, іноді кращим є ітеративне поглиблення A *, оскільки воно використовує менше пам'яті.


5
Чому б Dijkstra ніколи не використовувався для проходження маршруту? Чи можете ви докладно?
KingNestor

2
Тому що навіть якщо ви зможете придумати паршивий евристик, ви зробите краще, ніж Дійкстра. Іноді навіть якщо це неприпустимо. Це залежить від домену. Dijkstra також не працюватиме в ситуаціях з низькою пам'яттю, тоді як IDA * буде.
Shaggy Frog


7

Dijkstra - особливий випадок для A *.

Dijkstra знаходить мінімальні витрати від стартового вузла до всіх інших. A * знаходить мінімальну вартість від стартового вузла до вузла цілі.

Алгоритм Dijkstra ніколи не буде використовуватися для пошуку шляху. Використання A * можна придумати гідну евристику. Залежно від місця пошуку кращим є ітеративний A *, оскільки він використовує менше пам'яті.

Код алгоритму Дейкстри:

// 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 *:

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)

пропуск сусіда, який вже знаходиться в закритому наборі, дасть неоптимальний. Спробувавши це на цьому графіку (його приклад на відео YouTube, ігноруйте мову), дасть неправильну відповідь.
itsjwala

5

Dijkstra знаходить мінімальні витрати від стартового вузла до всіх інших. A * знаходить мінімальну вартість від стартового вузла до вузла цілі.

Тому, здавалося б, Dijkstra була б менш ефективною, коли все, що вам потрібно, - це мінімальна відстань від одного вузла до іншого.


2
Це не правда. Стандартний Дейкстра використовується для надання найкоротшого шляху між двома точками.
Еміль

3
Не вводьте в оману, що Dijkstra дає результат від s до всіх інших вершин. Таким чином, вона працює повільніше.
Іван Ворошилін

Я другий коментар @Emil Все, що вам потрібно зробити, це зупинитись, видаляючи вузол призначення з черги пріоритетів, і у вас найкоротший шлях від джерела до пункту призначення. Насправді це був оригінальний алгоритм.
seteropere

Точніше: якщо вказана ціль, Діккстра знайде найкоротший шлях до всіх вузлів, які лежать на шляхах, коротших, ніж шлях до вказаної цілі. Мета евристики в A * - обрізати деякі з цих шляхів. Ефективність евристики визначає, скільки обрізають.
Вейлон Флінн

@seteropere, але що робити, якщо ваш вузол призначення - це останній вузол, який шукають? Це, безумовно, менш ефективно, оскільки евристика A * та вибір пріоритетних вузлів - це те, що допомагає переконатися, що
шуканий

5

Ви можете вважати A * керованою версією Dijkstra. Значить, замість того, щоб вивчити всі вузли, ви будете використовувати евристику для вибору напрямку.

Якщо говорити конкретніше, якщо ви реалізуєте алгоритми з чергою пріоритетів, то пріоритетом вузла, який ви відвідуєте, буде функція вартості (попередні витрати вузлів + вартість, щоб потрапити сюди) і евристичної оцінки звідси до мети. Перебуваючи в Dijkstra, на пріоритет впливає лише фактична вартість вузлів. В будь-якому випадку критерій зупинки досягає мети.


2

Алгоритм Дейкстри точно знаходить найкоротший шлях. З іншого боку A * залежить від евристики. З цієї причини A * швидше алгоритму Діккстри і дасть хороші результати, якщо у вас є хороша евристика.


4
A * дає ті ж результати, що і Dijkstra, але швидше, коли ви використовуєте хорошу евристику. Алгоритм * встановлює деякі умови для правильної роботи, наприклад, розрахункова відстань між поточним вузлом та кінцевим вузлом має бути меншою, ніж реальна відстань.
Олександру

4
A * гарантовано надасть найкоротший шлях, коли евристика допустима (завжди недооцінюється)
Роберт

1

Якщо ви подивитесь на psuedocode для Astar:

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

Беручи до уваги, якщо ви подивитесь на те саме для Dijkstra :

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

Отже, справа в тому, що Astar не буде оцінювати вузол більше одного разу,
оскільки вважає, що перегляд вузла один раз достатній,
його евристиці.

ОТОГ, алгоритм Дейкстри не соромляться виправляти себе у випадку,
якщо вузол знову з’явиться.

Що повинно зробити Astar швидшим і придатнішим для пошуку шляху.


7
Це неправда: A * може переглядати вузли не один раз. Насправді Дайкстра - це особливий випадок A * ...
Еміль

2
Перевірте це для уточнення: stackoverflow.com/questions/21441662/…
spiralmoon

Усі алгоритми пошуку мають "межу" та "відвідуваний набір". Жоден алгоритм не виправляє шлях до вузла, коли він знаходиться у відвіданому наборі: за дизайном вони переміщують вузли від кордону до відвіданого набору в порядку пріоритетності. Мінімальні відомі відстані до вузлів можна оновлювати лише тоді, коли вони знаходяться на кордоні. Dijkstra's - це форма найкращого пошуку, і вузол ніколи не буде переглянутий після його розміщення в "відвіданому" наборі. A * ділиться цією властивістю, і він використовує допоміжний оцінювач, щоб вибрати, яким вузлам на кордоні слід розставити пріоритети. en.wikipedia.org/wiki/Dijkstra%27s_algorithm
pygosceles

0

У A * для кожного вузла ви перевіряєте їх вихідні з'єднання.
Для кожного нового вузла ви обчислюєте найнижчу вартість до цих пір (csf) залежно від ваги підключень до цього вузла та витрат, які вам довелося досягти попередньому вузлу.
Крім того, ви оцінюєте вартість від нового вузла до цільового вузла і додаєте його до csf. Тепер у вас є орієнтовна загальна вартість (тощо). (тощо = csf + орієнтовна відстань до цілі) Далі ви вибираєте з нових вузлів той, який має найменший рівень тощо.
Робіть так само, як і раніше, поки один із нових вузлів не стане ціллю.

Діжкстра працює майже так само. За винятком того, що орієнтовна відстань до цілі завжди дорівнює 0, і алгоритм спочатку зупиняється, коли ціль є не лише одним із нових вузлів , а й тим, що має найнижчий csf.

A * зазвичай швидше, ніж dijstra, хоча це не завжди так. У відеоіграх ви часто використовуєте підхід "досить близький для гри". Тому зазвичай достатньо «близького» шляху від A * достатньо.


-1

Алгоритм Dijkstra, безумовно, повний і оптимальний, що ви завжди знайдете найкоротший шлях. Однак це, як правило, займає більше часу, оскільки використовується в основному для виявлення декількох вузлів цілі.

A* searchз іншого боку, має значення з евристичними значеннями, які ви можете визначити, щоб досягти своєї мети ближче, як-от манхеттенська відстань до мети. Він може бути оптимальним або повним, що залежить від евристичних факторів. це, безумовно, швидше, якщо у вас є єдиний вузол цілі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.