使用 Dijkstra 算法的负权重
我试图理解为什么 Dijkstra 算法不适用于负权重。阅读 最短路径 上的示例,我试图找出以下场景:
2
A-------B
\ /
3 \ / -2
\ /
C
来自网站:
假设边缘都是从左到右,如果我们开始 对于 A,Dijkstra 算法将选择边缘 (A,x) 最小化 d(A,A)+长度(边),即(A,B)。然后设置 d(A,B)=2 并选择 另一条边 (y,C) 最小化 d(A,y)+d(y,C);唯一的选择是(A,C) 并且设置 d(A,C)=3。但它永远找不到从 A 到 A 的最短路径 B,经C,总长度1。
我不明白为什么使用以下Dijkstra实现,d[B]不会更新为1
(当算法到达顶点C时,它将运行a 在 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)
}
谢谢,
梅尔
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
您建议的算法确实会找到该图中的最短路径,但并不是一般情况下的所有图。例如,考虑以下图:
让我们通过追踪执行你的算法。
请注意,尽管到 C 的最短路径长度为 -200,但在最后,d(C) 仍然为 0。这意味着您的算法无法计算到所有节点的正确距离。此外,即使您要存储说明如何从每个节点到起始节点 A 的反向指针,您最终也会采取从 C 返回到 C 的错误路径。 em>A。
原因是 Dijkstra 算法(以及您的算法)是贪婪算法,它假设一旦计算出到某个节点的距离,找到的距离必须是最佳距离。换句话说,该算法不允许自己获取已扩展的节点的距离并更改该距离。在负边的情况下,您的算法和 Dijkstra 算法可能会因为看到负成本边而感到“惊讶”,因为负成本边确实会降低从起始节点到其他节点的最佳路径的成本。
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:
Let's trace through the execution of your algorithm.
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.
请注意,如果图表没有负循环,即总权重小于零的循环,则 Dijkstra 即使对于负权重也适用。
当然,有人可能会问,为什么在 templatetypedef Dijkstra 制作的示例中,即使没有负循环,甚至没有循环,也会失败。这是因为他使用了另一个停止标准,一旦到达目标节点(或者所有节点都已解决一次,他没有具体指定),算法就保持不变。在没有负权重的图中,这效果很好。
如果使用替代停止标准,当优先级队列(堆)空时停止算法(问题中也使用了该停止标准),那么即使对于具有负权重但没有权重的图,dijkstra 也会找到正确的距离负循环。
然而,在这种情况下,没有负环的图的 dijkstra 渐近时间界限会丢失。这是因为当由于负权重而找到更好的距离时,可以将先前固定的节点重新插入到堆中。此属性称为标签校正。
Note, that Dijkstra works even for negative weights, if the Graph has no negative cycles, i.e. cycles whose summed up weight is less than zero.
Of course one might ask, why in the example made by templatetypedef Dijkstra fails even though there are no negative cycles, infact not even cycles. That is because he is using another stop criterion, that holds the algorithm as soon as the target node is reached (or all nodes have been settled once, he did not specify that exactly). In a graph without negative weights this works fine.
If one is using the alternative stop criterion, which stops the algorithm when the priority-queue (heap) runs empty (this stop criterion was also used in the question), then dijkstra will find the correct distance even for graphs with negative weights but without negative cycles.
However, in this case, the asymptotic time bound of dijkstra for graphs without negative cycles is lost. This is because a previously settled node can be reinserted into the heap when a better distance is found due to negative weights. This property is called label correcting.
TL;DR:答案取决于您的实施。对于您发布的伪代码,它适用于负权重。
Dijkstra算法的变体
关键是Dijkstra算法有3种实现,但是这个问题下的所有答案都忽略了这些变体之间的差异。
for
循环来松弛顶点。这是实现 Dijkstra 算法的最简单方法。时间复杂度为O(V^2)。版本 1 和版本 1 2 在权重为负的图上会失败(如果在这种情况下得到正确答案,那只是巧合),但版本 3 仍然有效。
原始问题下发布的伪代码是上面的版本 3,因此它适用于负权重。
这是来自 算法(第 4 版) 的一个很好的参考,它说(并包含 java 实现我上面提到的版本 2 和 3):
更多实现细节以及版本3与Bellman-Ford算法的联系,请参见这个答案知乎。这也是我的答案(但是是中文的)。目前我没有时间将其翻译成英文。如果有人可以做到这一点并在 stackoverflow 上编辑这个答案,我真的很感激。
TL;DR: The answer depends on your implementation. For the pseudo code you posted, it works with negative weights.
Variants of Dijkstra's Algorithm
The key is there are 3 kinds of implementation of Dijkstra's algorithm, but all the answers under this question ignore the differences among these variants.
for
-loop to relax vertices. This is the easiest way to implement Dijkstra's algorithm. The time complexity is O(V^2).Version 1 & 2 will fail on graphs with negative weights (if you get the correct answer in such cases, it is just a coincidence), but version 3 still works.
The pseudo code posted under the original problem is the version 3 above, so it works with negative weights.
Here is a good reference from Algorithm (4th edition), which says (and contains the java implementation of version 2 & 3 I mentioned above):
For more implementation details and the connection of version 3 with Bellman-Ford algorithm, please see this answer from zhihu. It is also my answer (but in Chinese). Currently I don't have time to translate it into English. I really appreciate it if someone could do this and edit this answer on stackoverflow.
您没有在算法中的任何地方使用 S(除了修改它)。 dijkstra的思想是一旦一个顶点在S上,它就不会再被修改。在这种情况下,一旦 B 位于 S 内部,您将不会通过 C 再次到达它。
这一事实确保了 O(E+VlogV) 的复杂性 [否则,您将多次重复边,并且多次重复顶点
]换句话说,您发布的算法可能不在 O(E+VlogV) 中,正如 dijkstra 算法所承诺的那样。
you did not use S anywhere in your algorithm (besides modifying it). the idea of dijkstra is once a vertex is on S, it will not be modified ever again. in this case, once B is inside S, you will not reach it again via C.
this fact ensures the complexity of O(E+VlogV) [otherwise, you will repeat edges more then once, and vertices more then once]
in other words, the algorithm you posted, might not be in O(E+VlogV), as promised by dijkstra's algorithm.
由于 Dijkstra 是一种贪婪方法,一旦一个顶点被标记为在此循环中已访问,即使稍后有另一条成本更低的路径可以到达它,也永远不会再次对其进行重新评估。并且只有当图中存在负边时才会发生这种问题。
贪婪算法,顾名思义,总是做出当时看来最好的选择。假设您有一个需要优化的目标函数(在给定点最大化或最小化)。 贪婪算法在每一步都做出贪婪的选择,以确保目标函数得到优化。 贪婪算法只有一次机会来计算最佳解决方案,因此它永远不会返回并推翻决策。
Since Dijkstra is a Greedy approach, once a vertice is marked as visited for this loop, it would never be reevaluated again even if there's another path with less cost to reach it later on. And such issue could only happen when negative edges exist in the graph.
A greedy algorithm, as the name suggests, always makes the choice that seems to be the best at that moment. Assume that you have an objective function that needs to be optimized (either maximized or minimized) at a given point. A Greedy algorithm makes greedy choices at each step to ensure that the objective function is optimized. The Greedy algorithm has only one shot to compute the optimal solution so that it never goes back and reverses the decision.
考虑一下如果您在 B 和 C 之间来回移动会发生什么...瞧
(仅当图形无向时才相关)
编辑:
我认为问题与以下事实有关:AC* 的路径只能在存在负权重边缘的情况下比 AB 更好,因此在假设非负权重的情况下,在 AC 之后去哪里并不重要一旦你选择走AC后到达B,就不可能找到比AB更好的路径。
Consider what happens if you go back and forth between B and C...voila
(relevant only if the graph is not directed)
Edited:
I believe the problem has to do with the fact that the path with AC* can only be better than AB with the existence of negative weight edges, so it doesn't matter where you go after AC, with the assumption of non-negative weight edges it is impossible to find a path better than AB once you chose to reach B after going AC.
“2)我们能否使用 Dijksra 算法来计算具有负权重的图的最短路径 - 一种想法可以是,计算最小权重值,为所有权重添加一个正值(等于最小权重值的绝对值)并运行 Dijksra 算法对于修改后的图,这个算法会起作用吗?”
除非所有最短路径都具有相同的长度,否则这绝对行不通。例如,给定两条边长度的最短路径,并且在为每条边添加绝对值后,总路径成本增加 2 * |最大负权重|。另一方面,另一条路径的长度为三个边,因此路径成本增加了 3 * |最大负权重|。因此,所有不同的路径都会增加不同的量。
"2) Can we use Dijksra’s algorithm for shortest paths for graphs with negative weights – one idea can be, calculate the minimum weight value, add a positive value (equal to absolute value of minimum weight value) to all weights and run the Dijksra’s algorithm for the modified graph. Will this algorithm work?"
This absolutely doesn't work unless all shortest paths have same length. For example given a shortest path of length two edges, and after adding absolute value to each edge, then the total path cost is increased by 2 * |max negative weight|. On the other hand another path of length three edges, so the path cost is increased by 3 * |max negative weight|. Hence, all distinct paths are increased by different amounts.
您可以使用带有不包括负循环的负边的迪杰斯特拉算法,但您必须允许一个顶点可以被多次访问,并且该版本将失去其快速时间复杂度。
在这种情况下,实际上我发现最好使用具有正常队列并且可以处理负边。
You can use dijkstra's algorithm with negative edges not including negative cycle, but you must allow a vertex can be visited multiple times and that version will lose it's fast time complexity.
In that case practically I've seen it's better to use SPFA algorithm which have normal queue and can handle negative edges.
我将结合所有评论,以便更好地理解这个问题。
有两种使用 Dijkstra 算法的方法:
标记已经找到距源最小距离的节点(更快的算法,因为我们不会重新访问已经找到最短路径的节点)
不标记已经找到与源的最小距离的节点(比上面慢一点)
现在问题来了,如果我们不标记节点,这样我们就可以找到包含那些包含负数的最短路径权重?
答案很简单。考虑图中只有负权重的情况:
)
现在,如果您从节点 0(源),您的步骤如下(这里我没有标记节点):
0->0 as 0, 0->1 as inf , 0- >2为inf开头
0->1 为 -1
0->2 为 -5
0->0 as -8(因为我们没有放松节点)
0->1 为 -9 .. 依此类推
这个循环将永远持续下去,因此 Dijkstra 算法无法找到负权重情况下的最小距离(考虑所有情况)。
这就是为什么使用贝尔曼福特算法来寻找负权重情况下的最短路径,因为它会在负循环的情况下停止循环。
I will be just combining all of the comments to give a better understanding of this problem.
There can be two ways of using Dijkstra's algorithms :
Marking the nodes that have already found the minimum distance from the source (faster algorithm since we won't be revisiting nodes whose shortest path have been found already)
Not marking the nodes that have already found the minimum distance from the source (a bit slower than the above)
Now the question arises, what if we don't mark the nodes so that we can find shortest path including those containing negative weights ?
The answer is simple. Consider a case when you only have negative weights in the graph:
)
Now, if you start from the node 0 (Source), you will have steps as (here I'm not marking the nodes):
0->0 as 0, 0->1 as inf , 0->2 as inf in the beginning
0->1 as -1
0->2 as -5
0->0 as -8 (since we are not relaxing nodes)
0->1 as -9 .. and so on
This loop will go on forever, therefore Dijkstra's algorithm fails to find the minimum distance in case of negative weights (considering all the cases).
That's why Bellman Ford Algo is used to find the shortest path in case of negative weights, as it will stop the loop in case of negative cycle.