Search code examples
algorithmdijkstrashortest-pathgraph-algorithm

Negative weights using Dijkstra's Algorithm


I am trying to understand why Dijkstra's algorithm will not work with negative weights. Reading an example on Shortest Paths, I am trying to figure out the following scenario:

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

From the website:

Assuming the edges are all directed from left to right, If we start with A, Dijkstra's algorithm will choose the edge (A,x) minimizing d(A,A)+length(edge), namely (A,B). It then sets d(A,B)=2 and chooses another edge (y,C) minimizing d(A,y)+d(y,C); the only choice is (A,C) and it sets d(A,C)=3. But it never finds the shortest path from A to B, via C, with total length 1.

I can not understand why using the following implementation of Dijkstra, d[B] will not be updated to 1 (When the algorithm reaches vertex C, it will run a relax on B, see that the d[B] equals to 2, and therefore update its value to 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)
}

Thanks,

Meir


Solution

  • The algorithm you have suggested will indeed find the shortest path in this graph, but not all graphs in general. For example, consider this graph:

    A directed graph with four nodes, A, B, C, and D. Node A has an edge to B of cost 1, an edge to C of cost 0, and an edge to D of cost 99. Node B has an edge to cost 1 to node C. Node D has an edge of cost -300 to node B.

    Let's trace through the execution of your algorithm.

    1. First, you set d(A) to 0 and the other distances to ∞.
    2. You then expand out node A, setting d(B) to 1, d(C) to 0, and d(D) to 99.
    3. Next, you expand out C, with no net changes.
    4. You then expand out B, which has no effect.
    5. Finally, you expand D, which changes d(B) to -201.

    Notice that at the end of this, though, that d(C) is still 0, even though the shortest path to C has length -200. This means that your algorithm doesn't compute the correct distances to all the nodes. Moreover, even if you were to store back pointers saying how to get from each node to the start node A, you'd end taking the wrong path back from C to A.

    The reason for this is that Dijkstra's algorithm (and your algorithm) are greedy algorithms that assume that once they've computed the distance to some node, the distance found must be the optimal distance. In other words, the algorithm doesn't allow itself to take the distance of a node it has expanded and change what that distance is. In the case of negative edges, your algorithm, and Dijkstra's algorithm, can be "surprised" by seeing a negative-cost edge that would indeed decrease the cost of the best path from the starting node to some other node.