1 module dkh.graph.scc; 2 3 import dkh.graph.primitive; 4 import dkh.container.stackpayload, dkh.container.deque; 5 6 /// 強連結成分の情報 7 struct SCCInfo { 8 int[] id; /// 頂点id -> 強連結成分id 9 int[][] groups; /// 強連結成分id -> その連結成分の頂点idたち 10 /// iと同じgroupの頂点を返す 11 const(int[]) group(size_t i) const { 12 return groups[id[i]]; 13 } 14 this(size_t n) { 15 id = new int[n]; 16 } 17 } 18 19 /// 強連結成分分解 20 SCCInfo scc(T)(T g) { 21 import std.range; 22 import std.algorithm : each, map, min, reverse; 23 import std.conv : to; 24 auto n = g.length; 25 auto sccInfo = SCCInfo(n); 26 with (sccInfo) { 27 bool[] inS = new bool[n]; 28 int[] low = new int[n], ord = new int[n]; ord[] = -1; 29 int time = 0; 30 Deque!int st; 31 int bufC = 0; 32 StackPayload!int buf; buf.reserve(n); 33 StackPayload!(int[]) gBuf; 34 void dfs(int v) { 35 low[v] = ord[v] = time++; 36 st.insertBack(v); 37 inS[v] = true; 38 foreach (e; g[v]) { 39 if (ord[e.to] == -1) { 40 dfs(e.to); 41 low[v] = min(low[v], low[e.to]); 42 } else if (inS[e.to]) { 43 low[v] = min(low[v], ord[e.to]); 44 } 45 } 46 if (low[v] == ord[v]) { 47 while (true) { 48 int u = st.back; st.removeBack; 49 buf ~= u; 50 if (u == v) break; 51 } 52 auto gr = buf.data[bufC..$]; 53 bufC = buf.length.to!int; 54 gr.each!(x => inS[x] = false); 55 gBuf ~= gr; 56 } 57 } 58 foreach (i; 0..n) { 59 if (ord[i] == -1) dfs(i.to!int); 60 } 61 groups = gBuf.data; 62 reverse(groups); 63 groups.each!((i, v) => v.each!(x => id[x] = i.to!int)); 64 } 65 return sccInfo; 66 } 67 68 /// 69 unittest { 70 import std.algorithm, std.typecons; 71 alias E = Tuple!(int, "to"); 72 E[][] g = new E[][5]; 73 g[0] ~= E(1); 74 g[1] ~= E(2); 75 g[2] ~= E(0); g[2] ~= E(3); 76 g[3] ~= E(4); 77 g[4] ~= E(3); 78 79 auto info = scc(g); 80 81 assert(info.id[0] == info.id[1] && info.id[1] == info.id[2]); 82 assert(info.id[3] == info.id[4]); 83 assert(info.id[0] < info.id[3]); //idはトポロジカル順 84 assert(equal(info.group(0).dup.sort!"a<b", [0, 1, 2])); 85 assert(equal(info.group(3).dup.sort!"a<b", [3, 4])); 86 } 87 88 unittest { 89 import std.algorithm, std.conv, std.stdio, std.range; 90 import std.random; 91 import std.typecons; 92 93 struct E {int to;} 94 95 void f() { 96 int n = uniform(1, 50); 97 int p = uniform(1, 50); 98 E[][] g = new E[][n]; 99 bool[][] naive = new bool[][](n, n); 100 foreach (i; 0..n) { 101 foreach (j; 0..n) { 102 if (i == j) continue; 103 if (uniform(0, 100) < p) { 104 g[i] ~= E(j); 105 naive[i][j] = true; 106 } 107 } 108 } 109 110 auto sccInfo = scc(g); 111 112 foreach (k; 0..n) { 113 foreach (i; 0..n) { 114 foreach (j; 0..n) { 115 naive[i][j] |= naive[i][k] && naive[k][j]; 116 } 117 } 118 } 119 120 foreach (i; 0..n) { 121 int iid = sccInfo.id[i]; 122 assert(sccInfo.groups[iid].find(i).empty == false); 123 foreach (j; i+1..n) { 124 bool same = sccInfo.id[i] == sccInfo.id[j]; 125 if (same) { 126 assert(naive[i][j] && naive[j][i]); 127 } else { 128 assert(!naive[i][j] || !naive[j][i]); 129 if (sccInfo.id[i] < sccInfo.id[j]) { 130 assert(!naive[j][i]); 131 } else { 132 assert(!naive[i][j]); 133 } 134 } 135 } 136 } 137 } 138 import dkh.stopwatch; 139 auto ti = benchmark!(f)(1000); 140 writeln("SCC Random1000: ", ti[0].toMsecs); 141 }