web-dev-qa-db-ja.com

互いに最も離れた2つのポイントを見つけるアルゴリズム

レーシングゲームで使用するアルゴリズムを探しています。マップ/レベル/トラックはランダムに生成されるため、マップを最大限に活用するスタートとゴールの2つの場所を見つける必要があります。

  • アルゴリズムは、2次元空間内で機能することです
  • 各ポイントから、次のポイントまで4方向にしか移動できません。上下左右
  • ポイントはブロックまたは非ブロックのいずれかのみが可能で、ブロックされていないポイントのみがトラバースできます

距離の計算に関しては、より良い言葉がないために「鳥の道」であってはなりません。 AとBの間に壁(または他のブロッキング領域)がある場合は、AとBの間のパスを長くする必要があります。

どこから始めればよいかわからないので、コメントは大歓迎であり、提案された解決策が擬似コードで好まれます。

編集:そうです。調べた後 gsのコード 私はそれに別のショットを与えました。今回はPythonの代わりにC++で書きました。しかし、それでも、 ダイクストラアルゴリズムフラッドフィルHosam Alysソリューション を読んだ後でも、重大な違いを見つけることができません。私のコードはまだ機能しますが、実行するように見えるほど速くはありません。完全なソースは pastie にあります。唯一の興味深い行(私は推測する)は、78〜118行目のダイクストラバリアント自体です。

しかし、ここでは速度は主な問題ではありません。誰かがアルゴリズムの違いを指摘してくれるほど親切にしてくれたら、本当に助けていただければ幸いです。

  • Hosam Alysアルゴリズムでは、すべてのノードではなく、境界からスキャンする唯一の違いはありますか?
  • ダイクストラでは、歩行距離を追跡して上書きしますが、洪水ではありませんが、それだけですか?
28
Mizipzor

マップが長方形であると仮定すると、すべての境界点をループし、塗りつぶしを開始して、開始点から最も遠い点を見つけることができます。

_bestSolution = { start: (0,0), end: (0,0), distance: 0 };
for each point p on the border
    flood-fill all points in the map to find the most distant point
    if newDistance > bestSolution.distance
        bestSolution = { p, distantP, newDistance }
    end if
end loop
_

これはO(n^2)にあると思います。誤解しない限り、それは_(L+W) * 2 * (L*W) * 4_です。ここで、Lはマップの長さ、Wはマップの幅、_(L+W) * 2_はマップ上の境界点の数を表します。周囲長、_(L*W)_はポイントの数、_4_は、塗りつぶしがポイントに最大4回(すべての方向から)アクセスすることを前提としています。 nはポイント数に相当するため、これは_(L + W) * 8 * n_に相当し、_O(n_よりも優れているはずです。2_)_。 (マップが正方形の場合、順序は_O(16n_になります1.5_)_。)

更新:コメントによると、マップは迷路のようなものなので(最初に考えていたような単純な障害物があるマップよりも)、上記と同じロジックですが、マップ内のすべてのポイントをチェックします(境界上のポイントのみではありません)。これは_O(4n_の順序である必要があります2_)_、これはF-Wとダイクストラの両方よりも優れています。

注:塗りつぶし すべての頂点が4つの境界のみを介して直接接続されているため、この問題に適しています。マップの幅優先探索は、比較的迅速に結果を生成できます(O(n)だけで)。私は、各ポイントがその4つの隣接するそれぞれからのフラッドフィルでチェックされる可能性があると想定しています。したがって、上記の式の係数です。

更新2:このアルゴリズムに関して受け取ったすべての肯定的なフィードバックに感謝します。 @Georgに感謝します 彼のレビュー

P.S.コメントや訂正は大歓迎です。

10
Hosam Aly

Floyd-Warshallまたは Hosam Alyの単純なアルゴリズムに関する質問をフォローアップします。

両方の方法を使用できるテストプログラムを作成しました。これらはファイルです:

