检测图中是不是存在负循环的最快算法
Posted
技术标签:
【中文标题】检测图中是不是存在负循环的最快算法【英文标题】:Fastest algorithm to detect if there is negative cycle in a graph检测图中是否存在负循环的最快算法 【发布时间】:2013-05-29 16:52:09 【问题描述】:我使用矩阵d
来呈现图表。 d.(i).(j)
表示i
和j
之间的距离; 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-Fordfor 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 将不会检测到负循环。以上是关于检测图中是不是存在负循环的最快算法的主要内容,如果未能解决你的问题,请参考以下文章