caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] Strongly connected component algorithms
@ 2011-11-16  1:32 Diego Olivier Fernandez Pons
  0 siblings, 0 replies; only message in thread
From: Diego Olivier Fernandez Pons @ 2011-11-16  1:32 UTC (permalink / raw)
  To: caml-list

[-- Attachment #1: Type: text/plain, Size: 4036 bytes --]

     Caml-list

A couple weeks ago, Kim Quyen Ly asked a question about algorithms for
strongly connected components.

I answered the best known algorithms were Mehlhorn-Gabow's and Tarjan's,
both linear in number of arcs.
I wrote an implementation in Caml but was unsatisfied with it because in
theory these algorithms use 3 data structures in total (2 arrays + 1 stack
or 1 array + 2 stacks).
However I had an extra stack because of the recursion, and couldn't figure
out how to merge the call-stack with the open-node stack of the dfs.

I finally checked in the original Mehlhorn paper (Algorithmica 1996) and
Sedgewick implementations, to find out that not only did they use a
recursive function as well but they had MORE arrays and stacks than
theoretically required. I guess I will have to wait Knuth reaches the
corresponding TAOCP volume to uncompile his assembler code and finally know
the truth.

So here are my implementations. The example graph was built to show the
case where the call-stack and open-nodes stack differ (node 4).
I recommend potential users to prove correction before using the
implementation, I am a lousy coder.

(* Make matrix from list *)
let to_matrix = function list ->
  let n = 1 + List.fold_left (fun current (i, j) -> max current (max i j))
0 list in
  let matrix = Array.make_matrix n n 0 in
  let rec add_arc = function
    | [] -> matrix
    | (i, j) :: tail -> matrix.(i).(j) <- 1; add_arc tail
  in add_arc list

(* Example built to show the open-node stack / dfs call-stack difference *)
let example = to_matrix [(0, 1); (1, 2); (2, 3); (3, 4); (4, 2); (2, 1);
(3, 5); (5, 6); (6, 5)]


(* Mehlhorn Gabow scc *)
let cmg_scc = function matrix ->

  let n = Array.length matrix in
  let
      visited_at_depth = Array.make n max_int and
      roots = Stack.create () and
      open_nodes = Stack.create ()
  in

  let rec unstack_until = function i ->
    match Stack.pop open_nodes with
      | n when n = i -> [i]
      | n -> n :: unstack_until i
  in

  let rec dfs depth = function i ->

    let result = ref [] in

    (* mark *)
    Stack.push depth roots;
    Stack.push i open_nodes;
    visited_at_depth.(i) <- depth;

    (* dive *)
    for j = 0 to n - 1 do
      if (matrix.(i).(j) = 1) && (visited_at_depth.(j) = max_int) then
result := dfs (depth + 1) j @ !result
    done;

    (* process reverse-arcs *)
    for j = 0 to n - 1 do
      if (matrix.(i).(j) = 1) && (visited_at_depth.(j) < depth) then
let scc_returns_to_depth = visited_at_depth.(j) in
 while Stack.top roots > scc_returns_to_depth do ignore (Stack.pop roots)
done
    done;

    (* emit connected component if current node is root *)
    if depth = Stack.top roots then
      (
ignore (Stack.pop roots);
unstack_until i :: !result
      )
    else
      !result
  in

  let result = ref [] in
  for i = 0 to n - 1 do
    if (visited_at_depth.(i) = max_int) then result := (dfs 0 i) @ !result
  done;
  !result


(* Tarjan scc *)
let tarjan_scc = function matrix ->

  let n = Array.length matrix in
  let
      visited_at_depth = Array.make n max_int and
      scc_root = Array.make n max_int and
      open_nodes = Stack.create () and
      result = ref []
  in

  let rec unstack_until = function i ->
    match Stack.pop open_nodes with
      | n when n = i -> [i]
      | n -> n :: unstack_until i
  in

  let rec dfs depth = function i ->

    (* mark *)
    visited_at_depth.(i) <- depth;
    scc_root.(i) <- depth;
    Stack.push i open_nodes;

    for j = 0 to n - 1 do
      if matrix.(i).(j) = 1 then
if visited_at_depth.(j) = max_int then
  scc_root.(i) <- min scc_root.(i) (dfs (depth + 1) j) (* dive *)
 else
  scc_root.(i) <- min scc_root.(i) visited_at_depth.(j) (* reverse-arc *)
    done;

    (* emit connected component if current node is root *)
    if scc_root.(i) = visited_at_depth.(i) then
      result := unstack_until i :: !result;
     scc_root.(i)
  in

  for i = 0 to n - 1 do
    if (visited_at_depth.(i) = max_int) then ignore (dfs 0 i)
  done;
  !result

         Diego Olivier

[-- Attachment #2: Type: text/html, Size: 6114 bytes --]

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2011-11-16  1:32 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-11-16  1:32 [Caml-list] Strongly connected component algorithms Diego Olivier Fernandez Pons

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).