为什么Dijkstra的算法不能处理负负边缘?


Answers:


175

回想一下,在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)的解决方案。


5
抱歉,我没有收到任何错误。首先A->B将5和A->C2。然后B->C-5。因此,的价值C-5与Bellman-ford相同。这怎么不能给出正确的答案?
Anirban Nag'tintinmj'2014年

5
@tintinmj首先,Dijkstra将“关闭” A值为0的节点。然后,它将在值最小的节点上查找B为5且C为2。最小值为C,因此它将C以值2 关闭并且永远不会向后看稍后B关闭,它无法修改的值C,因为它已“关闭”。
2014年

4
@amit Dijkstra的算法如何找不到路径A -> B -> C?它将首先将C的距离更新为2,然后将的距离更新B为5。假设您的图形中没有的传出边C,则访问时我们什么也不做C(其距离仍然为2)。然后,我们访问D的相邻节点,唯一的相邻节点是C,其新距离为-5。请注意,在Dijkstra的算法中,我们还跟踪从中到达(并更新)节点C的父节点,然后从进行跟踪,您将获得父节点B,然后A得到,结果是正确的。我想念什么?
nbro

12
@amit您的推理问题(我认为),并且我已经看到其他人(奇怪),您认为该算法不会重新考虑已经确定了最短距离(并且我们已经完成)的节点,但这是不正确的,这就是为什么要执行“松弛”步骤...我们遍历图的所有节点,并且对于每个节点,我们遍历相邻节点,即使相邻节点中的任何一个可能例如,已经从我们的最低优先级队列中删除了。
nbro

10
@amit检查这个问题的答案,这个例子实际上是有意义的:stackoverflow.com/a/6799344/3924118
nbro

37

考虑下面显示的图形,其源为VertexA。首先尝试自己运行Dijkstra的算法。

在此处输入图片说明

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

Dijkstra的算法

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

初始化

我们首先提取Q = [A,B,C]中具有最小值(即A)的顶点,此后Q = [B,C]。请注意,A与B和C有直边,而且它们都在Q中,因此我们更新了这两个值,

第一次迭代

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

第二次迭代

最后,我们提取B,然后Q是披披。注意,B指向C,但是Q中不存在C,因此我们也不再在中输入for循环line16

第三?

所以我们最终得出距离为

不变的家伙

请注意,这是怎么回事,因为从A到C的最短距离是5 + -10 = -5 从a到b到c

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

之所以会发生这种情况,是因为Dijkstra的算法未尝试找到到达已从Q提取的顶点的较短路径。

什么是line16循环做的是顶点ü,并说:“嘿看起来我们可以去v通过从源头ü,是(ALT或替代)距离任何优于现行DIST [V]我们得到了什么?如果是这样,您更新dist [v]

注意,在line16他们检查所有邻居v(即有向边存在从u到v)的ü它们仍然在Q中。在line14他们从问:所以删除走访笔记,如果X是拜访邻居ü,路径源到你到x没有考虑从源头上可能更短的方式v

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

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

因此,我说运行此算法时,如果x是从y之前的Q中提取的,则不可能找到路径- 不可能较短。让我用一个例子来解释一下

由于y刚刚被提取并且x在其自身之前被提取,因此dist [y]> dist [x],因为否则y将在x之前被提取。(line 13最短距离优先)

正如我们已经假定的,边缘权重为正,即length(x,y)> 0。因此,通过y的替代距离(alt)始终要确保更大,即dist [y] + length(x,y)> dist [x]。因此,即使y被视为x的路径,dist [x]的值也不会被更新,因此我们得出结论,仅考虑仍在Q中的y的邻居是有意义的(请注意中的注释)line16

但是,这取决于我们对边的正长度的假设,如果length(u,v)<0,则取决于边的负值,我们可以在进行比较后替换dist [x]line18

因此,如果在删除所有顶点v之前删除x,则我们进行的所有dist [x]计算都将是错误的-从而使xv的相邻点,并且将它们连接到负边。

因为这些v个顶点中的每一个都是从源到x的潜在“更好”路径上的倒数第二个顶点,因此Dijkstra算法将其丢弃。

因此,在我上面给出的示例中,错误是因为在删除B之前先删除C。而那个C却是B的邻居,而且边缘不好!

需要说明的是,B和C是A的邻居。B有一个邻居C,而C没有邻居。length(a,b)是顶点a和b之间的边长。


2
就像您说的那样,解决此问题的更好方法是在每次比较之后使用heapq.heappush方法。我们将更新的距离推回队列。在这种情况下,Dijkstra可以负负重。我尝试了一下,结果显示为0,5,-5
nosense

1
“甚至不考虑x到u的路径源”;你是说你是x的来源吗?
slmatrix

1
@slmatrix感谢您捕捉到这一点,是的,我的意思是从源到u到x的路径,因为x是u的邻居。
Aditya P

23

Dijkstra的算法假定路径只能变得“更重”,因此,如果您具有从A到B的权重为3的路径,以及从A到C的权重为3的路径,则无法添加边和从A到B到C的重量小于3。

这种假设使算法比必须考虑负权数的算法更快。


8

Dijkstra算法的正确性:

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

归纳假设:在每个步骤中,我们将假定所有先前的迭代都是正确的。

归纳步骤:将顶点V添加到集合A并将距离设置为dist [V]时,必须证明该距离是最佳的。如果这不是最佳选择,那么到顶点V的路径必须较短。

假设这条其他路径通过某个顶点X。

现在,由于dist [V] <= dist [X],因此到V的任何其他路径都将是至少dist [V]长度,除非该图具有负边长。

因此,为了使dijkstra的算法起作用,边缘权重必须为非负数。


6

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

图形


6
抱歉,我没有收到任何错误。首先A->B1A->C意志100。然后B->D2。然后C->D-4900。因此,的价值D-4900与Bellman-ford相同。这怎么不能给出正确的答案?
2014年

9
@tintinmj如果您有一个从D出来的边,它将在D的距离减小之前被访问,因此在D以后不再更新。这样肯定会导致错误。如果在扫描出线边缘之后已经将D的2视为最终距离,则即使此图也会导致错误。
Christian Schnorr 2014年

@ tb-很抱歉,问了这么长时间后,我在这里走对了吗?首先A->B1A->C100。然后B进行探索并设置B->D2。然后探索D,因为当前它具有返回源的最短路径?我是否正确地说,如果B->D100C将首先进行探索?我了解人们在您以外的所有其他例子。
Pejman Poh

根据我的理解@PejmanPoh,如果B-> D为100,则由于将使用的HeapStructure中A-> C较高,因此提取最小值将首先返回A-> C,这意味着下一个找到的最短路径将是该路径到C,之后从C-> D且权重为-5000的路径将是显而易见的选择,这使我们得出结论,最短的路径将是从A-> C-> D,我很确定这将是是正常的行为。所以有时候,当我们有负向周期,我们仍然可能取得的最短路径正确的价值,但绝对不是往常一样,这是一个例子,我们不会..
T.Dimitrov

1

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


0

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

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

考虑一下:

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

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

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


0

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.