Thuật toán của Dijkstra và A-Star so sánh như thế nào?


154

Tôi đã xem xét những gì các chàng trai trong Cuộc thi Mario AI đang làm và một số trong số họ đã xây dựng một số bot Mario khá gọn gàng sử dụng Thuật toán Đường dẫn A * (A-Star).

văn bản thay thế
( Video của Mario A * Bot đang hoạt động )

Câu hỏi của tôi là, A-Star so sánh với Dijkstra như thế nào? Nhìn qua chúng, chúng có vẻ giống nhau.

Tại sao ai đó sẽ sử dụng cái này hơn cái kia? Đặc biệt là trong bối cảnh của con đường trong trò chơi?



@SLaks A * sử dụng nhiều bộ nhớ hơn dijkstra? Làm thế nào nếu một * chỉ đường dẫn hứa hẹn các nút trong khi dijkstra thử tất cả?
Poutrathor

Câu trả lời:


177

Dijkstra là trường hợp đặc biệt đối với A * (khi các heuristic bằng 0).


1
Trong dijkstra, chúng ta chỉ xem xét khoảng cách từ nguồn phải không? Và đỉnh tối thiểu được đưa vào xem xét?
Kraken

4
Tôi nghĩ A * là trường hợp đặc biệt đối với Dijkstra khi họ sử dụng phương pháp phỏng đoán. Vì Dijkstra đã ở đó afaik đầu tiên.
Madmenyo

46
@MennoGouw: Có thuật toán của Dijkstra được phát triển đầu tiên; nhưng đó là trường hợp đặc biệt của thuật toán tổng quát hơn A *. Nó không phải là bất thường (trên thực tế, có lẽ là tiêu chuẩn) cho các trường hợp đặc biệt được phát hiện đầu tiên, và sau đó được khái quát hóa.
Pieter Geerkens

1
Câu trả lời tuyệt vời cho bất cứ ai biết heuristic;)
lindhe

1
A * và việc sử dụng phương pháp phỏng đoán được thảo luận kỹ trong cuốn sách AI của Norvig và Russel
BoltzmannBrain

113

Dijkstra:

Nó có một hàm chi phí, là giá trị chi phí thực từ nguồn đến từng nút : f(x)=g(x).
Nó tìm thấy con đường ngắn nhất từ ​​nguồn đến mọi nút khác bằng cách chỉ xem xét chi phí thực.

A * tìm kiếm:

Nó có hai chức năng chi phí.

  1. g(x): giống như Dijkstra. Chi phí thực sự để đạt được một nút x.
  2. h(x): chi phí gần đúng từ nút xđến nút mục tiêu. Đó là một chức năng heuristic. Hàm heuristic này không bao giờ nên đánh giá quá cao chi phí. Điều đó có nghĩa là, chi phí thực tế để đạt được nút mục tiêu từ nút xphải lớn hơn hoặc bằng h(x). Nó được gọi là heuristic đáng ngưỡng mộ.

Tổng chi phí của mỗi nút được tính bằng f(x)=g(x)+h(x)

Một tìm kiếm * chỉ mở rộng một nút nếu nó có vẻ đầy hứa hẹn. Nó chỉ tập trung để tiếp cận nút mục tiêu từ nút hiện tại, không tiếp cận mọi nút khác. Nó là tối ưu, nếu chức năng heuristic là chấp nhận được.

Vì vậy, nếu chức năng heuristic của bạn là tốt để ước tính chi phí trong tương lai, thì bạn sẽ cần khám phá các nút ít hơn rất nhiều so với Dijkstra.


20

Những gì người đăng trước đã nói, cộng với vì Dijkstra không có kinh nghiệm và ở mỗi bước chọn các cạnh với chi phí nhỏ nhất, nó có xu hướng "bao phủ" nhiều hơn biểu đồ của bạn. Do đó, Dijkstra có thể hữu ích hơn A *. Ví dụ điển hình là khi bạn có một số nút mục tiêu ứng cử viên, nhưng bạn không biết, nút nào gần nhất (trong trường hợp A * bạn sẽ phải chạy nó nhiều lần: một lần cho mỗi nút ứng viên).


17
Nếu có một số nút mục tiêu tiềm năng, người ta có thể chỉ cần thay đổi chức năng kiểm tra mục tiêu để bao gồm tất cả chúng. Theo cách này, A * sẽ chỉ cần được chạy một lần.
Brad Larsen

9

