ワームホールノードで最短パスを見つける方法


25

例

これは、コードでやりたいことの例です。ジャンプポイント検索を使用して、緑のノードから赤のノードに問題なく、またはA *でも簡単に取得できることを知っています。しかし、これをワープでどのように計算しますか。

画像では、青のパスを取るときに緑のノードから赤のノードに到達するのに8回の移動しか必要ないことがわかります。青いパスは、紫色のノードから次のノードにすぐに移動します。中央に2移動するスペースは、移動する必要がある2つのワープゾーンの間のポイントです。

黄色のパスまで半分(大まかに)移動するだけでよいので、青いパスを取る方が明らかに高速ですが、プログラムでこれを行うにはどうすればよいですか?

この問題を解決するために、使用可能なグラフの周りに複数の紫色の「ワープ」があり、各紫色のポイントがワープする場所とグラフ上のどこにあるかを正確に把握します。

紫色のワープには双方向のものとそうでないものがあります。つまり、片側からしかワープに入ることができず、ワープ後に戻ることができない場合があります。

私は解決策について考えましたが、各ワープポイントまでの距離(一方向のポイントを差し引いたもの)、それらのポイント間の差、およびそれらに近いポイントをチェックすることで問題を計算できると結論付けました。

プログラムは、最初のジャンプから歩くのではなく、2番目のワープを取る方がより有益であることを何らかの方法で把握する必要があります。したがって、6つのスポットを移動してからワープし、残りの8ステップを徒歩で移動する代わりに(ワープをまったく使用しない場合よりも高速です)、6つの動きを取り、2つの動きを2番目のワープに移動します。

編集:青のパスは実際には8回ではなく12回の動きをすることに気付きましたが、問題は同じままです。


4
青のパスは12(最後の紫から赤に到達する2つを含む)であってはなりませんか?
BlueRaja-ダニーPflughoeft

5
青は実際には12(7 + 3 + 2)の動きですよね?
ダニエルジュール

おっと、めちゃくちゃだ、みんなありがとう!@ダニエルジュールとブルー
ジェフスミス

距離をモデル化する「正しい」方法は、トポロジを使用して、これをより高次元の表面としてモデル化することです。ここでそのような答えが適切だろうか?
オタク私

回答:


49

ほとんどのパス検索アルゴリズムは、グリッドではなくグラフで定義されます。グラフでは、離れた2つのノード間の接続は実際には問題ではありません。

ただし、ヒューリスティックに注意する必要があります。ワームホールでは、2つのノード間の最小距離はユークリッド距離ではなくなり、距離は三角形の不等式を満たしません。このようなヒューリスティックは、A *には受け入れられません。したがって、A *を簡単に使用することはできません。

もちろん、ダイクストラのような発見的手法を使用しないパス検索アルゴリズムも引き続き機能します。これは幅優先検索に似ており、余分な労力をかけずにワームホールを選択します。ただし、ダイクストラは、優れたヒューリスティックでA *より多くのノードを訪問します。(Dijkstraは、A *と同等heuristic(x) = 0です。)

すべての発信ワームホールをターゲットへのワームホールとして直接扱うヒューリスティックを使用すると、A *が機能すると思います。ヒューリスティックは距離を過小評価する可能性がありますが、決して過大評価してはなりません。つまり、ヒューリスティックは次のようになります。

def wormhole_heuristic(x):
  return min(euclidean_distance(x, g) for g in [goal, wormholes...])

非常に正確なヒューリスティックのために、ワームホールエンドポイントからゴールまたは次のワームホールまでの距離を(再帰的に)追加できます。つまり、事前計算として、2つのノード間の距離がユークリッド距離である、すべてのワームホールとゴールを含む(完全に接続された)サブグラフでパス検索を実行できます。これは、ワームホールの数がグリッド上の到達可能なセルの数よりもはるかに少ない場合に役立ちます。新しいヒューリスティックは次のようになります。

def wormhole_heuristic(x):
  direct = euclidean_distance(x, goal)
  via_wormhole = min(euclidean_distance(x, w) + wormhole_path_distance(w, goal) for w in wormholes)
  return min(direct, via_wormhole)

@Calethがコメントで指摘しているように、これはすべて非常に調整可能であり、最後のワームホール出口と目標の間の距離を追加することにより、ワームホールネットワークを介したフルパス検索を行わずに最初のワームホールヒューリスティックを改善できます。どのワームホール出口が最後に使用されるかわからないため、過大評価してはならないため、目標に最も近い出口を想定する必要があります。

def wormhole_heuristic(x):
  direct = euclidean_distance(x, goal)
  to_next_wormhole = min(euclidean_distance(x, w) for w in wormholes)
  from_last_wormhole = min(euclidean_distance(w.exit, goal) for w in wormholes)
  via_wormhole = to_next_wormhole + from_last_wormhole
  return min(direct, via_wormhole)

10
ダイクストラがちょうどA *であることに注意する価値がありますdijkstra_heuristic(x) = 0
カレス

[* wormholes、goal]の意味がわかりませんが、これを説明してもらえますか?
ジェフスミス

1
「最も近いワームホール出口までのユークリッド距離」はwormhole_path_distance、サブグラフ検索よりも安価な見積もりであり、「すべての出口が目標に向かっている」よりも過小評価されていません。
カレス

