A*(「Aスター」と発音)は、その完全性、最適性、および最適な効率性から、コンピュータサイエンスの多くの分野で使用されているグラフ探索および経路探索アルゴリズムです。 [ 1 ]重み付きグラフ、ソースノード、およびゴールノードが与えられると、このアルゴリズムは、ソースからゴールまでの最短経路(与えられた重みに関して)を見つけます。
実用上の大きな欠点の 1 つは、空間計算量は、d が最も浅い解の深さ (始点ノードから任意の目標ノードまでの最短経路の長さ) であり、b が分岐係数(任意の状態における後継ノードの最大数)である。実際の旅行経路決定システムでは、一般的に、グラフを前処理してパフォーマンスを向上させるアルゴリズム[ 2 ]や、メモリ制限のあるアプローチに劣るが、多くの場合、A* は依然として最良のソリューションである。[ 3 ]
スタンフォード研究所(現SRIインターナショナル)のピーター・ハート、ニルス・ニルソン、バートラム・ラファエルが1968年にこのアルゴリズムを初めて発表した。[ 4 ]これはダイクストラ法の拡張と見なすことができる。A*はヒューリスティクスを用いて探索を誘導することで、より優れたパフォーマンスを実現している。
A*アルゴリズムは、指定された出発点から考えられるすべての目的地までの最短経路ツリー全体を生成するのではなく、指定された目的地までの最短経路が見つかった時点で終了します。

A* は、自身の行動を計画できる移動ロボットを構築することを目的としたShakey プロジェクトの一環として作成されました。Nils Nilsson は当初、 Shakey の経路計画にグラフ トラバーサー アルゴリズム[ 5 ]を使用することを提案しました。 [ 6 ]グラフ トラバーサーは、ノードnから目標ノードまでの推定距離であるヒューリスティック関数h ( n )によってガイドされます。開始ノードからnまでの距離であるg ( n )は完全に無視されます。Bertram Raphael は、合計g ( n ) + h ( n )を使用することを提案しました。[ 7 ] Peter Hart は、現在ヒューリスティック関数の許容性と一貫性と呼ばれる概念を発明しました。A* は当初、経路のコストがそのコストの合計である場合に最小コスト経路を見つけるために設計されましたが、コスト代数の条件を満たすあらゆる問題に対して最適な経路を見つけるために A* を使用できることが示されています。[ 8 ]
1968年のオリジナルのA*論文[ 4 ]には、ヒューリスティック関数が一貫性があり、A*のタイブレーク規則が適切に選択されていれば、A *のようなアルゴリズム[ a ]はA*よりも少ないノードを展開することはできないという定理が含まれていました。数年後に「訂正」 [ 9 ]が発表され、一貫性は必要ないという主張がなされましたが、これは1985年にDechterとPearlによるA*の最適性(現在は最適効率と呼ばれている)に関する決定的な研究で誤りであることが示されました。この研究では、許容可能だが一貫性のないヒューリスティックを持つA*が、別のA*のようなアルゴリズムよりも任意に多くのノードを展開する例が示されました。[ 10 ]

