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 }