Dijkstra有负边缘。不了解这些示例,它们根据CLRS伪代码工作

时间:2017-02-24 21:12:34

标签: algorithm graph computer-science dijkstra

编辑2: 似乎这不是来自CLRS(我认为这是因为它遵循了在此Algos和DS中提供给我们的相同格式的CLRS代码课程)。 尽管如此,在本课程中,我们将此代码视为" Dijkstra"

我看了Why doesn't Dijkstra's algorithm work for negative weight edges?Negative weights using Dijkstra's Algorithm(第二个是我认为的OP算法特有的。)

从CLRS查看伪代码("算法简介"),我不明白为什么Dijkstra不会处理具有负边缘的图形示例。

在伪代码(下面)中,我们Insert节点返回到堆上,如果它们的新距离比它们之前的距离短,那么在我看来距离最终会更新到正确的距离。

例如:

enter image description here

这里的主张是(A,C)将设置为1并且永远不会更新到正确的距离-2。

但CLRS的伪代码表示我们首先将C和B分别放在Heap上,距离分别为1和2;然后我们弹出C,看不到外边;然后我们弹出B,看边缘(B,C),看到Dist[C] > Dist[B] + w(B,C),将Dist[C]更新为-2,将C放回堆上,看不到外边缘,我们就是完成。 所以它运作良好。

此问题的第一个答案中的示例相同:Negative weights using Dijkstra's Algorithm

答案的作者声称到C的距离不会更新到-200,但根据这个伪代码不正确,因为我们会把B放回堆上,然后计算出正确的最短值到C的距离。

(来自CLRS的伪代码)

Dijkstra(G(V, E, ω), s ∈ V )
for v in V do 
    dist[v] ← ∞ 
    prev[v] ← nil
end for
dist[s] = 0 
H←{(s,0)} 
while H̸=∅ do
    v ← DeleteMin(H) 
    for (v, w) ∈ E do
        if dist[w] > dist[v] + ω(v, w) then 
            dist[w] ← dist[v] + ω(v, w) 
            prev[w] ← v
            Insert((w, dist[w]), H)
        end if 
    end for
end while

编辑:我知道我们假设一旦节点从堆中弹出,就找到了最短的距离;但是,似乎(根据CLRS)我们确实将节点放回堆上,距离比先前计算的要短,所以最后算法运行时我们应该得到正确的最短距离。

1 个答案:

答案 0 :(得分:2)

该实现在技术上不是Dijkstra的算法,由Dijkstra here描述(找不到任何更好的链接):他所讨论的集合A是已知最小路径的节点。因此,一旦您向此集添加节点,它就会被修复。您知道它的最小路径,它不再参与算法的其余部分。它还讨论了传输节点,因此它们不能同时存在两个集合。

这符合维基百科的伪代码:

 1  function Dijkstra(Graph, source):
 2
 3      create vertex set Q
 4
 5      for each vertex v in Graph:             // Initialization
 6          dist[v] ← INFINITY                  // Unknown distance from source to v
 7          prev[v] ← UNDEFINED                 // Previous node in optimal path from source
 8          add v to Q                          // All nodes initially in Q (unvisited nodes)
 9
10      dist[source] ← 0                        // Distance from source to source
11      
12      while Q is not empty:
13          u ← vertex in Q with min dist[u]    // Node with the least distance will be selected first
14          remove u from Q 
15          
16          for each neighbor v of u:           // where v is still in Q.
17              alt ← dist[u] + length(u, v)
18              if alt < dist[v]:               // A shorter path to v has been found
19                  dist[v] ← alt 
20                  prev[v] ← u 
21
22      return dist[], prev[]

它的堆伪代码也是。

然而,请注意维基百科在回答这个问题时也说:

  

不是在初始化阶段用所有节点填充优先级队列,而是可以将其初始化为仅包含源; 然后,在if alt&lt; dist [v] block,如果节点尚未插入队列,则必须插入(而不是执行decrease_priority操作)。[3]:198

执行此操作仍会导致在某些情况下使用负值边缘重新插入节点,例如second linked question的已接受答案中给出的示例图。

所以似乎有些作者会产生这种混淆。在这种情况下,他们应该清楚地说明这个实现是否有负边缘,或者它不是一个正确的Dijkstra实现。

我猜原始论文可能会被解释为有点模糊。 Dijkstra在任何地方都没有提到任何负面或正边缘,除了在A集合中无法更新节点的任何替代解释之外,他也没有说清楚。我不知道他自己是否在随后的任何作品或演讲中进一步澄清了事情,或者剩下的只是其他人的解释问题。

所以从我的观点来看,你可以说它也是一个有效的Dijkstra。

至于为什么你可以用这种方式实现它:因为如果我们只有正边缘,它在实践中可能不会慢,并且因为它更快地编写而不必执行额外的检查或不那么标准的堆操作