A*アルゴリズムは、情報に基づく探索アルゴリズム、または最良優先探索アルゴリズムであり、重み付きグラフを用いて定式化されます。グラフの特定の開始ノードから出発し、与えられた目標ノードまでの経路のうち、コストが最小(移動距離が最小、所要時間が最短など)となる経路を見つけることを目指します。このアルゴリズムは、開始ノードから始まる経路のツリーを維持し、目標ノードに到達するまで、一度に1つのエッジずつ経路を拡張していくことでこれを実現します。
メインループの各イテレーションで、A* はどのパスを延長するかを決定する必要があります。これは、パスのコストと、パスを目標まで完全に延長するために必要なコストの推定値に基づいて行われます。具体的には、A* は、コストを最小化するパスを選択します。
ここで、nはパス上の次のノード、g ( n )は開始ノードからnまでのパスのコスト、h ( n ) はnから目標までの最も安いパスのコストを推定するヒューリスティック関数です。ヒューリスティック関数は問題固有のものです。
A* の典型的な実装では、優先度付きキューを使用して、展開する最小 (推定) コストのノードを繰り返し選択します。この優先度付きキューは、オープン セット、フリンジ、またはフロンティアとして知られています。アルゴリズムの各ステップで、 f ( x )値が最も低いノードがキューから削除され、その隣接ノードのf値とg値がそれに応じて更新され、これらの隣接ノードがキューに追加されます。削除されたノード (つまり、すべてのフリンジ ノードの中でf値が最も低いノード) が目標ノードになるまで、アルゴリズムは続きます。[ b ]目標のf値は、許容ヒューリスティックでは目標でのhがゼロであるため、最短経路のコストにもなります。
これまで説明したアルゴリズムは、最短経路の長さしか示しません。実際の手順の順序を求めるには、経路上の各ノードが先行ノードを追跡するようにアルゴリズムを簡単に修正できます。このアルゴリズムを実行すると、終了ノードは先行ノードを指し示すようになり、あるノードの先行ノードが開始ノードになるまで、このプロセスが繰り返されます。
例えば、地図上で最短ルートを探す場合、h ( x )はゴールまでの直線距離を表すことがあります。これは、物理的に見て任意の2点間の最小距離だからです。ビデオゲームのグリッドマップの場合、利用可能な移動方法(4方向または8方向)に応じて、タクシー距離またはチェビシェフ距離を使用する方が適しています。
ヒューリスティックh が、グラフのすべてのエッジ( x , y )に対して追加条件h ( x ) ≤ d ( x , y ) + h ( y )を満たす場合 (ここでd はそのエッジの長さを表す)、hは単調または一貫性があると呼ばれます。一貫性のあるヒューリスティックを使用すると、A* はどのノードも 1 回以上処理することなく最適なパスを見つけることが保証され、A* はコストを削減したDijkstra アルゴリズムd' ( x , y ) = d ( x , y ) + h ( y ) - h ( x )を実行することと同等になります。[ 11 ]
以下の擬似コードは、そのアルゴリズムを説明するものです。
function reconstruct_path ( came_from , current ) total_path := {current} while current in came_from . keys : current := came_from [ current ] total_path . prepend ( current ) return total_path// A* は開始ノードから目標ノードまでの経路を見つけます。// h はヒューリスティック関数です。h(n) はノード n から目標ノードに到達するまでのコストを推定します。function a_star ( start , goal , h ) // 再拡張が必要になる可能性のある発見されたノードのセット。// 最初は開始ノードのみがわかっています。// これは通常、ハッシュセットではなく、最小ヒープまたは優先度キューとして実装されます。open_set := {start}// ノード n の場合、came_from[n] は、現在知られている開始ノードから n までの最短経路で、nの直前のノードです。came_from :=空のマップ// ノード n の場合、g_score[n] は、start から n までの最も安いパスの現在わかっているコストです。g_score :=デフォルト値がInfinityのマップg_score [ start ] := 0// ノード n の場合、f_score[n] := g_score[n] + h(n) となります。f_score[n] は、n を経由する場合に開始から終了までのパスがどれだけ安価になるかについての現在の最良の推測を表します。f_score : =デフォルト値がInfinityのマップf_score [ start ] := h ( start )while open_set is not empty // open_set が最小ヒープまたは優先度キューの場合、この操作は O(Log(N)) 時間で実行できますcurrent : = open_set内でf_score []値が最も低いノードif current = goal return reconstruct_path ( came_from , current )open_set.remove ( current ) for each neighbor of current // d(current,neighbor) is the weight of the edge from current to neighbor // tentative_g_score is the distance from start to the neighbor through current tentative_g_score := g_score [ current ] + d ( current , neighbor ) if tentative_g_score < g_score [ neighbor ] // this path to neighbor is better than which previous one. Record it! came_from [ neighbor ] : = current g_score [ neighbor ] : = tentative_g_score f_score [ neighbor ] := tentative_g_score + h ( neighbor ) if neighbor not in open_set open_set.add ( neighbor )// オープンセットは空だが、目標は達成されなかったため、失敗を返す注記:この擬似コードでは、ノードが 1 つのパスで到達され、 から削除されopen_set、その後、より安価なパスで到達された場合、再び に追加されます。これは、ヒューリスティック関数が許容可能だが 一貫性open_setがない場合に返されるパスが最適であることを保証するために不可欠です。ヒューリスティックが一貫性がある場合、へのパスからノードが削除されると、それが最適であることが保証されるため、ノードに再び到達すると、テスト ' ' は常に失敗します。ここで実装されている擬似コードは、 A* のグラフ探索バージョンと呼ばれることがあります。[ 12 ]これは、ノードを に戻すための' ' テストがないバージョンとは対照的です。このバージョンは、A* の木探索バージョンと呼ばれることがあり、最適性を保証するために一貫性のあるヒューリスティックが必要です。open_settentative_g_score < g_score[neighbor]tentative_g_score < g_score[neighbor]open_set

ノードが道路で接続された都市であり、h(x)が目標地点までの直線距離である場合の、A*アルゴリズムの動作例を以下に示します。

凡例:緑:開始地点、青:ゴール地点、オレンジ:訪問済み
A*アルゴリズムは実世界で応用されています。この例では、エッジは鉄道、h(x)は目標地点までの大円距離(球面上での最短距離)を表しています。このアルゴリズムは、ワシントンD.C.とロサンゼルス間の経路を探索しています。

