125

ダイクストラのアルゴリズムが負の重みで機能しない理由を理解しようとしています。最短経路の例を読んで、私は次のシナリオを理解しようとしています。

    2
A-------B
 \     /
3 \   / -2
   \ /
    C

ウェブサイトから:

エッジがすべて左から右に向けられていると仮定すると、Aから始める場合、ダイクストラのアルゴリズムは、d(A、A)+ length(edge)、つまり(A、B)を最小化するエッジ(A、x)を選択します。次に、d(A、B)= 2を設定し、d(A、y)+ d(y、C)を最小化する別のエッジ(y、C)を選択します。唯一の選択肢は(A、C)で、d(A、C)=3に設定されます。ただし、全長1で、Cを経由してAからBへの最短経路を見つけることはありません。

次のダイクストラの実装を使用すると、d [B]がに更新されない理由がわかりません1(アルゴリズムが頂点Cに到達すると、Bでリラックスが実行され、d [B]がに等しいことを確認して、2更新します)その値を1)に。

Dijkstra(G, w, s)  {
   Initialize-Single-Source(G, s)
   S ← Ø
   Q ← V[G]//priority queue by d[v]
   while Q ≠ Ø do
      u ← Extract-Min(Q)
      S ← S U {u}
      for each vertex v in Adj[u] do
         Relax(u, v)
}

Initialize-Single-Source(G, s) {
   for each vertex v  V(G)
      d[v] ← ∞
      π[v] ← NIL
   d[s] ← 0
}

Relax(u, v) {
   //update only if we found a strictly shortest path
   if d[v] > d[u] + w(u,v) 
      d[v] ← d[u] + w(u,v)
      π[v] ← u
      Update(Q, v)
}

ありがとう、

メイア

4

9 に答える 9

222

あなたが提案したアルゴリズムは確かにこのグラフで最短経路を見つけますが、一般的にすべてのグラフではありません。たとえば、次のグラフについて考えてみます。

4つのノード、A、B、C、およびDを持つ有向グラフ。ノードAには、コスト1のBへのエッジ、コスト0のCへのエッジ、およびコスト99のDへのエッジがあります。ノードBには、ノードCへのコスト1。ノードDには、ノードBへのコスト-300のエッジがあります。

アルゴリズムの実行を追跡してみましょう。

  1. まず、d(A)を0に設定し、他の距離を∞に設定します。
  2. 次に、ノードAを展開し、d(B)を1に設定し、d(C)を0に設定し、d(D)を99に設定します。
  3. 次に、正味の変更なしでCを展開します。
  4. 次に、 Bを展開しますが、これは効果がありません。
  5. 最後に、Dを展開します。これにより、d(B)が-201に変更されます。

ただし、この最後では、 Cへの最短経路の長さが-200であっても、d(C)はまだ0であることに注意してください。これは、アルゴリズムがすべてのノードまでの正しい距離を計算しないことを意味します。さらに、各ノードから開始ノードAに到達する方法を示すバックポインターを格納したとしても、 CからAに戻る間違ったパスをたどってしまうことになります。

この理由は、ダイクストラのアルゴリズム(およびあなたのアルゴリズム)が欲張りアルゴリズムであり、ノードまでの距離を計算すると、見つかった距離が最適な距離でなければならないと想定しているためです。つまり、アルゴリズムでは、拡張したノードの距離を取得して、その距離を変更することはできません。負のエッジの場合、アルゴリズムとダイクストラのアルゴリズムは、開始ノードから他のノードへの最適なパスのコストを実際に削減する負のコストのエッジを確認することで「驚かされる」可能性があります。

お役に立てれば!

于 2011-07-23T08:53:12.013 に答える
31

グラフに負のサイクルがない場合、つまり、合計された重みがゼロ未満のサイクルがある場合、ダイクストラは負の重みに対しても機能することに注意してください。

