1 /** 2 calc shortest path 3 */ 4 module dkh.graph.dijkstra; 5 6 import dkh.algorithm, dkh.container.radixheap; 7 8 /// information of shortest path 9 struct DijkstraInfo(T) { 10 T[] dist; /// distance 11 int[] from; /// there is a shortest path (s, ..., from[i], i) 12 this(int n, T inf) { 13 dist = new T[n]; dist[] = inf; 14 from = new int[n]; 15 } 16 } 17 18 /// calc shortest path with O(ElogE) 19 DijkstraInfo!D dijkstra(D, T)(T g, int s, D inf = D.max) { 20 import std.conv : to; 21 import std.typecons : Tuple; 22 import std.container : make, Array, heapify; 23 import std.container.binaryheap : BinaryHeap; 24 import std.traits : isIntegral; 25 26 int V = g.length.to!int; 27 auto dijk = DijkstraInfo!D(V, inf); 28 with (dijk) { 29 alias P = Tuple!(int, "to", D, "dist"); 30 auto q = (){ 31 static if (isIntegral!D) { 32 return RadixHeap!(P, "a.dist")(); 33 } else { 34 return heapify!"a.dist>b.dist"(make!(Array!P)); 35 } 36 }(); 37 q.insert(P(s, D(0))); 38 dist[s] = D(0); 39 from[s] = -1; 40 while (!q.empty) { 41 P p = q.front; q.removeFront(); 42 if (dist[p.to] < p.dist) continue; 43 foreach (e; g[p.to]) { 44 if (p.dist+e.dist < dist[e.to]) { 45 dist[e.to] = p.dist+e.dist; 46 from[e.to] = p.to; 47 q.insert(P(e.to, dist[e.to])); 48 } 49 } 50 } 51 } 52 return dijk; 53 } 54 55 /// calc shortest path with O(V^2) 56 DijkstraInfo!D dijkstraDense(D, T)(T g, int s, D inf = D.max) { 57 import std.conv : to; 58 import std.typecons : Tuple; 59 import std.container : make, Array, heapify; 60 import std.range : enumerate; 61 import std.algorithm : filter; 62 63 int V = g.length.to!int; 64 auto dijk = DijkstraInfo!D(V, inf); 65 with (dijk) { 66 alias P = Tuple!(int, "to", D, "dist"); 67 68 bool[] used = new bool[](V); 69 dist[s] = D(0); 70 from[s] = -1; 71 while (true) { 72 //todo can optimize 73 auto rng = dist.enumerate.filter!(a => !used[a.index]); 74 if (rng.empty) break; 75 auto nx = rng.minimum!"a.value < b.value"; 76 used[nx.index] = true; 77 P p = P(nx.index.to!int, nx.value); 78 if (p.dist == inf) continue; 79 foreach (e; g[p.to]) { 80 if (p.dist+e.dist < dist[e.to]) { 81 dist[e.to] = p.dist+e.dist; 82 from[e.to] = p.to; 83 } 84 } 85 } 86 } 87 return dijk; 88 } 89 90 unittest { 91 import std.algorithm, std.conv, std.stdio, std.range; 92 import std.random; 93 import std.typecons; 94 95 alias E = Tuple!(int, "to", int, "dist"); 96 97 void f(alias pred)() { 98 int n = uniform(1, 50); 99 int m = uniform(1, 500); 100 E[][] g = new E[][n]; 101 int[][] dist = new int[][](n, n); 102 foreach (i, ref v; dist) { 103 v[] = 10^^9; 104 } 105 iota(n).each!(i => dist[i][i] = 0); 106 foreach (i; 0..m) { 107 int a = uniform(0, n); 108 int b = uniform(0, n); 109 int c = uniform(0, 1000); 110 g[a] ~= E(b, c); 111 dist[a][b] = min(dist[a][b], c); 112 } 113 foreach (k; 0..n) { 114 foreach (i; 0..n) { 115 foreach (j; 0..n) { 116 dist[i][j] = min(dist[i][j], dist[i][k]+dist[k][j]); 117 } 118 } 119 } 120 foreach (i; 0..n) { 121 auto dijk = pred!int(g, i, 10^^9); 122 foreach (j; 0..n) { 123 assert(dist[i][j] == dijk.dist[j]); 124 } 125 assert(dijk.from[i] == -1); 126 foreach (j; 0..n) { 127 if (i == j) continue; 128 if (dist[i][j] == 10^^9) continue; 129 int b = dijk.from[j]; 130 assert(dijk.dist[j] == dist[i][b]+dist[b][j]); 131 } 132 } 133 } 134 import dkh.stopwatch; 135 auto ti = benchmark!(f!dijkstra, f!dijkstraDense)(100); 136 writeln("Dijkstra Random100: ", ti[].map!(a => a.toMsecs())); 137 } 138 139 unittest { 140 import std.algorithm, std.conv, std.stdio, std.range; 141 import std.random; 142 import std.typecons; 143 144 alias E = Tuple!(int, "to", int, "dist"); 145 146 void f(alias pred)() { 147 int n = uniform(1, 50); 148 int m = uniform(1, 500); 149 E[][] g = new E[][n]; 150 int[][] dist = new int[][](n, n); 151 foreach (i, ref v; dist) { 152 v[] = int.max; 153 } 154 iota(n).each!(i => dist[i][i] = 0); 155 foreach (i; 0..m) { 156 int a = uniform(0, n); 157 int b = uniform(0, n); 158 int c = uniform(0, 1000); 159 g[a] ~= E(b, c); 160 dist[a][b] = min(dist[a][b], c); 161 } 162 foreach (k; 0..n) { 163 foreach (i; 0..n) { 164 foreach (j; 0..n) { 165 if (dist[i][k] == int.max) continue; 166 if (dist[k][j] == int.max) continue; 167 dist[i][j] = min(dist[i][j], dist[i][k]+dist[k][j]); 168 } 169 } 170 } 171 foreach (i; 0..n) { 172 auto dijk = pred!int(g, i); 173 foreach (j; 0..n) { 174 assert(dist[i][j] == dijk.dist[j]); 175 } 176 assert(dijk.from[i] == -1); 177 foreach (j; 0..n) { 178 if (i == j) continue; 179 if (dist[i][j] == int.max) continue; 180 int b = dijk.from[j]; 181 assert(dijk.dist[j] == dist[i][b]+dist[b][j]); 182 } 183 } 184 } 185 import dkh.stopwatch; 186 auto ti = benchmark!(f!dijkstra, f!dijkstraDense)(100); 187 writeln("Dijkstra INF Random100: ", ti[].map!(a => a.toMsecs())); 188 }