A*アルゴリズムの実装パフォーマンスに大きな影響を与える、いくつかの単純な最適化や実装の詳細があります。まず注目すべき点は、優先度キューが同順位を処理する方法が、状況によってはパフォーマンスに大きな影響を与える可能性があるということです。キューがLIFO方式で動作するように同順位を解消すると、A*アルゴリズムは等コストのパス間で深さ優先探索のように動作します(複数の同等に最適な解を探索することを避ける)。
検索の最後にパスが必要な場合、各ノードにそのノードの親への参照を保持するのが一般的です。検索の最後に、これらの参照を使用して最適なパスを復元できます。これらの参照を保持する場合、同じノードが優先度キューに複数回出現しないことが重要になる場合があります(各エントリはノードへの異なるパスに対応し、それぞれ異なるコストを持ちます)。ここでの標準的なアプローチは、追加しようとしているノードが既に優先度キューに存在するかどうかを確認することです。存在する場合は、優先度と親ポインタを、より低いコストのパスに対応するように変更します。標準的なバイナリヒープベースの優先度キューは、その要素の1つを検索する操作を直接サポートしていませんが、要素をヒープ内の位置にマッピングするハッシュテーブルで拡張することで、この優先度低下操作を対数時間で実行できます。あるいは、フィボナッチヒープは、同じ優先度低下操作を償却定数時間で実行できます。
ダイクストラ法は、均一コスト探索アルゴリズムのもう一つの例として、A* の特殊なケースと見なすことができる。すべてのxに対して。[ 13 ] [ 14 ]一般的な深さ優先探索は、非常に大きな値で初期化されたグローバルカウンタCが存在することを考慮すると、A* を使用して実装できます。ノードを処理するたびに、新しく発見されたすべての隣接ノードにCを割り当てます。割り当てごとに、カウンタC を1 つ減らします。したがって、ノードが早く発見されるほど、そのノードの値。ダイクストラ法と深さ優先探索はどちらも、 を含めずに、より効率的に実装できます。各ノードにおける値。
非負のエッジ重みを持つ有限グラフでは、A* は必ず終了し、完全です。つまり、解 (開始から目標までのパス) が存在する場合は必ず見つけます。有限の分岐係数とゼロから離れたエッジコストを持つ無限グラフでは (ある固定値に対してA* は、解が存在する場合にのみ終了することが保証されています。[ 1 ]
探索アルゴリズムは、最適解を返すことが保証されている場合に許容可能であると言われます。A* で使用されるヒューリスティック関数が許容可能であれば、A* も許容可能です。この直感的な「証明」は次のとおりです。
ノードが訪問済みでオープンセットに含まれていない場合、そのノードは閉じていると言います。ノードをオープンセットから削除すると、そのノードは閉じられます。A* アルゴリズムの基本的な性質は、以下で証明の概略を示すように、閉鎖されています。は、開始地点から目標地点までの実際の距離の楽観的な推定値(下限値)です。したがって、目標ノードでは、 、は閉鎖されています、は、実際の距離に他なりません。一方で、目標までの経路の長さにヒューリスティック項を加えたものであるため、実際の距離より小さくなることはありません。
これから、ノードが閉鎖されています。これは楽観的な推定値です。開集合が空でない場合は、少なくとも1つのノードが存在することを確認すれば十分です。目標への最適な経路に沿っては開始地点からの真の距離です。なぜなら、その場合 + は目標までの距離を過小評価しており、したがって閉じた頂点に選択されたより小さな値も同様に過小評価している。 をスタート地点からゴール地点までの最適な経路とする。最後に閉じられたノードになるそのためには、スタート地点から までの実際の距離です。(開始点はそのような頂点の 1 つです)。次のノードは正しい値、更新されたとき閉まっていたが、閉まっていないので開いている。
アルゴリズム A は、問題集合P上の代替アルゴリズム集合Altsに対して最適に効率的であるとは、 P内のすべての問題 P とAlts内のすべてのアルゴリズム A′について、P を解く際に A によって展開されるノードの集合が、P を解く際に A′ によって展開されるノードの集合の部分集合 (場合によっては等しい) である場合をいう。 A* の最適効率に関する決定的な研究は、Rina Dechter と Judea Pearl によるものである。[ 10 ]彼らは 、A* のヒューリスティックが単に許容可能であるか、一貫性がありかつ許容可能であるかという条件と組み合わせたAltsとP のさまざまな定義を検討した。彼らが証明した最も興味深い肯定的な結果は、一貫性のあるヒューリスティックを持つ A* は、すべての「非病理的」探索問題において、すべての許容可能な A* ライクな探索アルゴリズムに対して最適に効率的であるということである。大まかに言えば、彼らの非病理的問題の概念は、現在私たちが「タイブレークまで」と呼んでいるものである。 A*のヒューリスティックが許容可能であっても一貫性がない場合、この結果は成り立ちません。その場合、DechterとPearlは、一部の非病理的な問題において、A*よりも任意に少ないノード数で展開できる、許容可能なA*類似アルゴリズムが存在することを示しました。
最適な効率とは、ノード展開の数(A* のメインループの反復回数)ではなく、展開されるノードのセットに関するものです。使用されているヒューリスティックが許容可能だが一貫性がない場合、ノードが A* によって何度も展開される可能性があり、最悪の場合は指数関数的に展開される可能性があります。 [ 15 ] このような状況では、ダイクストラ法は A* を大きく上回る可能性があります。しかし、最近の研究では、この異常なケースは、検索グラフのエッジの重みがグラフのサイズに対して指数関数的である特定の人為的な状況でのみ発生し、特定の一貫性のない(ただし許容可能な)ヒューリスティックによって A* 探索におけるノード展開の回数が減少する可能性があることがわかっています。[ 16 ] [ 17 ]

許容基準は最適な解経路を保証する一方で、A*アルゴリズムが最適な経路を見つけるために、同等に優れたすべての経路を検証する必要があることも意味します。近似最短経路を計算するには、許容基準を緩和することで最適性を犠牲にして探索を高速化できます。多くの場合、この緩和に制限を設けて、解経路が最適な解経路の (1 + ε ) 倍よりも悪くならないことを保証したいと考えます。この新しい保証は、 ε許容と呼ばれます。
ε許容アルゴリズムはいくつか存在する。
ヒューリスティック探索アルゴリズムであるA*の性能は、ヒューリスティック関数の質に大きく左右される。ヒューリスティックが目標に対する真のコストに近似していれば、A*アルゴリズムはノード展開の数を大幅に削減できる。一方、不適切なヒューリスティックは、多くの不必要な展開につながる可能性がある。
最悪の場合、A*はすべてのノードを展開しますそのために、 どここれは最適な目標ノードのコストです。
ノードがあると仮定しますオープンリストで、そしてそれは次に展開されるノードです。目標ノードには、 そして目標ノードはf値が低くなり、展開される前にしたがって、A* はノードを拡張しません。。
より少ないノードを展開する最適なアルゴリズムが存在すると仮定します。最悪の場合、同じヒューリスティックを使用します。つまり、何らかのノードが存在する必要があります。そのためしかし、アルゴリズムはそれを展開しないことを選択する。
次に、コストの新しいエッジが追加された修正グラフを考えてみましょう。(と) が追加されます目標に向かって。もしすると、新しい最適経路はしかし、アルゴリズムは依然として拡張を回避しているためそうすると、新たな最適経路を見失い、最適性を損なうことになる。
したがって、A* を含む最適なアルゴリズムでは、ノード数を減らすことはできません。最悪の場合。
A*の最悪ケースの複雑さは、しばしば次のように表現されます。、 どこは分岐係数であり、は最も浅いゴールの深さです。これは大まかな直感にはなりますが、A* の実際の動作を正確に捉えているわけではありません。
より正確な境界では、ノードの数を考慮します。。 もしは、-異なるノード間のコストを考慮すると、A*は最大で以下まで拡張される可能性があります。
これは、最悪の場合における時間計算量と空間計算量の両方を表しています。
A* の空間計算量は、生成されたすべてのノードをメモリに保持するため、他のすべてのグラフ検索アルゴリズムとほぼ同じです。[ 1 ]実際には、これが A* 探索の最大の欠点であることが判明し、反復深化 A*、メモリ制限 A*、SMA*などのメモリ制限ヒューリスティック探索の開発につながりました。
A* は、ビデオゲームなどのアプリケーションにおける一般的な経路探索問題によく使用されますが、元々は汎用グラフ探索アルゴリズムとして設計されました。 [ 4 ]自然言語処理における確率的文法を用いた構文解析 問題など、さまざまな問題に応用されています。[ 27 ] その他には、オンライン学習を用いた情報検索などがあります。[ 28 ]
A*が貪欲な最良優先探索アルゴリズムと異なる点は、既に移動したコスト/距離g ( n )を考慮に入れることです。
ダイクストラ法の一般的な変種の中には、ヒューリスティックがA*アルゴリズムの特殊なケースと見なせるものがある。すべてのノードについて。[ 13 ] [ 14 ]ダイクストラ法とA*法はどちらも動的計画法の特殊なケースである。[ 29 ] A*法自体は分岐限定法 の一般化の特殊なケースである。[ 30 ]
最初に検討した問題の一つは、シェイキーが場所から場所へ移動する際に使用できる「ウェイポイント」のシーケンスをどのように計画するかということでした。[…] シェイキーのナビゲーション問題は、私が先に述べたものと同様の探索問題です。
当時Shakeyの開発を指揮していたバートラム・ラファエルは、スコアのより良い値は、初期位置から移動した距離と、ロボットが進むべき距離に関する私の経験的な推定値の合計であると指摘した。