ダイクストラのアルゴリズムが減少キーを使用するのはなぜですか?


93

ダイクストラのアルゴリズムは私に教えられました

while pqueue is not empty:
    distance, node = pqueue.delete_min()
    if node has been visited:
        continue
    else:
        mark node as visited
    if node == target:
        break
    for each neighbor of node:
         pqueue.insert(distance + distance_to_neighbor, neighbor)

しかし、私はアルゴリズムに関していくつかの読書をしてきました、そして私が見る多くのバージョンは挿入ではなく減少キーを使用しています。

これはなぜですか、2つのアプローチの違いは何ですか?


14
Downvoter-この質問の何が問題になっているのか説明していただけますか?私はそれは完全に公平だと思います、そして多くの人々(私を含む)は最初に減少キーバージョンよりもむしろダイクストラのOPバージョンに紹介されました。
templatetypedef

回答:


68

ノードを再挿入するのではなく減少キーを使用する理由は、優先キュー内のノードの数を少なくして、優先キューのデキューの総数を少なくし、各優先キューのコストを低く抑えるためです。

新しい優先度でノードを優先度キューに再挿入するダイクストラのアルゴリズムの実装では、グラフ内のm個のエッジのそれぞれについて、1つのノードが優先度キューに追加されます。つまり、優先キューにはm個のエンキュー操作とm個のデキュー操作があり、合計実行時間はO(m T e + m T d)になります。ここで、T eは優先キューにエンキューするのに必要な時間で、T dは優先キューからデキューするのに必要な時間。

減少キーをサポートするダイクストラのアルゴリズムの実装では、ノードを保持する優先度キューは、nノードで始まり、アルゴリズムの各ステップで1つのノードを削除します。これは、ヒープデキューの総数がnであることを意味します。各ノードには、それにつながる各エッジに対して1回ずつ減少キーが呼び出される可能性があるため、実行される減少キーの総数は最大でmです。これにより、(n T e + n T d + m T k)のランタイムが得られます。ここで、T kは減少キーを呼び出すのに必要な時間です。

では、これはランタイムにどのような影響を与えるのでしょうか?これは、使用する優先キューによって異なります。以下は、さまざまな優先キューと、さまざまなダイクストラのアルゴリズム実装の全体的な実行時間を示す簡単な表です。

Queue          |  T_e   |  T_d   |  T_k   | w/o Dec-Key |   w/Dec-Key
---------------+--------+--------+--------+-------------+---------------
Binary Heap    |O(log N)|O(log N)|O(log N)| O(M log N)  |   O(M log N)
Binomial Heap  |O(log N)|O(log N)|O(log N)| O(M log N)  |   O(M log N)
Fibonacci Heap |  O(1)  |O(log N)|  O(1)  | O(M log N)  | O(M + N log N)

ご覧のように、ほとんどのタイプの優先度キューでは、漸近ランタイムに実際の違いはなく、Decrease-Keyバージョンの方がはるかに優れているとは言えません。ただし、優先度キューのフィボナッチヒープ実装を使用する場合、実際に減少キーを使用すると、ダイクストラのアルゴリズムが漸近的に効率的になります。

つまり、reduce-keyと適切な優先度キューを使用すると、エンキューとデキューを続けた場合に可能なことを超えて、ダイクストラの漸近ランタイムがドロップされる可能性があります。

この点に加えて、Gabowの最短パスアルゴリズムなどのいくつかのより高度なアルゴリズムは、サブルーチンとしてダイクストラのアルゴリズムを使用し、減少キーの実装に大きく依存しています。有効な距離の範囲が事前にわかっている場合は、その事実に基づいて非常に効率的な優先キューを作成できるという事実を使用します。

お役に立てれば!


1
+1:ヒープの説明を忘れていました。挿入バージョンのヒープにはエッジごとにノード(つまりO(m))があるため、アクセス時間はO(log m)にすべきではなく、合計実行時間はO(m log m)になります。つまり、通常のグラフではmはn ^ 2以下であるため、これはO(m log n)に減少しますが、2つのノードが異なる重みの複数のエッジによって結合される可能性があるグラフでは、mは無制限です(もちろん、2つのノード間の最小パスは最小限のエッジのみを使用し、これを通常のグラフに削減すると主張できますが、ノンスにとっては興味深いです)。
ランプオン2012

2
@ rampion-ポイントはありますが、一般に、アルゴリズムを起動する前に平行エッジが削減されていると想定されているため、O(log n)とO(log m)はあまり重要ではないと思います。通常、mはO(n ^ 2)と見なされます。
templatetypedef

27

2007年に、減少キーバージョンと挿入バージョンを使用した場合の実行時間の違いを調査した論文がありました。http://www.cs.utexas.edu/users/shaikat/papers/TR-07-54.pdfを参照してください

彼らの基本的な結論は、ほとんどのグラフで減少キーを使用しないことでした。特にスパースグラフの場合、非減少キーは減少キーバージョンよりも大幅に高速です。詳細については、論文を参照してください。


7
cs.sunysb.edu/~rezaul/papers/TR-07-54.pdfは、その論文のリンクです。
eleanora 2013年

警告:リンクされた論文にバグがあります。ページ16、関数B.2:であるif k < d[u]必要がありますif k <= d[u]
Xeverous、

2

ダイクストラを実装するには2つの方法があります。1つは減少キーをサポートするヒープを使用し、もう1つはそれをサポートしないヒープを使用します。

どちらも一般的に有効ですが、通常は後者の方が適しています。以下では、「m」を使用してエッジの数を示し、「n」を使用してグラフの頂点の数を示します。

可能な限り最悪の場合の複雑さが必要な場合は、減少キーをサポートするフィボナッチヒープを使用します。素晴らしいO(m + nlogn)が得られます。

平均的なケースが気になる場合は、バイナリヒープも使用できます。O(m + nlog(m / n)logn)になります。証明はこちら、99/100ページ。グラフが密(m >> n)の場合、このグラフと前のグラフの両方がO(m)になる傾向があります。

実際のグラフで実行するとどうなるかを知りたい場合は、Mark Meketonの回答で示唆されているように、このペーパーをチェックできます。

実験結果が示すのは、「単純な」ヒープがほとんどの場合に最良の結果を与えるということです。

実際、減少キーを使用する実装の中で、ダイクストラは、フィボナッチヒープを使用する場合よりも、単純なバイナリヒープまたはペアリングヒープを使用した方がパフォーマンスが向上します。これは、フィボナッチヒープに含まれる定数係数が大きくなり、実際の減少キー操作の数が、最悪の場合の予測よりもはるかに小さくなる傾向があるためです。

同様の理由で、減少キー操作をサポートする必要のないヒープは、定数がさらに少なく、実際に最高のパフォーマンスを発揮します。特にグラフが疎な場合。

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