Ответ 1
Dijkstra - частный случай для A * (когда эвристики равны нулю).
Я смотрел на то, что делали парни в AI Mario Competition, и некоторые из них создали довольно симпатичных ботов Mario, используя алгоритм A * (A-Star) Pathing.
(Видео Марио A * Bot In Action)
Мой вопрос, как A-Star сравнивается с Dijkstra? Глядя на них, они кажутся похожими.
Зачем кому-то использовать один над другим? Особенно в контексте путей в играх?
Dijkstra - частный случай для A * (когда эвристики равны нулю).
Он имеет одну функцию затрат, которая представляет собой реальное себестоимость от источника к каждому node: f(x)=g(x)
.
Он находит кратчайший путь от источника ко всем другим node, рассматривая только реальную стоимость.
Он имеет две функции стоимости.
g(x)
: то же, что и Дейкстра. Реальная стоимость для достижения node x
.h(x)
: приблизительная стоимость от node x
до цели node. Это эвристическая функция. Эта эвристическая функция никогда не должна переоценивать стоимость. Это означает, что реальная стоимость достижения цели node от node x
должна быть больше или равна h(x)
. Это называется допустимой эвристикой.Общая стоимость каждого node вычисляется на f(x)=g(x)+h(x)
A * поиск расширяет только node, если он кажется многообещающим. Он фокусируется только на достижении цели node от текущего node, а не на всех остальных узлах. Это оптимально, если эвристическая функция допустима.
Итак, если ваша эвристическая функция хороша для приближения будущих затрат, вам придется исследовать намного меньше узлов, чем Дийкстра.
Какой предыдущий плакат сказал, плюс потому, что у Дийкстры нет эвристики, и на каждом шаге выбирает края с наименьшими затратами, он имеет тенденцию "покрывать" больше вашего графика. Из-за этого Dijkstra может быть более полезным, чем A *. Хорошим примером является то, что у вас есть несколько целевых целевых узлов, но вы не знаете, какой из них наиболее близок (в случае A * вам придется запускать его несколько раз: один раз для каждого кандидата node).
Алгоритм Дейкстры никогда не будет использоваться для поиска пути. Использование A * без проблем, если вы можете придумать приличную эвристику (обычно это легко для игр, особенно в 2D-мирах). В зависимости от пространства поиска, Iterative Deepening A * иногда предпочтительнее, поскольку использует меньше памяти.
Дейкстра - частный случай для A *.
Dijkstra находит минимальные затраты от стартового node ко всем остальным. A * находит минимальную стоимость с начала node до цели node.
Алгоритм Дейкстры никогда не будет использоваться для поиска пути. Используя A *, можно придумать приличную эвристику. В зависимости от пространства поиска предпочтительным является итеративный A *, поскольку он использует меньше памяти.
Код для алгоритма Дейкстры:
// A C / C++ program for Dijkstra 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 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)
Dijkstra находит минимальные затраты от стартового node ко всем остальным. A * находит минимальную стоимость с начала node до цели node.
Поэтому казалось бы, что Дейкстра будет менее эффективна, когда все, что вам нужно, - это минимальное расстояние от одного node до другого.
Вы можете рассмотреть A * управляемую версию Dijkstra. Значение, вместо того, чтобы исследовать все узлы, вы будете использовать эвристику, чтобы выбрать направление.
Чтобы сделать это более конкретно, если вы реализуете алгоритмы с приоритетной очередью, приоритет node, который вы посещаете, будет функцией стоимости (предыдущие узлы стоят + стоимость, чтобы добраться сюда) и эвристическая оценка отсюда к цели. В то время как в Дейкстре приоритет зависит только от фактической стоимости узлов. В любом случае критерий остановки достигает цели.
Алгоритм Дейкстры находит кратчайший путь определенно. С другой стороны, А * зависит от эвристики. По этой причине A * быстрее алгоритма Дейкстры и даст хорошие результаты, если у вас хорошая эвристика.
Если вы посмотрите на 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 не будет оценивать node более одного раза,
поскольку он считает, что один раз увидеть node один раз, так как
к его эвристике.
OTOH, алгоритм Дейкстры не стесняется само исправления, в случае, если a node появится снова.
Какой должен сделать Astar быстрее и более подходящим для поиска путей.
Алгоритм Dijkstra определенно завершен и оптимален, что вы всегда найдете кратчайший путь. Однако он имеет тенденцию занимать больше времени, поскольку он используется в основном для обнаружения нескольких узлов цели.
A* search
, с другой стороны, имеет значение с эвристическими значениями, которые вы можете определить, чтобы приблизить свою цель ближе, например, расстояние до ворот на Манхэттене. Он может быть оптимальным или полным, что зависит от эвристических факторов. это определенно быстрее, если у вас есть одна цель node.
В A *, для каждого node вы проверяете исходящие соединения для своих.
Для каждого нового node вы вычисляете самую низкую стоимость пока (csf) в зависимости от веса соединений с этим node и расходами, которые вам приходилось на предыдущий node.
Кроме того, вы оцениваете стоимость от нового node до целевого node и добавьте его в csf. Теперь у вас есть общая стоимость (и т.д.). (etc = csf + расчетное расстояние до цели)
Затем вы выбираете из новых узлов тот, который имеет самый низкий и т.д.
Сделайте то же, что и раньше, пока не станет одним из новых узлов.
Дейкстра работает почти так же. За исключением того, что расчетное расстояние до цели всегда равно 0, и алгоритм сначала останавливается, когда цель является не только одним из новых узлов, но также и с наименьшим csf.
A * обычно быстрее, чем dijstra, хотя это не всегда так. В видеоиграх вы часто предпочитаете подход "достаточно близко для игры". Поэтому достаточно "достаточно близкого" оптимального пути от A *.