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

Sof*_*mur 5 algorithm graph-theory dijkstra bellman-ford floyd-warshall

我使用矩阵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
Run Code Online (Sandbox Code Playgroud)

我的问题是

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

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

Tim*_*lds 5

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


贝尔曼-福特法弗洛伊德-沃歇尔法都适合解决这个问题。性能对比如下:

由于|E|的边界是|V|^2贝尔曼-福特显然是赢家,我建议您使用它。


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

  • @SoftTimur 哦,我错过了关于你使用图表矩阵的部分。在这种情况下,是的,它们是相同的。:) 考虑到这一点,我仍然会说贝尔曼-福特有“轻微”优势,因为空间复杂度较小,但我同意你可以使用其中任何一个。 (2认同)

Kon*_*ira 5

尽管Timothy Shield 的答案中列出的选项都是在有向加权图中找到负循环的正确算法,但它们并不是最快的。

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

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

我在我的博客中写了一篇文章,解释了使用 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"
Run Code Online (Sandbox Code Playgroud)