web-dev-qa-db-ja.com

特定のノードを訪問するグラフで最短経路を見つける

約100個のノードと約200個のエッジを持つ無向グラフがあります。 1つのノードには「開始」というラベルが付けられ、もう1つには「終了」というラベルが付けられ、「mustpass」というラベルが付けられたダースが約12個あります。

'start'で始まり、 'end'で終わり、すべての 'mustpass'ノードを(任意の順序で)通過するこのグラフの最短経路を見つける必要があります。

http://3e.org/local/maize-graph.png / http://3e.org/local/maize-graph.dot.txt は問題のグラフ-ペンシルバニア州ランカスターのトウモロコシ迷路を表します)

72
dmd

これを巡回セールスマン問題と比較する他の人は、おそらくあなたの質問を注意深く読んでいないでしょう。 TSPの目的は、頂点を訪れるall最短サイクルを見つけることです(ハミルトニアンサイクル)-every「mustpass」というラベルのノード。

あなたの場合、「mustpass」とラベル付けされたダースが約12個しかないことを考えると、それは12個です!かなり小さい(479001600)の場合、「mustpass」ノードのみのすべての順列を試すことができ、「mustpass」ノードを順番に訪れる「start」から「end」までの最短パスを見ることができます-それは単にそのリスト内の2つの連続するノード間の最短パスを連結します。

言い換えれば、最初に頂点の各ペア間の最短距離を見つけます(ダイクストラのアルゴリズムなどを使用できますが、それらの小さな数(100ノード)で、最も簡単なコーディングでも Floyd-Warshallアルゴリズム =時間内に実行されます)。次に、これをテーブルに入れたら、「mustpass」ノードのすべての順列とそれ以外の順列を試します。

このようなもの:

//Precomputation: Find all pairs shortest paths, e.g. using Floyd-Warshall
n = number of nodes
for i=1 to n: for j=1 to n: d[i][j]=INF
for k=1 to n:
    for i=1 to n:
        for j=1 to n:
            d[i][j] = min(d[i][j], d[i][k] + d[k][j])
//That *really* gives the shortest distance between every pair of nodes! :-)

//Now try all permutations
shortest = INF
for each permutation a[1],a[2],...a[k] of the 'mustpass' nodes:
    shortest = min(shortest, d['start'][a[1]]+d[a[1]][a[2]]+...+d[a[k]]['end'])
print shortest

(もちろん、それは実際のコードではありません。実際のパスが必要な場合、どの順列が最短距離を与えるか、またすべてのペアの最短パスが何であるかを追跡する必要がありますが、アイデアは得られます。)

妥当な言語で最大数秒で実行されます:)
[n個のノードとk個の「mustpass」ノードがある場合、実行時間はO(n3)Floyd-Warshallパートの場合、およびO(k!n)のすべての順列パートの場合、100 ^ 3 +(12!)(100)は、実際に制限のある制約がない限り、実質的にピーナッツです。]

72
ShreevatsaR

Djikstraのアルゴリズム を実行して、すべての重要なノード(開始、終了、必須パス)間の最短パスを検索します。次に、深さ優先のトラバーサルにより、接触する結果のサブグラフを通る最短パスがわかります。すべてのノードが開始する必要があります... mustpasses ...終了

23
Steven A. Lowe

実際、投稿した問題は巡回セールスマンに似ていますが、単純な経路探索の問題に近いと思います。各ノードをすべて訪問する必要があるのではなく、可能な限り短い時間(距離)で特定のノードのセットを訪問するだけです。

この理由は、巡回セールスマンの問題とは異なり、トウモロコシ迷路では、他のノードを通過せずに地図上のある地点から他の地点に直接移動できないためです。

実際に、考慮すべき手法としてA *パスファインディングをお勧めします。これを設定するには、どのノードが他のどのノードに直接アクセスできるか、および特定のノードからの各ホップの「コスト」を決定します。この場合、ノードは比較的狭い間隔に見えるため、各「ホップ」は同じコストになる可能性があります。 A *はこの情報を使用して、任意の2点間の最低コストパスを見つけることができます。ポイントAからポイントBに移動して、その間の約12を訪問する必要があるため、パスファインディングを使用したブルートフォースアプローチでもまったく問題はありません。

考慮すべき代替案です。 驚くほど巡回セールスマンの問題のように見えます。これらは読み進めるのに適した論文ですが、よく見ると複雑すぎていることがわかります。 ^ _ ^これは、以前にこの種のことを扱ったビデオゲームプログラマーの心から来ています。

14
Nicholas Flynt

これは2つの問題です... Steven Loweはこれを指摘しましたが、問題の後半を十分に尊重していませんでした。

