ダイクストラのアルゴリズムを使用した負の重み


113

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

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

ウェブサイトから:

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

ダイクストラの次の実装を使用すると、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)
}

おかげで、

メイア


一般的に負のエッジウェイトを使用したパスファインディングは非常に困難です。どのようなルートを見つけたとしても、どこかに任意の大きな負のエッジウェイトがある任意の長いルートが常に存在する可能性があります。それが完全なNPであるならば、私は驚かないでしょう。
Nick Johnson

4
この疑問を持つ他の誰にとっても、負の重みサイクルを持たないグラフGIVENで最短経路を見つけることができます。上記のアルゴリズムは、リラックスが実際に成功したときにRelax関数が「true」の値を返した場合に機能します。この場合、隣接する頂点「v」が存在しない場合は優先キューにエンキューされ、存在する場合は更新されます。これは、訪問されたノードがリラックスし続けると、再び優先キューに追加できることを意味します。
goelakash

回答:


202

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

グラフ図

例のように、エッジが左から右に向けられていると仮定します。

アルゴリズムは次のように機能します。

  1. 最初に、に設定d(A)zero、他の距離をに設定しますinfinity
  2. その後、ノードを展開するA設定、d(B)1d(C)zero、とd(D)します99
  3. 次に、Cネットを変更せずに展開します。
  4. 次に、を展開しますBが、効果はありません。
  5. 最後に、あなたが拡大D変化する、d(B)-201

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


35
あなたの優れた答えに追加するには:貪欲なアルゴリズムであるダイクストラが近視眼的な選択の理由です。
11

4
技術的には、このグラフのすべてのパスには、負のサイクルA、D、B、Aのおかげで負の無限大のコストがかかることを指摘しておきます。
ネイト

2
@ Nate-明確にするために、グラフのすべてのエッジは左から右に向けられています。私の高品質なASCIIアートで矢印をレンダリングするのは少し難しいことでした。:-)
templatetypedef

2
これまでに負のエッジを持つグラフを見たことがない人にとって、このグラフの有用な解釈は、エッジの重みがあなたに支払う通行料を与える有料道路のネットワークであることがわかります。-300道路はクレイジーな後方有料道路で、代わりに$ 300を提供します。
D Coetzee

3
@ SchwitJanwityanujit-これがダイクストラのアルゴリズムの仕組みです。アルゴリズムはパスを探索しませんが、代わりにノードを処理することによって機能します。各ノードは1回だけ処理されるため、Bノードを処理してその距離が1になるとすぐに、ノードBに再度アクセスしたり、距離の更新を試みたりすることはありません。
templatetypedef

25

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

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

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

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


2.なぜ私が「ベルマンフォードに似ている」と思うのか、指数関数的ではない(これはベルマンフォードよりも悪い)と考える理由が明確ではありません。具体的なアルゴリズムと証明が頭にありますか?
Gassa 2014

3
1 .:キュー基準が空になると停止する前述の停止基準でまったく同じダイクストラの実装を使用できるため(元の質問の疑似コードを参照)、動作が異なる場合でも、最短パスのダイクストラアルゴリズムです。ノードを数回解決する(ラベル修正)。
infty10000101 2014

1
へ2 .:あくまで推測でしたので、削除します。探索する必要のある指数関数的に多くのパスがあるので、私はあなたが指数関数的な時間で正しいと思います。
infty10000101 2014

11

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

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

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


また、負の重みのエッジなしで頂点を変更する必要はありません。これにより、パスコストはエッジの繰り返しによってのみ増加するという仮定を完全に破ります
prusswan

この仮定は、Sを使用することを可能にするものであり、頂点がSに入ると、それが再び変更されることはありません。
2011

あなたの最後の声明は間違っています。ポストされたアルゴリズムは、負のエッジのないグラフで機能する場合、時間の複雑さO(E + VlogV)を持ちます。すでにノードを訪問したことを確認する必要はありません。ノードが訪問されたという事実は、緩和手順によってキューにもう一度追加されないことを保証するためです。
ピクサー

7

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


貪欲アルゴリズムは、名前が示すように、常にその時点で最善であると思われる選択肢となります。特定の点で最適化(最大化または最小化)が必要な目的関数があるとします。貪欲アルゴリズムは、各ステップで貪欲な選択を行い、目的関数が確実に最適化されるようにします。貪欲アルゴリズムは、最適解を計算するための唯一のワンショットがあるように、それは戻っていないとの決定を逆転させないし。


4

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


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

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

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

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

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

以下は、Algorithm(第4版)からの優れたリファレンスです。これには、前述のバージョン2および3のJava実装が含まれています。

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

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


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


1

BとCの間を行き来するとどうなるか考えてみてください...

(グラフが指示されていない場合にのみ該当)

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


これは不可能です、グラフが指示されます。
amit

@アミット:良い点、私はそれを逃した。問題を再検討する時間
プロスワン

1

「2)ウェイトが負のグラフの最短パスにダイクスラのアルゴリズムを使用できますか?1つのアイデアは、最小ウェイト値を計算し、正の値(最小ウェイト値の絶対値に等しい)をすべてのウェイトに追加して、ダイクスラのアルゴリズムを実行することです。変更されたグラフの場合。このアルゴリズムは機能しますか?」

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


0

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

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

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