为什么Dijkstra的算法不能用于负权重边缘?

时间:2012-10-31 13:41:00

标签: algorithm shortest-path dijkstra

有人可以告诉我为什么Dijkstra的单源最短路径算法假设边缘必须是非负的。

我说的只是边缘而不是负重量周期。

9 个答案:

答案 0 :(得分:145)

回想一下,在Dijkstra的算法中,一旦顶点被标记为“已关闭”(并且在开集之外) - 算法找到了最短的路径,并且永远不必开发此节点再次 - 它假定开发到此路径的路径是最短的。

但是负重 - 可能不是真的。例如:

       A
      / \
     /   \
    /     \
   5       2
  /         \
  B--(-10)-->C

V={A,B,C} ; E = {(A,C,2), (A,B,5), (B,C,-10)}
来自A的Dijkstra将首先开发C,后来无法找到A->B->C


编辑更深入的解释:

请注意,这很重要,因为在每个放松步骤中,算法假设“闭合”节点的“成本”确实是最小的,因此下次选择的节点也是最小的。

它的想法是:如果我们有一个打开的顶点使其成本最小 - 通过向任何顶点添加任何正数 - 最小值永远不会改变。
没有正数约束 - 上述假设不正确。

由于我们“知道”每个“闭合”的顶点都是最小的 - 我们可以安全地进行放松步骤 - 而不是“回头看”。如果我们确实需要“回顾” - Bellman-Ford提供类似递归的(DP)解决方案。

答案 1 :(得分:27)

考虑下面显示的图,源为Vertex A.首先尝试自己运行Dijkstra算法。

enter image description here

当我在解释中提到Dijkstra的算法时,我将讨论下面实现的Dijkstra算法,

Dijkstra’s algorithm

因此,开始最初分配给每个顶点的从源到顶点的距离),

initialization

我们首先在 Q = [A,B,C] 中提取具有最小值的顶点,即A,之后 Q = [B,C] 。注意A对B和C有一个有向边,它们都在Q中,因此我们更新了这两个值,

first iteration

现在我们将C提取为(2 <5),现在 Q = [B] 。请注意,C未连接,因此line16循环不会运行。

second iteration

最后我们提取B,之后Q is Phi。注意B有一个C的有向边,但C不存在于Q中,因此我们再次不在line16中输入for循环,

3rd?

所以我们最终将距离视为

no change guys

注意这是错误的,因为当你走a to b to c时,从A到C的最短距离是5 + -10 = -5。

因此,对于此图,Dijkstra的算法错误地计算从A到C的距离。

这是因为Dijkstra的算法不会尝试找到已从Q 中提取的顶点的较短路径。

line16循环正在做的是取顶点 u 并说&#34;嘿,看起来我们可以从 v 通过来源,(alt或替代)距离是否比我们得到的当前 dist [v] 更好?如果允许,请更新 dist [v] &#34;

请注意,在line16中,他们会检查所有邻居 v (即 u到v 的有向边缘) u 仍然在Q 。在line14中,他们会从Q中移除访问过的笔记。因此,如果 x u 的访问过的邻居,则路径source to u to x 甚至不会将视为从源到 v 的可能更短的方式。

在上面的示例中,C是B的访问邻居,因此未考虑路径A to B to C,使当前最短路径A to C保持不变。

如果边缘权重都是正数,这实际上是有用的,因为那样我们就不会浪费时间考虑不能的路径短。

所以我说在运行此算法时如果在 y 之前从Q中提取 x ,则无法找到路径 - not possible短。让我用一个例子来解释这个,

由于 y 刚刚被提取并且 x 已经被提取出来,所以 dist [y]&gt; dist [x] 因为否则 y 会在 x 之前被提取出来。 (line 13最小距离)

因为我们已经假设边缘权重是正的,即长度(x,y)> 0 。因此,通过 y 的替代距离(alt)总是肯定更大,即 dist [y] + length(x,y)&gt; DIST [X] 即可。因此,即使 y 被视为 x 的路径, dist [x] 的值也不会更新,因此我们得出结论有意义的是只考虑仍在Q中的 y 的邻居(注意line16中的评论)

