私はOpenStreetMapマップで宅配便/ロジスティクスシミュレーションを書いていますが、下の写真のような基本的なA *アルゴリズムは、大規模なマップ(Greater Londonなど)には十分高速ではないことに気付きました。
緑のノードは、オープンセット/優先度キューに入れられたノードに対応し、膨大な数(マップ全体は100万から200万程度)のため、写真のルートを見つけるのに5秒ほどかかります。残念ながら、ルートごとの100ミリ秒は私の絶対的な限界についてです。
現在、ノードは隣接リストと空間100x100 2D配列の両方に保存されています。
より高速なクエリのために、前処理の時間、スペース、および必要に応じてルートの最適性をトレードオフできる方法を探しています。ヒューリスティックコストの定型のHaversine式は、プロファイラーによれば最も高価な関数です。私はできる限り基本A *を最適化しました。
たとえば、2Dアレイの各象限から任意のノードXを選択し、各象限間でA *を実行すると、後続のシミュレーションのためにルートをディスクに保存できると考えていました。クエリを実行する場合、A *検索を象限でのみ実行して、事前計算されたルートとXの間を移動できます。
私が上で説明したもののより洗練されたバージョン、またはおそらく私が追求すべき別の方法はありますか?どうもありがとう!
記録として、ヒューリスティックコストを任意に重み付けし、ランダムに選択されたノードの10ペア間のパスを計算するためのベンチマーク結果を次に示します。
Weight // AvgDist% // Time (ms)
1 1 1461.2
1.05 1 1327.2
1.1 1 900.7
1.2 1.019658848 196.4
1.3 1.027619169 53.6
1.4 1.044714394 33.6
1.5 1.063963413 25.5
1.6 1.071694171 24.1
1.7 1.084093229 24.3
1.8 1.092208509 22
1.9 1.109188175 22.5
2 1.122856792 18.2
2.2 1.131574742 16.9
2.4 1.139104895 15.4
2.6 1.140021962 16
2.8 1.14088128 15.5
3 1.156303676 16
4 1.20256964 13
5 1.19610861 12.9
驚くべきことに、係数を1.1に増やすと、同じルートを維持しながら実行時間がほぼ半分になりました。
最適性をトレードオフすることで、はるかに高速にすることができるはずです。ウィキペディアの 許容性と最適性 を参照してください。
アイデアは、最適パスの1 + epsilon
倍以上のソリューションをもたらすepsilon
値を使用することですが、アルゴリズムによって考慮されるノードの数が少なくなります。これは、返されたソリューションが常に最適パスの1 + epsilon
倍になるという意味ではないことに注意してください。これは最悪のケースです。あなたの問題に対して実際にどのように動作するかは正確にはわかりませんが、検討する価値はあると思います。
ウィキペディアのこのアイデアに依存するアルゴリズムがいくつか与えられます。これはアルゴリズムを改善するための最善の策であり、適切なパスを返している間に制限時間内に実行できる可能性があると思います。
あなたのアルゴリズムは5秒で数百万のノードを処理するので、実装にもバイナリヒープを使用すると思いますか?それらを手動で実装した場合は、それらが単純な配列として実装されていること、およびバイナリヒープであることを確認してください。
この問題には、多くの事前計算を行う専門アルゴリズムがあります。事前計算により、A *が使用するグラフに情報が追加され、直線距離よりもはるかに正確なヒューリスティックが生成されます。ウィキペディアは http://en.wikipedia.org/wiki/Shortest_path_problem#Road_networks でいくつかのメソッドの名前を示しており、ハブラベリングがリーダーであると述べています。これに関するクイック検索が表示されます http://research.Microsoft.com/pubs/142356/HL-TR.pdf 。 A *を使用する古いバージョンは http://research.Microsoft.com/pubs/64505/goldberg-sp-wea07.pdf にあります。
本当にHaversineを使用する必要がありますか?ロンドンをカバーするには、平らな地球を想定してピタゴラスを使用したり、各リンクの長さをグラフに保存したりできると思います。
Microsoft Researchがこのテーマについて書いた本当に素晴らしい記事があります。
http://research.Microsoft.com/en-us/news/features/shortestpath-070709.aspx
元の論文はここでホストされています(PDF):
http://www.cc.gatech.edu/~thad/6601-gradAI-fall2012/02-search-Gutman04siam.pdf
基本的に、試すことができることがいくつかあります。
GraphHopper は、高速で非ヒューリスティックで柔軟なルーティングを実現するために、さらに2つのことを行います(注:私は著者であり、オンラインで試すことができます here )
だから、ロンドンをもっと速く行くためのルートを手に入れることができるはずです。
さらに、デフォルトモードは速度モードであり、すべてを1桁速くします(たとえば、ヨーロッパの広いルートの場合は30ミリ秒)。ただし、前処理が必要なため、柔軟性は低くなります( Contraction Hierarchies )。これが気に入らない場合は、無効にして、含まれている自動車の道路をさらに微調整するか、おそらくトラックの新しいプロファイルを作成してください。サービスストリートとトラックを除外すると、さらに30%向上します。また、あらゆる双方向アルゴリズムと同様に、並列検索を簡単に実装できます。
「象限」でアイデアを練り上げることは価値があると思います。より厳密には、これを低解像度ルート検索と呼びます。
十分に近いX接続ノードを選択して、それらを単一の低解像度ノードとして扱うことができます。グラフ全体をそのようなグループに分割すると、低解像度のグラフが得られます。これは準備段階です。
ソースからターゲットへのルートを計算するには、まずそれらが属する低解像度ノードを特定し、低解像度ルートを見つけます。次に、高解像度グラフでルートを見つけて結果を改善しますが、アルゴリズムを低解像度ルートの低解像度ノードに属するノードのみに制限します(オプションで、ある深さまでの隣接する低解像度ノードを検討することもできます) )。
これは、高低だけでなく、複数の解像度に一般化することもできます。
最後に、最適に十分近いルートを取得する必要があります。これはローカルで最適ですが、解像度のジャンプ(つまり、ノードのグループが単一ノードとして定義されている場合に行う近似)によっては、グローバルでの最適よりもいくらか劣る場合があります。
私は大手のナビゲーション会社で働いていたので、組み込み機器であっても100ミリ秒でロンドンからアテネまでのルートを確保できると確信できます。 Greater Londonは便利なため、テストマップになります(RAM-これは実際には必要ありません)。
まず、A *は完全に古くなっています。その主な利点は、「技術的に」前処理を必要としないことです。実際には、とにかくOSMマップを前処理する必要があるので、それは無意味な利点です。
速度を大幅に向上させる主な手法は、アークフラグです。たとえば5x6のセクションにマップを分割すると、セクションごとに32ビット整数で1ビットの位置を割り当てることができます。これで、各Edgeについて、旅行中に役立つかどうかを判断できますtoセクション{X,Y}
別のセクションから。多くの場合、道路は双方向であり、これは2つの方向のうち1つだけが役立つことを意味します。したがって、2つの方向のうちの1つでそのビットが設定され、もう1つでクリアされます。これは本当の利点とは思えないかもしれませんが、多くの交差点で、検討する選択肢の数を2から1に減らすことを意味します。これは、単一のビット演算のみを必要とします。
ここでの法案に合うかもしれないA *バリエーションは数十あります。ただし、ユースケースについて考える必要があります。
私たちがあなたとあなたの雇用主が知っているすべての詳細を知る方法はありません。したがって、最初の目的地は CiteSeer またはGoogle Scholarである必要があります。同じ一般的な制約のセットでパスファインディングを扱う論文を探してください。
次に、3つまたは4つのアルゴリズムにダウンセレクトし、プロトタイピングを行い、それらがどのようにスケールアップするかをテストし、それらを微調整します。ポイント間の距離、残り時間、またはその他の要因に基づいて、同じグランドパスファインディングルーチンでさまざまなアルゴリズムを組み合わせることができることに注意してください。
すでに述べたように、ターゲット領域の小規模に基づいて、Haversineをドロップすることはおそらく、高価なトリガー評価で貴重な時間を節約する最初のステップです。注:緯度と経度の座標でユークリッド距離を使用することはお勧めしません。たとえば、マップを中央近くの横メルカトル図法およびヤードまたはメートルのデカルト座標を使用してください!
事前計算は2番目の方法であり、コンパイラを変更することは3番目のアイデアになる可能性があります(CまたはC++に切り替えます-詳細は https://benchmarksgame.alioth.debian.org/ を参照してください)。
追加の最適化手順には、動的メモリ割り当てを取り除くことや、ノード間の検索に効率的なインデックスを使用することが含まれます(Rツリーとその派生物/代替物を考えてください)。
通常、A *は時間の問題ではなく、メモリの消費量が多すぎます。
ただし、通常は「大通り」の一部であるノードを使用して計算するだけで、通常は小さな路地の上の高速道路を選択する方が便利だと思います。
これはすでに重み関数に使用していると思いますが、優先度キューを使用して、次に移動するノードを次にテストするかどうかを決定すると、より速くなる可能性があります。
また、グラフを低コストエッジの一部であるノードのみに減らし、これらのノードの最も近いノードから開始/終了する方法を見つけることもできます。したがって、最初から「大通り」まで、および「大通り」までの2つのパスがあります。これで、縮小されたグラフの「大通り」の一部である2つのノード間の最適パスを計算できます。