もちろん、templatetypedefによって作成された例では、負のサイクルがなくても、実際にはサイクルさえないのに、なぜダイクストラが失敗するのかと疑問に思うかもしれません。これは、ターゲットノードに到達するとすぐにアルゴリズムを保持する別の停止基準を使用しているためです(またはすべてのノードが一度解決された場合、彼はそれを正確に指定しませんでした)。負の重みのないグラフでは、これは正常に機能します。

優先度付きキュー(ヒープ)が空になったときにアルゴリズムを停止する代替停止基準を使用している場合(この停止基準は質問でも使用されました)、ダイクストラは負の重みを持つグラフでも正しい距離を見つけますが、負のサイクル。

ただし、この場合、負のサイクルのないグラフのダイクストラの漸近時間限界は失われます。これは、負の重みが原因でより良い距離が見つかった場合に、以前に解決されたノードをヒープに再挿入できるためです。このプロパティは、ラベル修正と呼ばれます。

于 2014-08-06T10:54:05.217 に答える
14

TL; DR:答えは実装によって異なります。投稿した擬似コードの場合、負の重みで機能します。


ダイクストラのアルゴリズムの変種

重要なのは、ダイクストラのアルゴリズムの実装には3種類ありますが、この質問のすべての回答は、これらのバリアント間の違いを無視しています。

  1. ネストされたforループを使用して頂点を緩和します。これは、ダイクストラのアルゴリズムを実装する最も簡単な方法です。時間計算量はO(V ^ 2)です。
  2. 優先度付きキュー/ヒープベースの実装+再入可能は許可されていません。再入可能とは、緩和された頂点を優先度付きキューに再度プッシュして、後で再び緩和できることを意味します
  3. 優先度キュー/ヒープベースの実装+再入可能。

バージョン1と2は、負の重みを持つグラフでは失敗します(このような場合に正しい答えが得られた場合、それは単なる偶然です)が、バージョン3は引き続き機能します。

元の問題で投稿された擬似コードは上記のバージョン3であるため、負の重みで機能します。

これは、 Algorithm(4th edition)からの優れたリファレンスであり、次のように述べています(そして、前述のバージョン2および3のJava実装が含まれています)。

Q.ダイクストラのアルゴリズムは負の重みで機能しますか?

A.はい、いいえ。頂点を優先キューに複数回エンキューできるかどうかに応じて、ダイクストラのアルゴリズムと呼ばれる2つの最短経路アルゴリズムがあります。重みが負でない場合、2つのバージョンは一致します(頂点が複数回エンキューされることはないため)。DijkstraSP.javaに実装されているバージョン(頂点を複数回エンキューできるようにする)は、負のエッジの重みが存在する場合は正しいですが(ただし、負のサイクルはありません)、最悪の場合、実行時間は指数関数的になります。(エッジ加重有向グラフに負の加重のエッジがある場合、DijkstraSP.javaは例外をスローするため、プログラマーはこの指数関数的な動作に驚かないことに注意してください。)頂点をエンキューできないようにDijkstraSP.javaを変更した場合複数回(たとえば、marked []配列を使用して、緩和された頂点をマークする)、


実装の詳細とバージョン3とベルマンフォードアルゴリズムとの接続については、zhihuからのこの回答を参照してください。それは私の答えでもあります(ただし中国語で)。現在、英語に翻訳する時間がありません。誰かがこれを行い、stackoverflowでこの回答を編集できれば本当に感謝しています。

于 2019-04-09T15:51:53.587 に答える
11

アルゴリズムのどこにもSを使用していません(変更する以外)。ダイクストラの考え方は、頂点がSにあると、二度と変更されることはありません。この場合、BがSの中に入ると、Cを介して再び到達することはありません。

この事実により、O(E + VlogV)の複雑さが保証されます[そうでない場合は、エッジを2回以上繰り返し、頂点を1回以上繰り返すことになります]

言い換えれば、あなたが投稿したアルゴリズムは、ダイクストラのアルゴリズムによって約束されているように、O(E + VlogV)にない可能性があります。