Thuật toán của Dijkstra sẽ không bao giờ được sử dụng cho tìm đường. Sử dụng A * là không có trí tuệ nếu bạn có thể đưa ra một heuristic phong nha (thường dễ dàng cho các trò chơi, đặc biệt là trong thế giới 2D). Tùy thuộc vào không gian tìm kiếm, lặp đi lặp lại A * đôi khi thích hợp hơn vì nó sử dụng ít bộ nhớ hơn.


5
Tại sao Dijkstra không bao giờ được sử dụng để tìm đường? Bạn có thể xây dựng?
KingNestor

2
Bởi vì ngay cả khi bạn có thể nghĩ ra một heuristic tệ hại, bạn sẽ làm tốt hơn Dijkstra. Đôi khi ngay cả khi nó không được chấp nhận. Nó phụ thuộc vào tên miền. Dijkstra cũng sẽ không hoạt động trong các tình huống bộ nhớ thấp, trong khi IDA * sẽ.
Ếch xù lông


7

Dijkstra là trường hợp đặc biệt cho A *.

Dijkstra tìm thấy các chi phí tối thiểu từ nút bắt đầu cho tất cả những người khác. A * tìm thấy chi phí tối thiểu từ nút bắt đầu đến nút mục tiêu.

Thuật toán của Dijkstra sẽ không bao giờ được sử dụng để tìm đường dẫn. Sử dụng A * người ta có thể đưa ra một heuristic phong nha. Tùy thuộc vào không gian tìm kiếm, lặp lại A * là thích hợp hơn vì nó sử dụng ít bộ nhớ hơn.

Mã cho thuật toán của Dijkstra là:

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

Mã cho thuật toán A * là:

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)

bỏ qua hàng xóm đã được thiết lập kín sẽ cho tối ưu. Thử nó trên biểu đồ này (Đây là một ví dụ video youtube, bỏ qua ngôn ngữ) sẽ đưa ra câu trả lời sai.
itjwala

5

Dijkstra tìm thấy các chi phí tối thiểu từ nút bắt đầu cho tất cả những người khác. A * tìm thấy chi phí tối thiểu từ nút bắt đầu đến nút mục tiêu.

Do đó, dường như Dijkstra sẽ kém hiệu quả hơn khi tất cả những gì bạn cần là khoảng cách tối thiểu từ nút này đến nút khác.


2
Đây không phải là sự thật. Dijkstra tiêu chuẩn được sử dụng để đưa ra con đường ngắn nhất giữa hai điểm.
Emil

3
Xin đừng đánh lạc hướng, Dijkstra đưa ra kết quả từ s cho tất cả các đỉnh khác. Do đó, nó hoạt động chậm hơn.
Ivan Voroshilin

Tôi thứ hai @Emil bình luận. Tất cả những gì bạn cần làm là dừng lại khi xóa nút đích khỏi hàng ưu tiên và bạn có đường dẫn ngắn nhất từ ​​nguồn đến đích. Đây là thuật toán ban đầu thực sự.
seteropere

Chính xác hơn: nếu một mục tiêu được chỉ định, Dijkstra sẽ tìm thấy con đường ngắn nhất tới tất cả các nút nằm trên các đường dẫn ngắn hơn đường dẫn đến mục tiêu được chỉ định. Mục đích của heuristic trong A * là cắt tỉa một số đường dẫn này. Hiệu quả của heuristic xác định có bao nhiêu được cắt tỉa.
Waylon Flinn

@seteropere, nhưng nếu nút đích của bạn là nút cuối cùng được tìm kiếm thì sao? Nó chắc chắn là ít hiệu quả hơn, vì các heuristic của A * và chọn các nút ưu tiên là điều giúp đảm bảo rằng nút đích được tìm kiếm không phải là nút cuối cùng trong danh sách
Knight0fDragon

5

Bạn có thể coi A * là phiên bản được hướng dẫn của Dijkstra. Có nghĩa là, thay vì khám phá tất cả các nút, bạn sẽ sử dụng phương pháp heuristic để chọn hướng.

Nói một cách cụ thể hơn, nếu bạn triển khai các thuật toán với hàng ưu tiên thì mức độ ưu tiên của nút bạn đang truy cập sẽ là một hàm của chi phí (các nút trước đó chi phí + chi phí để có được ở đây) và ước tính heuristic từ đây đến mục tiêu. Trong khi ở Dijkstra, mức độ ưu tiên chỉ bị ảnh hưởng bởi chi phí thực tế cho các nút. Trong cả hai trường hợp, tiêu chí dừng là đạt được mục tiêu.


2

