ダイクストラのアルゴリズムとAスターの違いは何ですか?


154

私はマリオAIコンテストのメンバーが何をしているかを見ていました。彼らの何人かはA *(A-Star)Pathing Algorithmを利用してかなりきちんとしたマリオボットを構築しています。

代替テキスト
マリオA *ボットの動作のビデオ

私の質問は、A-Starとダイクストラの違いを教えてください。それらを見ると、彼らは似ているようです。

なぜ誰かが他のものを使用するのですか?特にゲームでのパスのコンテキストでは?



@SLaks A *はdijkstraよりも多くのメモリを使用しますか?ダイクストラがそれらすべてを試しながら、a *のみが有望なノードをパスするとどうなりますか?
Poutrathor

回答:


177

ダイクストラはA *の特殊なケースです(ヒューリスティックがゼロの場合)。


1
ダイクストラでは、ソースからの距離のみを考慮します。そして、最小の頂点が考慮されますか?
クラーケン2013

4
A *はダイクストラのヒューリスティックを使用する特殊なケースだと思いました。ダイクストラがあったので、最初のアファイク。
Madmenyo 2013

46
@MennoGouw:はい、ダイクストラのアルゴリズムが最初に開発されました。しかし、これはより一般的なアルゴリズムA *の特殊なケースです。特別なケースが最初に発見され、その後一般化されることは決して珍しいことではありません。
Pieter Geerkens 2013

1
ヒューリスティックを知っている人にとっては素晴らしい答えです;)
lindhe '19年

1
A *とヒューリスティックの使用については、NorvigとRusselのAIブック
BoltzmannBrain

113

ダイクストラ:

これには、ソースから各ノードへの実際のコスト値である1つのコスト関数がありますf(x)=g(x)
実際のコストのみを考慮して、ソースから他のすべてのノードへの最短経路を見つけます。

検索:

2つのコスト関数があります。

  1. g(x):ダイクストラと同じ。ノードに到達するための実際のコストx
  2. h(x):ノードxから目標ノードまでのおおよそのコスト。これはヒューリスティック関数です。このヒューリスティック関数は、コストを過大評価してはなりません。つまり、ノードから目標ノードに到達するための実際のコストは、x以上である必要がありますh(x)。これは、許容ヒューリスティックと呼ばれます。

各ノードの合計コストは、 f(x)=g(x)+h(x)

A *検索は、有望と思われる場合にのみノードを展開します。他のすべてのノードに到達するのではなく、現在のノードから目標ノードに到達することにのみ焦点を当てます。ヒューリスティック関数が許容できる場合は最適です。

したがって、ヒューリスティック関数が将来のコストを概算するのに適している場合は、ダイクストラよりもはるかに少ないノードを探索する必要があります。


20

以前のポスターの発言に加えて、ダイクストラにはヒューリスティックがなく、各ステップで最小のコストでエッジを選択するため、グラフの多くを「カバー」する傾向があります。そのため、ダイクストラはA *よりも便利な場合があります。良い例は、複数の候補ターゲットノードがあるが、どれが最も近いかわからない場合です(A *の場合、候補ノードごとに1回、複数回実行する必要があります)。


17
複数の潜在的な目標ノードがある場合、それらすべてを含めるように目標テスト関数を変更できます。このように、A *は一度だけ実行する必要があります。
Brad Larsen

9

ダイクストラのアルゴリズムは、パスファインディングには決して使用されません。まともなヒューリスティックを思い付くことができれば、A *の使用は非常に簡単です(通常、ゲーム、特に2Dの世界では簡単です)。サーチスペースによっては、使用するメモリが少ないため、反復深化A *の方が好ましい場合があります。


5
なぜダイクストラがパスファインディングに使用されないのですか?詳しく説明できますか?
KingNestor 2009

2
ひどいヒューリスティックを思いついたとしても、ダイクストラよりも優れているからです。それが許されなくても時々。ドメインによって異なります。ダイクストラはメモリ不足の状況でも機能しませんが、IDA *は機能します。
シャギーフロッグ


7

ダイクストラはA *の特殊なケースです。

ダイクストラは、開始ノードから他のすべてへの最小コストを見つけます。A *は、開始ノードから目標ノードまでの最小コストを見つけます。

ダイクストラのアルゴリズムが経路探索に使用されることはありません。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

ダイクストラは、開始ノードから他のすべてへの最小コストを見つけます。A *は、開始ノードから目標ノードまでの最小コストを見つけます。

したがって、あるノードから別のノードへの最小距離が必要なだけの場合、ダイクストラは効率が悪いように思われます。


2
本当じゃない。標準のダイクストラを使用して、2点間の最短経路を提供します。
エミル

3
誤解を招かないでください。ダイクストラはsから他のすべての頂点に結果を与えます。したがって、動作が遅くなります。
Ivan Voroshilin 2013

