I use a matrix d
to present a graph. d.(i).(j)
means the distance between i
and j
; v
denotes the number of nodes in the graph.
It is possible that there is negative cycle in this graph.
I would like to check if a negative cycle exists. I have written something as follows from a variation of 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
My questions are
part 1
useful?Because I call this function very often, I really want to make it as fast as possible. So my 3) question is if other algorithms (especially Bellman-Ford
) can be quicker than that?
Although the options listed in Timothy Shield's answer are all correct algorithms for finding a negative cycle in a directed weighted graph, they are not the fastest.
My go-to algorithm in this case is always the Shortest Path Faster Algorithm.
Although it has a worst-case time complexity of O(|V|*|E|)
, which is the same as Bellman-Ford, there are very few graphs for which the SPFA actually reaches that time. In practice, it is much faster, even reaching an (unproven) average time of O(|E|)
.
I have written an article in my blog explaining the details of using the SPFA to find negative cycles.
If you don't want to read the full article, the pseudocode you need is below.
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"