すべてのテストケースで、Floyd-Warshallは大幅に遅くなりました。これはおそらく、このアルゴリズムがこれを達成するのに役立つエッジの量が非常に限られているためです。

これらは、フィールドが4つ組であり、10フィールドのうち3フィールドが障害となった時代でした。

サイズHosamAly Floyd-Warshall 
(10x10)0m0.002s 0m0.007s 
(20x20)0m0.009s 0m0.307s 
(40x40)0m0.166s 0m22.052s 
(80x80)0m2.753s-
(160x160)0m48.028s-

Hosam Alyの時間は二次のように見えるので、そのアルゴリズムを使用することをお勧めします。また、Floyd-Warshallによるメモリ消費量はnです。2、明らかに必要以上のもの。 Floyd-Warshallが非常に遅い理由がわからない場合は、コメントを残すか、この投稿を編集してください。

PS:私は長い間CまたはC++を書いていません、私はあまり多くの間違いをしなかったと思います。

9
Georg Schölly

グラフの直径 で区切られた終点が必要なようです。かなり良くて計算しやすい近似は、ランダムな点を選び、そこから最も遠い点を見つけて、そこから最も遠い点を見つけることです。これらの最後の2つのポイントは、最大限に分離されている必要があります。

長方形の迷路の場合、これは、2回の塗りつぶしで、開始点と終了点のかなり良いペアが得られることを意味します。

5
Boojum

Floyd-Warshallアルゴリズムを推奨する元の投稿を削除しました。 :(

gsは現実的なベンチマークを実行しました そして何を推測しますか、F-Wは、典型的なマップサイズに対するHosam Alyの「フラッドフィル」アルゴリズムよりも大幅に遅いです!したがって、F-Wはクールなアルゴリズムであり、密グラフのダイクストラよりもはるかに高速ですが、非常にまばらなグラフ(各頂点には4つのエッジしかない)が関係するOPの問題にはもうお勧めできません。

記録のために:

  • ダイクストラのアルゴリズム の効率的な実装には、E個のエッジとV個の頂点を持つグラフのO(Elog V)時間がかかります。
  • Hosam Alyの「フラッドフィル」は 幅優先探索 、つまりO(V)です。これは、ダイクストラのアルゴリズムの特殊なケースと考えることができます。この場合、頂点の距離推定を修正することはできません。
  • Floyd-Warshallアルゴリズム はO(V ^ 3)時間かかり、コーディングが非常に簡単で、密グラフ(頂点が通常他の多くの頂点に接続されているグラフ)では依然として最速です。しかし、それはOPのタスクにとって正しい選択ではありません。これには非常にまばらなグラフが含まれます。
5
j_random_hacker

Raimund Seidelは、彼の論文の最初のセクションで、行列乗算を使用して、重み付けされていない無向グラフ(まさにあなたが望むもの)ですべてのペアの距離行列を計算する簡単な方法を示しています All-Pairs-Shortest-について)重み付けされていない無向グラフの経路問題[pdf]

入力は隣接行列であり、出力はすべてのペアの最短経路距離行列です。実行時間は、nポイントのO(M(n)* log(n))です。ここで、M(n)は、行列乗算アルゴリズムの実行時間です。

このホワイトペーパーでは、これも必要な場合に、(同じランタイムで)実際のパスを計算する方法についても説明します。

Seidelのアルゴリズムは、実行時間がエッジの数に依存しないためクールですが、グラフがまばらであるため、実際にはここでは気にしません。ただし、すべてのペアの距離行列が必要な場合は、これは(n ^ 2よりもわずかに悪い実行時間にもかかわらず)依然として適切な選択である可能性があり、迷路での塗りつぶしよりも実装とデバッグが簡単な場合もあります。

擬似コードは次のとおりです。

Let A be the nxn (0-1) adjacency matrix of an unweighted, undirected graph, G