@Emilコメントの2番目です。優先ノードから宛先ノードを削除するときに停止するだけで、ソースから宛先への最短パスが得られます。これは実際には元のアルゴリズムでした。
seteropere 2016

より正確には、ターゲットが指定されている場合、ダイクストラは、指定されたターゲットへのパスよりも短いパス上にあるすべてのノードへの最短パスを見つけます。A *のヒューリスティックの目的は、これらのパスの一部をプルーニングすることです。ヒューリスティックの有効性により、プルーニングする数が決まります。
Waylon Flinn 2017

@seteropereですが、宛先ノードが検索される最後のノードである場合はどうなりますか?A *のヒューリスティックと優先ノードの選択は、検索された宛先ノードがリストの最後のノードではないことを確認するのに役立つため、
明らかに効率が低下

5

A *はダイクストラのガイド付きバージョンと考えることができます。つまり、すべてのノードを探索するのではなく、ヒューリスティックを使用して方向を選択します。

より具体的に言えば、優先度キューでアルゴリズムを実装している場合、訪問しているノードの優先度は、コスト(以前のノードのコスト+ここに到達するためのコスト)とここからのヒューリスティックな見積もりの​​関数になります。ゴールへ。ダイクストラでは、優先度はノードへの実際のコストによってのみ影響を受けます。どちらの場合も、停止基準は目標に到達しています。


2

ダイクストラのアルゴリズムは、最短経路を確実に見つけます。一方、A *はヒューリスティックに依存します。このため、A *はダイクストラのアルゴリズムよりも高速で、優れたヒューリスティックがあれば良い結果が得られます。


4
A *はダイクストラと同じ結果になりますが、優れたヒューリスティックを使用すると高速になります。*アルゴリズムは、現在のノードと最終ノードの間の推定距離が実際の距離よりも低くなければならないなど、正しく動作するためのいくつかの条件を課します。
Alexandru

4
A *は、ヒューリスティックが許容される場合(常に過小評価)の最短パスを提供することが保証されています
Robert

1

Astar の疑似コードを見ると:

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

一方、あなたが同じ見ればダイクストラ

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

したがって、要点は、Astarはノードを1回しか評価しない
ことです。これは、
その経験則により、ノードを1回見れば十分であると考えられているためです。

OTOH、
ノードが再びポップアップした場合に備えて、ダイクストラのアルゴリズムはそれ自体を修正することをためらわない。

これにより、Astarがより高速になり、パス検索により適しているはずです。


7
これは真実ではありません。A*はノードを複数回見ることができます。実際、ダイクストラはA * ...の特殊なケースです
Emil

2
明確化のために、このいずれかを確認します。stackoverflow.com/questions/21441662/...
spiralmoon

すべての検索アルゴリズムには、「フロンティア」と「訪問セット」があります。どちらのアルゴリズムも、ノードが訪問済みセットに入ると、ノードへのパスを修正しません。設計上、ノードは境界から優先順でノードを訪問済みセットに移動します。ノードまでの既知の最小距離は、ノードが境界にある間のみ更新できます。ダイクストラはベストファーストサーチの形式であり、「訪問済み」セットに配置されたノードは再訪問されません。A *はこのプロパティを共有しており、フロンティアのどのノードを優先するかを選択するために補助推定器を使用します。en.wikipedia.org/wiki/Dijkstra%27s_algorithm
pygosceles

0

A *では、各ノードについて、発信接続をチェックします。
新しいノードごとに、このノードへの接続の重みと前のノードに到達する必要があったコストに応じて、これまでの最低コスト(csf)を計算します。
さらに、新しいノードからターゲットノードまでのコストを見積もり、これをcsfに追加します。これで推定総コスト(など)がわかりました。(ETC = CSF +推定ターゲットまでの距離)次はあなたがなど、最低で1が新しいノードから選ぶ
の1まで前と同じ操作を行い、新たなノードが対象となります。

ダイクストラはほとんど同じように機能します。ターゲットまでの推定距離が常に0であることを除いて、ターゲットがターゲットの1つだけではない場合、アルゴリズムは最初に停止します。新しいノードの、CSFが最も低いノードでも、ます。

A *は通常dijstraより高速ですが、常にそうであるとは限りません。ビデオゲームでは、「ゲームに十分近づく」というアプローチをよくします。したがって、通常、A *からの「十分に近い」最適なパスで十分です。


-1

ダイクストラのアルゴリズムは間違いなく完全で最適であり、常に最短経路を見つけることができます。ただし、主に複数の目標ノードを検出するために使用されるため、時間がかかる傾向があります。

A* search一方、ヒューリスティック値は重要です。ヒューリスティック値を定義すると、マンハッタンのゴールまでの距離など、ゴールに近づくように定義できます。ヒューリスティックな要素に応じて、最適または完全のいずれかになります。目標ノードが1つの場合は、明らかに高速です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.