于 2011-07-23T08:42:48.813 に答える
7

ダイクストラは貪欲なアプローチであるため、このループで頂点が訪問済みとしてマークされると、後で到達するためのコストが低い別のパスがある場合でも、頂点が再評価されることはありません。そして、そのような問題は、グラフに負のエッジが存在する場合にのみ発生する可能性があります。


欲張りアルゴリズムは、その名前が示すように、常にその時点で最良と思われる選択を行います。特定のポイントで最適化(最大化または最小化)する必要のある目的関数があると仮定します。欲張りアルゴリズムは、目的関数が最適化されるように、各ステップで欲張りな選択を行います。欲張りアルゴリズムは、最適な解を計算するためのショットが1つしかないため、二度と戻って決定が逆転することはありません。

于 2014-10-25T09:36:52.470 に答える
1

BとCの間を行ったり来たりするとどうなるか考えてみてください...出来上がり

(グラフが指示されていない場合にのみ関連します)

編集:問題は、AC *を使用したパスは、負の重みエッジが存在する場合にのみABよりも優れている可能性があるという事実に関係していると思います。したがって、ACを追跡する場所は、非負の重みのエッジは、ACに進んだ後にBに到達することを選択した後は、ABよりも優れたパスを見つけることは不可能です。

于 2011-07-23T08:31:05.590 に答える
1

"2)負の重みを持つグラフの最短経路にDijksraのアルゴリズムを使用できますか?1つのアイデアは、最小の重み値を計算し、すべての重みに正の値(最小の重み値の絶対値に等しい)を追加して、Dijksraのアルゴリズムを実行することです。修正されたグラフの場合。このアルゴリズムは機能しますか?」

すべての最短パスが同じ長さでない限り、これは絶対に機能しません。たとえば、2つのエッジの長さの最短パスが与えられ、各エッジに絶対値を追加した後、パスの合計コストは2*|最大負の重み|だけ増加します。一方、長さが3エッジの別のパスでは、パスのコストが3*|最大負の重み|増加します。したがって、すべての個別のパスは異なる量だけ増加します。

于 2017-05-22T17:20:18.837 に答える
0

負のサイクルを含まない負のエッジでダイクストラのアルゴリズムを使用できますが、頂点に複数回アクセスできるようにする必要があり、そのバージョンでは時間計算量が速くなります。

その場合、実際には、通常のキューを持ち、ネガティブエッジを処理できるSPFAアルゴリズムを使用する方がよいことがわかりました。

于 2020-03-29T10:41:21.567 に答える
0

この問題をよりよく理解するために、すべてのコメントを組み合わせます。

ダイクストラのアルゴリズムを使用するには、次の2つの方法があります。

  1. ソースからの最小距離をすでに見つけているノードをマークする(最短パスがすでに見つかっているノードを再訪しないため、より高速なアルゴリズム)

  2. ソースからの最小距離をすでに見つけているノードをマークしない(上記より少し遅い)

ここで、負の重みを含む最短経路を見つけることができるようにノードにマークを付けないとどうなるかという疑問が生じます。

答えは簡単です。グラフに負の重みしかない場合を考えてみましょう。

ネガを含むグラフ。 サイクル)。

ここで、ノード0(ソース)から開始する場合、次のような手順があります(ここではノードをマークしていません)。

  1. 0-> 0は0、0-> 1はinf、0->2はinfとして最初に

  2. 0-> 1 as -1

  3. 0-> 2 as -5

  4. 0-> 0 as -8(ノードを緩和していないため)

  5. 0-> 1 as-9..など

このループは永久に続くため、ダイクストラのアルゴリズムは、負の重みの場合(すべての場合を考慮して)最小距離を見つけることができません。

そのため、ベルマンフォードアルゴは、負のサイクルの場合にループを停止するため、負の重みの場合に最短経路を見つけるために使用されます。

于 2021-07-26T20:51:55.460 に答える