检测图中是不是存在负循环的最快算法

Posted

技术标签:

【中文标题】检测图中是不是存在负循环的最快算法【英文标题】:Fastest algorithm to detect if there is negative cycle in a graph检测图中是否存在负循环的最快算法 【发布时间】:2013-05-29 16:52:09 【问题描述】:

我使用矩阵d 来呈现图表。 d.(i).(j) 表示ij 之间的距离; v 表示图中的节点数。

这个图中可能存在负循环。

我想检查是否存在负循环。我从Floyd-Warshall 的变体中写了如下内容:

let dr = Matrix.copy d in

(* part 1 *)
for i = 0 to v - 1 do
  dr.(i).(i) <- 0
done;

(* part 2 *)
try
  for k = 0 to v - 1 do
    for i = 0 to v - 1 do
      for j = 0 to v - 1 do
          let improvement = dr.(i).(k) + dr.(k).(j) in  
          if improvement < dr.(i).(j) then (
          if (i <> j) then
            dr.(i).(j) <- improvement
          else if improvement < 0 then
            raise BreakLoop )
      done
    done
  done;
  false
with 
  BreakLoop -> true

我的问题是

    上面的代码正确吗? part 1 有用吗?

因为我经常调用这个函数,我真的想让它尽可能快。所以我的 3) 问题是其他算法(尤其是Bellman-Ford)是否可以比这更快?

【问题讨论】:

【参考方案1】:

虽然Timothy Shield's answer 中列出的选项都是在有向加权图中找到负循环的正确算法,但它们并不是最快的。

在这种情况下,我的首选算法始终是 Shortest Path Faster Algorithm。

尽管它的最坏情况时间复杂度为O(|V|*|E|),与 Bellman-Ford 相同,但 SPFA 实际达到该时间的图很少。在实践中,它要快得多,甚至达到(未经证实的)平均时间 O(|E|)

我在my blog写了一篇文章,解释了使用SPFA寻找负循环的细节。

如果你不想看全文,你需要的伪代码如下。

function SPFA(G):
    for v in V(G):
        len[v] = 0
        dis[v] = 0
        Queue.push(v)
    while !Queue.is_empty():
        u = Queue.pop()
        for (u, v) in E(G):
            if dis[u] + w(u, v) < dis[v]:
                len[v] = len[u] + 1
                if len[v] == n:
                    return "negative cycle detected"
                dis[v] = dis[i] + w(u, v)
                if !Queue.contains(v):
                    Queue.push(v)
    return "no negative cycle detected"

【讨论】:

【参考方案2】:

关于你的代码正确性的第一个问题更适合http://codereview.stackexchange.com。


Bellman-Ford 或 Floyd-Warshall 中的任何一个都适合此问题。性能比较如下:

Bellman-Ford(***) 时间复杂度:O(|V|*|E|) 空间复杂度:O(|V|) The section on "Finding negative cycles"是你想要的 Floyd-Warshall(***) 时间复杂度:O(|V|^3) 空间复杂度:O(|V|^2)

由于|E||V|^2 为界,Bellman-Ford 显然是赢家,我建议您使用它。


如果图没有负循环是预期的正常情况,那么作为算法的第一步进行快速检查可能是合适的:图是否包含任何负边?如果不是,那么它当然不包含任何负循环,并且您有一个O(|E|) 最佳情况算法来检测是否存在任何负循环。

【讨论】:

感谢您的评论...由于我对图形的唯一表示是矩阵,因此 Bellman-Ford for each edge (u, v) with weight w in edges: wiki 页面中的步骤仍然需要通过两个循环 for i = 0 to v - 1 do for j = 0 to v - 1 do ... 来实现。所以对于我来说,|E||V|^2 是完全一样的,不是吗? @SoftTimur 哦,我错过了关于您使用图表矩阵的部分。在那种情况下,是的,它们是相同的。 :) 考虑到这一点,我仍然会说 Bellman-Ford 有 轻微 优势,因为空间复杂性较小,但我同意你可以使用任何一个。 如果从源头无法到达负循环,Bellman Ford 将不会检测到负循环。

以上是关于检测图中是不是存在负循环的最快算法的主要内容,如果未能解决你的问题,请参考以下文章

用于检测图中循环的算法的修改

Bellman-Ford 算法检测啥?负重还是负循环?

最短路之Bellman-ford算法java代码模板

适用于负循环的 Floyd-Warshall 算法 [关闭]

数据结构8——最短路径

Bellman_Ford算法求带有负边权的最短路算法