All-Pairs-Distances(A)
    Z = A * A
    Let B be the nxn matrix s.t. b_ij = 1 iff i != j and (a_ij = 1 or z_ij > 0)
    if b_ij = 1 for all i != j return 2B - A //base case
    T = All-Pairs-Distances(B)
    X = T * A
    Let D be the nxn matrix s.t. d_ij = 2t_ij if x_ij >= t_ij * degree(j), otherwise d_ij = 2t_ij - 1
    return D

距離が最大のポイントのペアを取得するには、argmax_ij(d_ij)を返します。

3
Imran

わかりました。「Hosamのアルゴリズム」は、ノードを事前に選択した幅優先探索です。エッジには重みがないため、ここではダイクストラのアルゴリズムを適用しないでください。

エッジの重みが異なる場合は、多くのオプション(代替ルート)を開いたままにして、すべてのステップでそれらをチェックする必要があるため、違いは非常に重要です。これにより、アルゴリズムがより複雑になります。幅優先探索では、各ノードへの最短パスを見つけることを保証する方法で、すべてのエッジを1回探索するだけです。つまり、見つけた順序でエッジを探索します。

したがって、基本的に違いは、Dijkstraが「バックトラック」し、以前に探索したエッジを調べて、最短ルートをたどっていることを確認する必要があるのに対し、幅優先探索では常に最短ルートをたどっていることを認識していることです。

また、迷路では、外側の境界のポイントが最長ルートの一部であるとは限りません。たとえば、巨大なスパイラルの形をした迷路があり、外側の端が中央に戻っている場合、スパイラルの中心とスパイラルの端に2つのポイントを設定できます。途中で!

したがって、これを行うための良い方法は、すべてのポイントから幅優先探索を使用することですが、検索後に開始ポイントを削除します(そこへのすべてのルートとそこからのすべてのルートはすでにわかっています)。最初の幅優先の複雑さはO(n)です。ここで、n = | V | + | E |です。これをVのノードごとに1回実行するため、O(n ^ 2)になります。

1
Rutger Prins

問題のdijkstraソリューションのpythonモックアップを終了しました。コードが少し長くなったので、別の場所に投稿しました: http://refactormycode.com/codes/717-dijkstra -to-find-two-points-furthest-away-from-each-other

私が設定したサイズでは、1つのノードに対してアルゴリズムを実行するのに約1.5秒かかります。すべてのノードで実行するには数分かかります。

ただし、機能していないようです。常に左上隅と右下隅が最長のパスとして表示されます。 58タイル。もちろん、障害物がない場合はどちらが当てはまりますか。しかし、ランダムに配置されたものをいくつか追加しても、プログラムはそれが最も長いものを見つけます。たぶんそれはまだ真実であり、より高度な形状なしでテストするのは難しいです。

しかし、おそらくそれは少なくとも私の野心を示すことができます。

1
Mizipzor

オブジェクト(ポイント)が頻繁に移動しない場合は、O(n ^ 3)時間よりもはるかに短い時間でそのような計算を実行できます。

必要なのは、スペースを大きなグリッドに分割し、グリッド間の距離を事前に計算することです。次に、最も離れたグリッドを占めるポイントペアを選択することは、単純なテーブルルックアップの問題です。平均的な場合、ペアごとにチェックする必要があるのは、オブジェクトの小さなセットだけです。

このソリューションは、距離メトリックが連続している場合に機能します。したがって、たとえば、マップに(迷路のように)多くの障壁がある場合、この方法は失敗する可能性があります。

0
Boris Gorelik

あなたの説明は私には 迷路ルーティング 問題のように聞こえます。 Lee Algorithm を確認してください。 VLSI設計における配置配線の問題に関する本が役立つかもしれません- Sherwaniの「VLSI物理設計自動化のアルゴリズム」 良いです、そしてあなたは見つけるかもしれません SaitとYoussefによるVLSI物理設計自動化 便利(そしてGoogleバージョンではもっと安い...)

0
Yuval F