但这个事情取决于我们对正边长的假设,如果长度(u,v)<0 那么取决于我们可能取代 dist [x]的负边缘程度在line18进行比较后,

  

因此,如果在所有顶点 v 之前删除 x ,我们所做的任何 dist [x] 计算都将不正确 - 这样 x v 的邻居,负边连接它们 - 被移除。

因为每个 v 顶点都是潜在的第二个顶点&#34;更好&#34;从源到 x 的路径,由Dijkstra算法丢弃。

所以在我上面给出的例子中,错误是因为在删除B之前删除了C.虽然那个C是B的邻居,但有一个负边缘!

为了澄清,B和C是A的邻居。 B有一个邻居C,C没有邻居。 length(a,b)是顶点a和b之间的边长。

答案 2 :(得分:18)

Dijkstra的算法假定路径只能变得“更重”,所以如果你有一条从A到B的路径,权重为3,路径从A到C的权重为3,那你就无法添加边缘,从A到B到C,重量小于3。

这个假设使得算法比必须考虑负权重的算法更快。

答案 3 :(得分:5)

Dijkstra算法的正确性:

我们在算法的任何步骤都有2组顶点。集合A由我们计算最短路径的顶点组成。集B由剩余的顶点组成。

归纳假设:在每一步我们都会假设所有先前的迭代都是正确的。

归纳步骤:当我们将一个顶点V添加到集合A并将距离设置为dist [V]时,我们必须证明该距离是最佳的。如果这不是最优的,则必须有一些到顶点V的其他路径,其长度较短。

假设这个其他路径经过某个顶点X.

现在,由于dist [V]&lt; = dist [X],因此到V的任何其他路径将至少为dist [V]长度,除非图形具有负边长。

因此,对于dijkstra的算法,边缘权重必须是非负的。

答案 4 :(得分:4)

在下图中尝试Dijkstra的算法,假设A是源节点,看看发生了什么:

Graph

答案 5 :(得分:1)

回想一下,在Dijkstra的算法中,将某个顶点标记为“封闭”(且不在开放集中)-它假定源自该顶点的任何节点都将导致更大的距离,因此,算法找到了到达它的最短路径,并且不再需要再次开发此节点,但是在负权重的情况下就不成立了。

答案 6 :(得分:1)

您可以对不包含负循环的负边缘使用dijkstra算法,但是必须允许顶点可以多次访问,而该版本将失去快速的时间复杂性。

在那种情况下,实际上我已经看到最好使用SPFA algorithm,它具有正常的队列并且可以处理负边缘。

答案 7 :(得分:1)

Dijkstra的算法 假设所有边都为正加权,并且这种假设比其他算法(考虑到可能性)的运行速度更快的负边缘(例如,贝尔曼·福特算法的复杂度为O(V ^ 3))。

在以下情况下,如果A是源顶点,此算法将无法给出正确的结果:

enter image description here

此外,即使存在负边缘,Dijkstra的算法 有时也可以给出正确的解决方案 。以下是这种情况的示例:

enter image description here

它将永远不会检测到负循环 ,并且 总是会产生结果 ,如果负权重循环可从源到达,因为在这种情况下,图中从源顶点不存在最短路径。

答案 8 :(得分:0)

到目前为止,其他答案很好地说明了为什么Dijkstra的算法无法处理路径上的负权重。

但是问题本身可能是基于对路径权重的错误理解。如果通常在寻路算法中允许对路径施加负权重,那么您将获得永不停止的永久循环。

考虑一下:

A  <- 5 ->  B  <- (-1) ->  C <- 5 -> D

A和D之间的最佳路径是什么?

任何寻路算法都必须在B和C之间连续循环,因为这样做会减少总路径的权重。因此,允许负连接权重将使任何pathfindig算法无济于事,除非您将每个连接限制为仅使用一次。