web-dev-qa-db-ja.com

ワームホールノードで最短経路を見つける方法は?

example

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

画像では、青のパスをたどる場合、緑のノードから赤のノードに到達するのに8回の移動しか必要ないことがわかります。青いパスは、紫のノードから次のノードへと瞬時に位置を移動します。中央にある2移動分のスペースは、到達するために移動する必要がある2つのワープゾーン間のポイントです。

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

この問題を解決するために、使用できるグラフの周りに複数の紫色の「ワープ」があり、各紫色の点がどこにワープするか、およびそれらがグラフのどこにあるかを正確に把握しているとします。

紫色の縦糸には双方向のものもあれば、双方向でないものもあります。つまり、片方からのみ縦糸に入ることができ、縦糸の後は元に戻らないことがあります。

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

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

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

25
Jeff smith

ほとんどの経路探索アルゴリズムは、グリッドではなくグラフで定義されます。グラフでは、2つの別の遠方のノード間の接続は実際には問題になりません。

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

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

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)
49
amon

座標を持つグリッド上に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  -
_

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

次に Dijkstraのアルゴリズム優先度キュー で使用できます。

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です

5
MT0

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

0
Rene