Thuật toán của Dijkstra tìm thấy con đường ngắn nhất chắc chắn. Mặt khác A * phụ thuộc vào heuristic. Vì lý do này, A * nhanh hơn thuật toán của Dijkstra và sẽ cho kết quả tốt nếu bạn có một heuristic tốt.


4
A * cho kết quả tương tự như Dijkstra, nhưng nhanh hơn khi bạn sử dụng phương pháp heuristic tốt. Thuật toán * áp đặt một số điều kiện để hoạt động chính xác như khoảng cách ước tính giữa nút hiện tại và nút cuối cùng phải thấp hơn khoảng cách thực.
Alexandru

4
A * được đảm bảo đưa ra con đường ngắn nhất khi heuristic được chấp nhận (luôn bị đánh giá thấp)
Robert

1

Nếu bạn nhìn vào psuedocode cho Astar:

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

Trong khi đó, nếu bạn nhìn tương tự cho Dijkstra :

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

Vì vậy, vấn đề là, Astar sẽ không đánh giá một nút nhiều lần,
vì họ tin rằng nhìn vào một nút là đủ, do
các heuristic của nó.

OTOH, thuật toán của Dijkstra không ngại tự sửa, trong trường hợp
một nút bật lên một lần nữa.

Điều này sẽ làm cho Astar nhanh hơn và phù hợp hơn cho việc tìm đường.


7
Điều này không đúng: A * có thể nhìn vào các nút nhiều lần. Trên thực tế, Dijkstra là trường hợp đặc biệt của A * ...
Emil

2
Kiểm tra cái này để làm rõ: stackoverflow.com/questions/21441662/ từ
xoắn ốc

Tất cả các thuật toán tìm kiếm đều có "biên giới" và "tập hợp truy cập". Không thuật toán nào sửa đường dẫn đến một nút khi nó nằm trong tập đã truy cập: theo thiết kế, chúng di chuyển các nút từ biên giới sang tập đã truy cập theo thứ tự ưu tiên. Khoảng cách tối thiểu được biết đến các nút chỉ có thể được cập nhật khi chúng ở biên giới. Dijkstra là một hình thức tìm kiếm đầu tiên tốt nhất và một nút sẽ không bao giờ được xem lại sau khi nó được đặt trong tập hợp "đã truy cập". A * chia sẻ thuộc tính này và nó sử dụng một công cụ ước tính phụ trợ để chọn các nút trên biên giới để ưu tiên. vi.wikipedia.org/wiki/Dijkstra%27s_alacticm
pygosceles

0

Trong A *, đối với mỗi nút bạn kiểm tra các kết nối đi cho chúng.
Đối với mỗi nút mới, bạn tính chi phí thấp nhất cho đến nay (csf) tùy thuộc vào trọng số của các kết nối đến nút này và chi phí bạn phải trả cho nút trước đó.
Ngoài ra, bạn ước tính chi phí từ nút mới đến nút đích và thêm chi phí này vào csf. Bây giờ bạn có tổng chi phí ước tính (vv). (etc = csf + khoảng cách ước tính đến mục tiêu) Tiếp theo bạn chọn từ các nút mới có nút thấp nhất, v.v.
Thực hiện tương tự như trước cho đến khi một trong các nút mới sẽ là mục tiêu.

Dijkstra hoạt động gần như giống nhau. Ngoại trừ khoảng cách ước tính đến mục tiêu luôn là 0 và trước tiên thuật toán dừng lại khi mục tiêu không chỉ là một trong các nút mới , mà còn là nút có csf thấp nhất.

A * thường nhanh hơn dijstra, mặc dù điều này sẽ không luôn luôn như vậy. Trong các trò chơi video, bạn thường tìm cách tiếp cận "đủ gần cho một trò chơi". Do đó, đường dẫn tối ưu "đủ gần" từ A * thường đủ.


-1

Thuật toán của Dijkstra hoàn toàn đầy đủ và tối ưu mà bạn sẽ luôn tìm thấy con đường ngắn nhất. Tuy nhiên, nó có xu hướng mất nhiều thời gian hơn vì nó được sử dụng chủ yếu để phát hiện nhiều nút mục tiêu.

A* searchmặt khác, vấn đề với các giá trị heuristic, mà bạn có thể xác định để đạt được mục tiêu của mình gần hơn, chẳng hạn như khoảng cách manhattan hướng tới mục tiêu. Nó có thể là tối ưu hoặc hoàn thành phụ thuộc vào các yếu tố heuristic. nó chắc chắn nhanh hơn nếu bạn có một nút mục tiêu duy nhất.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.