3
@Caleth確かに!ここには多くの微調整の可能性があります。たとえば、n = 3のジャンプを先読みすることを決定できます。サブグラフ検索は、すべての非周期ワームホールジャンプのクローズに対応します。n = 1ジャンプを先読みするというあなたの提案は、基本的に追加費用がゼロなので非常にエレガントです:)
amon

1
簡単にするために、ワームホールが1つ(2つのノード)しかない場合、この2点間の等距離線を対称軸として対称平面をコピーすることにより、この平面1ワームホールを2つのミラー平面に変換できます。これで2つのプレーンができました。それらを実際のプレーン(ワームホールは取りません)と架空のプレーン(ワームホールを取ります)と呼びましょう。次に、Z座標を紹介します。この座標は、実平面内のすべてのポイントに対して0になり、仮想平面のすべてのポイントに対してdist(wormhole、point)になります。その後、3次元空間にA *を適用します。
-lilezek

5

座標を持つグリッド上に6つの頂点を持つグラフがあります:

A ( 0,0)
B ( 4,7)
C ( 7,4)
D (10,4)
E (16,2)
F (16,0)

それらの頂点で完全なグラフを生成し、コストを各エッジに割り当てることができます。コストはMAX( ABS( x1 - x2 ), ABS( y1 - y2 ) )標準エッジの場合で、ワームホールのコストは0です。

これにより、コストが(隣接行列として)与えられます:

   A  B  C  D  E  F
- -- -- -- -- -- --
A  -  7  7 10 16 16
B  7  -  0  6 12 12
C  7  0  -  3  9  9
D 10  6  3  -  0  6
E 16 12  9  0  -  2
F 16 12  9  6  2  -

一方向のワープがある場合、グラフ(または隣接行列)にエッジを作成しますが、そのエッジはその方向に進みますが、反対にはなりません。

その後、優先キューダイクストラのアルゴリズムを使用できます

A優先キューから開始し、隣接する各エッジをプッシュします。

形式:(パス:コスト)

queue     = [ (A-B : 7), (A-C : 7), (A-D : 10), (A-E : 16), (A-F : 16) ]

アイテムがキューにプッシュされるとき-各頂点の最小コストを追跡し、既存の最小コストよりもコストが低い場合にのみパスをキューにプッシュします。

min-costs = { A: 0, B: 7, C: 7, D: 10, E: 16, F: 16 }

キューから最初のアイテムを削除し、そのコストが最小コストと一致する場合、そのパスと隣接するエッジによって形成されるすべての複合パスを優先キューにプッシュします(複合パスのコストが既存の最小よりも低い場合):

削除: (A-B : 7)

  • 試してください(A-B-A : 14)-高いコストとして拒否
  • 試す(A-B-C : 7)-同じ費用で拒否
  • 試してください(A-B-D : 13)-高いコストとして拒否
  • 試してください(A-B-E : 19)-高いコストとして拒否
  • 試してください(A-B-F : 19)-高いコストとして拒否

削除する (A-C : 7)

  • 試してください(A-C-A : 14)-高いコストとして拒否
  • 試す(A-C-B : 7)-同じ費用で拒否
  • 試す(A-C-D : 10)-同じ費用で拒否
  • 試す(A-C-E : 16)-同じ費用で拒否
  • 試す(A-C-F : 16)-同じ費用で拒否

削除する (A-D : 10)

  • 試してください(A-D-A : 20)-高いコストとして拒否
  • 試してください(A-D-B : 16)-高いコストとして拒否
  • 試してください(A-D-C : 13)-高いコストとして拒否
  • 試してください(A-D-E : 10)-キューに挿入
  • 試す(A-D-F : 16)-同じ費用で拒否

キューは次のようになります。

queue     = [ (A-D-E : 10), (A-E : 16), (A-F : 16) ]
min-costs = { A: 0, B: 7, C: 7, D: 10, E: 10, F: 16 }

削除する (A-D-E : 10)

  • 試してください(A-D-E-A : 26)-高いコストとして拒否
  • 試してください(A-D-E-B : 22)-高いコストとして拒否
  • 試してください(A-D-E-C : 19)-高いコストとして拒否
  • 試す(A-D-E-D : 10)-同じ費用で拒否
  • 試してください(A-D-E-F : 12)-キューに挿入

次に、キューは次のとおりです。

queue     = [ (A-D-E-F : 12), (A-E : 16), (A-F : 16) ]
min-costs = { A: 0, B: 7, C: 7, D: 10, E: 10, F: 12 }

を削除し(A-D-E-F : 12)、12のコストで宛先ノードに到達したことを確認します。

注:パス(A-B-C-D-E-F)(A-C-D-E-F)および(A-D-E-F)すべての最小コストは12です。


0

すべての頂点を含む行列を設定し、Floyd-Wallenstein-AlgorithmまたはBellman-Ford-Algorithmを使用します。両方とも、すべてのポイント間で可能な限り最短のパスを持つマトリックスになります。その後、マトリックスを反復処理して、2点を結ぶ最短経路を見つけることができます。(問題は非対称TSPと同じです)。

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