最初に、すべての重要なノード(開始、終了、mustpass)間の最短パスを発見する必要があります。これらのパスが検出されると、新しいグラフの各エッジが元のグラフの1つの重要なノードから別のノードへのパスである単純化されたグラフを作成できます。ここで最短経路を見つけるために使用できる多くの経路探索アルゴリズムがあります。

ただし、この新しいグラフを作成すると、出張セールスマンの問題が発生します(ほとんど...出発点に戻る必要はありません)。これに関する上記の投稿はすべて適用されます。

14
Andrew Top

Andrew Topには正しい考えがあります。

1)ジクストラのアルゴリズム2)いくつかのTSPヒューリスティック。

Lin-Kernighanヒューリスティックをお勧めします。これは、すべてのNP完全な問題で最もよく知られているものの1つです。覚えておくべき他の唯一のことは、ステップ2展開されたパスにループがあるので、それらを短絡させます(パスに沿った頂点の度合いを見てください)。

実際、このソリューションが最適と比較してどれだけ良いかはわかりません。おそらく短絡に関係するいくつかの病理学的ケースがあります。結局のところ、この問題はスタイナーツリーのようなLOTに見えます。 http://en.wikipedia.org/wiki/Steiner_tree そして、グラフを縮小してクラスカルを例。

5
Ying Xiao

これはnot TSPの問題であり、NP-hardではありません。元の質問では、パスを通過する必要があるノードを1回だけ訪問する必要がないためです。これにより、すべてのパスパスノード間の最短パスのリストをダイクストラのアルゴリズムを介してコンパイルした後、ブルートフォースに比べて答えがはるかに簡単になります。より良い方法があるかもしれませんが、単純な方法は単純にバイナリツリーを逆方向に処理することです。ノードのリストを想像してください[start、a、b、c、end]。単純な距離を合計する[start-> a-> b-> c-> end]これは、あなたの新しい目標距離です。 [start-> a-> c-> b-> end]を試してみて、それがターゲットとして設定した方がよい場合(そして、それがそのノードのパターンから来たことを思い出してください)。順列を逆にたどります:

  • [開始-> a-> b-> c->終了]
  • [開始-> a-> c-> b->終了]
  • [開始-> b-> a-> c->終了]
  • [開始-> b-> c-> a->終了]
  • [開始-> c-> a-> b->終了]
  • [開始-> c-> b-> a->終了]

それらの1つが最短になります。

(もしあれば、「複数回訪問した」ノードはどこにありますか?最短パス初期化ステップで非表示になります。aとbの間の最短パスにはcまたはエンドポイントさえ含まれます。気にする必要はありません。 )

4
bjorke

ノードとエッジの量が比較的有限であることを考慮すると、おそらくすべての可能なパスを計算し、最短パスを取ることができます。

一般に、これは巡回セールスマン問題として知られ、使用するアルゴリズムに関係なく、非決定的な多項式ランタイムを持ちます。

http://en.wikipedia.org/wiki/Traveling_salesman_problem

3
Rafe

アルゴリズムが約1秒、1日、1週間程度で動作するかどうかを教えていただければ嬉しいです。 。しかし、もしそれがユーザーインターフェースに埋め込まれていて、一日に何度も計算しなければならないなら...別の問題だと思います。

2
Szundi

どこにも言及されていないことの1つは、パス内で同じ頂点に複数回アクセスしても問題ないかどうかです。ここでの回答のほとんどは、同じEdgeを複数回アクセスしても問題ないと想定していますが、質問(パスが同じ頂点に複数回アクセスするべきではありません!)であるということは、not同じ頂点を2回訪問してもかまいません。

そのため、ブルートフォースのアプローチは依然として適用されますが、パスの各サブセットを計算しようとするときに、既に使用されている頂点を削除する必要があります。

0
kirsch

ダースの「必見」ノードでブルートフォースを使用するのはどうですか。 12のノードの可能なすべての組み合わせを十分に簡単にカバーできます。これにより、それらをカバーするために従うことができる最適な回路が残ります。

これで、問題は開始ノードから回路までの最適なルートを見つけることの1つに単純化され、それらをカバーするまで辿り、そこから最後までのルートを見つけます。

最終パスは以下で構成されます。

開始->回路へのパス*->ノードを訪問する必要がある回路->終了へのパス*->終了

このように*でマークしたパスが見つかります

開始ノードから回線上のすべてのポイントに対してA *検索を実行します。これらのそれぞれに対して、回線上の次および前のノードから最後までA *検索を実行します(どちらの方向にも巡回することができるため)最終的には多くの検索パスがあり、最もコストの低いものを選択できます。

検索をキャッシュすることで最適化の余地はたくさんありますが、これは良いソリューションを生み出すと思います。

ただし、最適な解決策を探すことには近づきません。これは、検索内の巡回する必要があるためです。

0
justinhj