【问题标题】:Can someone walk me through why this implementation of dijkstra's algo doesn't work for negative weights? [duplicate]有人可以告诉我为什么这个 dijkstra 算法的实现不适用于负权重吗? [复制]
【发布时间】:2016-01-12 20:27:04
【问题描述】:

我查看了 dijkstra 算法 found here 的实现,但它似乎适用于负权重。

具体来说,relax 方法会更新顶点在优先级队列中的距离,如果它最初不在队列中,则将其重新插入。

而且由于没有任何检查来确保我们不会重新插入已知顶点,所以这个实现会不会更像是一个贝尔曼福特算法,我们不断插入顶点来访问和放松,直到用完减少距离的边缘?

例如:

在下图中以A为源运行时,我们首先确定以下距离:

C = 0
B = 1
D = 99

然后在我们移除队列之后,我们剩下 (D, 99),这使我们访问顶点 D 并放松它。当放松顶点 D 时,我们发现 (D to B = -300),这使得到 B 的距离为 -201。现在使用上面的“放松”方法,我们将 (B, -201) 重新插入到队列中。现在我们取队列的最小值,即我们刚刚插入的 (B, -201)。由此,我们放松 B,我们可以得到到 C 的距离为 -200。

我知道任何负循环都会使程序不会终止,但是如果我们得到一个没有负循环的图呢? 希望我在这里没有遗漏任何琐碎的事情。感谢您的帮助!

【问题讨论】:

  • 在任何周期尝试负重。
  • 这个问题仅关于 SO 就有相当多的历史,但看看他的CS answer

标签: java algorithm dijkstra


【解决方案1】:

Dijkstra 是基于这样一个事实,即一旦节点被访问并且它的所有边都被“使用”了,它就不再被访问过,因此复杂度很低(不需要“重新平衡整个图”。)

这两者都基于非负边,因为如果存在负边,您必须“重新平衡”它们,复杂性会大大增加。正如您所提到的,具有负循环的循环根本不会结束。


作者抛出异常的原因是因为他不想处理“无效”图,尤其是无限循环。


PS:您的特定图实际上应该可以由 Dijkstra 求解,但想象一下这个图有负边且没有循环:

A ->(1) B ->(1) -> C
|      (-300)
v       ^
(5)     |
D ->(1) E 

你真的解决了

B=1
C=2

在此之后,您关闭 A、B 和 C,并且不想再次输入它们。但是在这之后你发现 A->D->E->B 是 -294,然后你重新输入了所有其他值。

为了想象为什么会如此糟糕,我再举一个例子

    A ->(1) B ->(1) -> C -> SUPERGRAPH
    |    (-999999)
    v       ^
  (99999)   |
    D ->(1) E 

所以现在 A->D 的值非常高,C 指向一些 SUPERGRAPH,但是从 A 的总“长度”不超过 99999。

会发生什么?即使这可行并且没有其他循环,您从 A 求解整个超图,但在那之后,您发现实际上 A->B 短得多,您必须再次求解整个 SUPERGRAPH。如果您有更多这样的情况,对于每一种情况,您都必须再次运行 Dijkstra 算法。

【讨论】:

    【解决方案2】:

    因为如果你输入一个负数,它会throw new IllegalArgumentException("edge " + e + " has negative weight");

    【讨论】:

    • 确实如此....但是如果我把异常取出来怎么办?推理似乎仍然有效
    • 就我个人而言,我没有深入研究过这个算法,但我相信它不会创建最短路径,并且会错误地解决问题。开发人员显然输入throw 是有原因的,甚至指定它用于“边缘权重为非负的地方”。如果您允许负权重,它可能会提供不正确的无效输出,或者有某种乍一看并不明显的错误。但是,它可能能够正确解决一些问题,但通常程序的作者想要 100% 正确的解决方案,而不是高正确率。
    猜你喜欢
    • 2012-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-30
    相关资源
    最近更新 更多