6

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

该图中可能存在负循环。

我想检查是否存在负循环。我从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

我的问题是

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

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

4

2 回答 2

6

尽管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"
于 2020-05-01T12:05:49.253 回答
4

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


Bellman-FordFloyd-Warshall中的任何一个都适用于这个问题。性能比较如下:

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


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

于 2013-06-03T22:17:35.320 回答