互いに最も離れた2つのポイントを見つけるアルゴリズム
-
20-08-2019 - |
質問
Im makingのレースゲームで使用されるアルゴリズムを探しています。マップ/レベル/トラックはランダムに生成されるので、マップを最大限に活用する開始地点と目標地点の2つの場所を見つける必要があります。
- アルゴリズムは2次元空間内で動作する
- 各ポイントから、次のポイントまで4方向にしか移動できません。上、下、左、右
- ポイントはブロックまたは非ブロックのいずれかのみであり、ブロックされていないポイントのみを通過できます
距離の計算に関しては、<!> quot; bird path <!> quot;であってはなりません。より良い言葉の欠如のため。 AとB間の壁(または他のブロック領域)がある場合、AとBの間のパスは長くなります。
どこから始めればいいかわからない、コメントは大歓迎、提案された解決策は擬似コードで優先される。
編集:はい。 gsのコードに目を通した後別のショットを与えた。 pythonの代わりに、今回はC ++で作成しました。それでも、 Dijkstrasアルゴリズムを読んだ後でも、 floodfill および Hosam Alysのソリューションでは、重大な違いを見つけることができません。私のコードはまだ動作しますが、実行中のコードほど高速ではありません。完全なソースは pastie にあります。唯一の興味深い行(私は推測します)は、行78〜118にあるダイクストラバリアント自体です。
ただし、速度はここでは主要な問題ではありません。誰かがアルゴリズムの違いを指摘するのに十分親切であれば、私は本当に助けに感謝します。
- Hosam Alysアルゴリズムでは、彼がすべてのノードではなく境界からスキャンする唯一の違いは何ですか?
- ダイクストラでは、歩いた距離を追跡して上書きしますが、フラッドフィルではなく、それについてですか?
解決
地図が長方形であると仮定すると、すべての境界点をループし、塗りつぶしを開始して開始点から最も遠い点を見つけることができます:
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
になります。)
更新:コメントの通り、マップは迷路のようです(当初考えていた単純な障害物よりも)ので、上記と同じロジックを作成できますが、すべてのポイントをチェックしますマップ内(境界上のポイントのみとは対照的に)。これはO(n)
2 <=>の順にする必要がありますが、F-Wとダイクストラの両方よりも優れています。
注: 洪水充填はこの問題により適しています、すべての頂点は4つの境界のみで直接接続されているため。マップを幅優先で走査すると、比較的迅速に結果が得られます(<=>のみ)。私は、各ポイントが4つの近隣のそれぞれからの塗りつぶしでチェックされる可能性があると仮定しています。したがって、上記の式の係数です。
更新2:このアルゴリズムに関して受け取ったすべての肯定的なフィードバックに感謝しています。 彼のレビュー@ a>。
PSコメントや修正を歓迎します。
他のヒント
Floyd-Warshallについての質問またはホサムアリー:
両方の方法を使用できるテストプログラムを作成しました。それらはファイルです:
すべてのテストケースで、Floyd-Warshallは大幅に遅くなりました。これはおそらく、このアルゴリズムがこれを達成するのに役立つエッジの量が非常に限られているためです。
これらは、フィールドが4連符であり、10のフィールドのうち3つが障害である時間でした。
Size Hosam Aly 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の時間は2次のようであるため、そのアルゴリズムを使用することをお勧めします。 また、Floyd-Warshallによるメモリ消費量はn 2 であり、明らかに必要以上です。 Floyd-Warshallの処理速度が遅い理由をご存知の場合は、コメントを残すか、この投稿を編集してください。
PS:私は長い間CやC ++を書いたことはありませんが、多くの間違いを犯していないことを望みます。
Floyd-Warshallアルゴリズムを推奨する元の投稿を削除しました。 :(
記録用:
- Dijkstraのアルゴリズムの効率的な実装には、O(Elog V)時間かかりますEエッジとV頂点を持つグラフ。
- Hosam Alyの<!> quot; flood fill <!> quot; 幅優先検索、つまりO(V)です。これは、頂点の距離推定値を修正できないダイクストラのアルゴリズムの特殊なケースと考えることができます。
- Floyd-WarshallアルゴリズムはO(V ^ 3 )時間、コーディングは非常に簡単で、密なグラフ(頂点が通常他の多くの頂点に接続されているグラフ)で最も高速です。ただし、OPのタスクには正しい選択ではありません。これには、非常にまばらなグラフが含まれます。
必要なのは、グラフの直径で区切られたエンドポイントのようです。かなり良い計算しやすい近似は、ランダムなポイントを選択し、そこから最も遠いポイントを見つけて、そこから最も遠いポイントを見つけることです。これらの最後の2つのポイントは、最大限に分離する必要があります。
長方形の迷路の場合、これは、2つの塗りつぶしでかなり良い開始点と終了点のペアが得られることを意味します。
Raimund Seidelは、彼の論文の最初のセクションで、行列乗算を使用して、重みのない無向グラフ(これがまさに必要なもの)の全ペア距離行列を計算する簡単な方法を提供します無加重無向グラフの全ペア-最短パス問題について [pdf] 。
入力は隣接行列であり、出力はすべてのペアの最短パス距離行列です。実行時間は、nポイントのO(M(n)* log(n))です。ここで、M(n)は行列乗算アルゴリズムの実行時間です。
このペーパーでは、必要に応じて(同じランタイムで)実際のパスを計算する方法も示しています。
Seidelのアルゴリズムはランタイムがエッジの数に依存しないためクールですが、グラフがスパースであるため実際にはここでは気にしません。ただし、すべてのペアの距離行列が必要な場合は、(n ^ 2よりわずかに悪いランタイムにもかかわらず)これはまだ良い選択かもしれませんし、これは迷路のフラッドフィルよりも実装とデバッグが簡単かもしれません。 / p>
擬似コードは次のとおりです。
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)を返すだけです
この問題に対するdijkstraソリューションのPythonモックアップを完成しました。 コードが少し長くなったので、別の場所に投稿しました: http://refactormycode.com/codes/717-dijkstra-to-find-two-points-furthest-away-from-each-other
設定したサイズでは、1つのノードでアルゴリズムを実行するのに約1.5秒かかります。すべてのノードで実行するには数分かかります。
動作しないようですが、常に左上の隅と右下の隅が最長のパスとして表示されます。 58タイル。もちろん、あなたが障害物を持っていないときは本当です。しかし、ランダムに配置されたものをいくつか追加しても、プログラムはそれを最も長く見つけます。多分まだ本当で、より高度な形状なしではテストするのは難しいでしょう。
しかし、少なくとも私の野心を示すことができるかもしれません。
OK、<!> quot; Hosamのアルゴリズム<!> quot;ノードの事前選択による幅優先検索です。 エッジには重みがないため、ダイクストラのアルゴリズムはここに適用しないでください。
エッジのウェイトが異なる場合、多くのオプション(代替ルート)を開いたままにして、すべてのステップでそれらをチェックする必要があるため、違いは重要です。これにより、アルゴリズムがより複雑になります。 幅優先検索では、各ノードへの最短パスを見つけることができるように、すべてのエッジを一度探索するだけです。つまり、エッジを見つけた順に探索します。
したがって、基本的に違いは、ダイクストラが「バックトラック」し、最短ルートを追跡していることを確認するために前に探索したエッジを調べる必要があることです。
また、迷路では、外側の境界上のポイントが最長ルートの一部であるとは限りません。 たとえば、巨大なスパイラルの形をした迷路があり、外側の端が中央に戻っている場合、1つはスパイラルの中心にあり、もう1つはスパイラルの端にあります。真ん中に!
したがって、これを行う良い方法は、すべてのポイントから幅優先検索を使用し、検索後に開始点を削除することです(開始点と開始点のすべてのルートを既に知っています)。 最初に幅の複雑さはO(n)です。ここで、n = | V | + | E |です。 Vのすべてのノードに対してこれを1回行うため、O(n ^ 2)になります。
説明は、迷路ルーティングの問題のように聞こえます。 リーアルゴリズムをご覧ください。 VLSI設計における配置配線の問題に関する書籍が役立つ場合があります- Sherwaniの<!> quot ; VLSI Physical Design Automation <!> quot; のアルゴリズムは優れており、 SaitとYoussefによるVLSIフィジカルデザインオートメーションは便利です(Googleバージョンでは安価です...)
オブジェクト(ポイント)が頻繁に移動しない場合、O(n ^ 3)時間よりもはるかに短い時間でこのような計算を実行できます。
必要なのは、スペースを大きなグリッドに分割し、グリッド間距離を事前に計算することです。次に、最も離れたグリッドを占めるポイントペアを選択することは、単純なテーブル検索の問題です。平均的なケースでは、ごく一部のオブジェクトのみをペアワイズでチェックする必要があります。
このソリューションは、距離メトリックが連続している場合に機能します。したがって、たとえば、迷路のようにマップに多くの障壁がある場合、この方法は失敗する可能性があります。