1717package adventofcode.util.graph
1818
1919import java.util.PriorityQueue
20- import kotlin.time.measureTime
21- import kotlin.time.measureTimedValue
22-
23- private fun compareDoubles (left : Double , right : Double ): Int {
24- if (left < right) {
25- return - 1
26- }
27- if (right < left) {
28- return 1
29- }
30- return 0
31- }
3220
3321object Graphs {
3422
35- /* *
36- * Implementation of Yen's K-Shortest Paths Algorithm as a sequence
37- * @param start the starting vertex
38- * @param end the ending vertex
39- * @param vertices the set of all vertices
40- * @param neighbors a function that returns the neighbors of a vertex
41- * @param weight a function that returns the weight of an edge
42- * @return a sequence of increasingly longer paths from the start vertex to the end vertex
43- */
44- fun <V > shortestPaths (start : V , end : V , vertices : Set <V >, neighbors : (V ) -> List <V >, weight : (V , V ) -> Double ) =
45- sequence {
46- val previousPaths = mutableListOf<List <V >>()
47- val absoluteShortest = shortestPaths(start, vertices, neighbors, weight)
48- if (absoluteShortest.pathExists(end)) {
49- val shortest = absoluteShortest.pathTo(end)
50- yield (shortest)
51- previousPaths.addLast(shortest)
52- var k = 2
53- while (true ) {
54- val previousPath = previousPaths.last()
55- val candidates = previousPath.asSequence().zipWithNext().map { (from, to) ->
56- val head = previousPath.takeWhile { it != to }
57- val visited = head.toSet()
58- val limitedNeighbors: (V ) -> List <V > =
59- { n -> if (n == from) neighbors(n) - visited - to else neighbors(n) - visited }
60- val (tails, _) = measureTimedValue { shortestPaths(from, vertices, limitedNeighbors, weight) }
61- head to tails
62- }
63- .filter { (_, tails) -> tails.pathExists(end) }
64- .map { (head, tails) -> head + tails.pathTo(end).drop(1 ) }
65- .filter { it !in previousPaths }
66- .toList()
67-
68- if (candidates.isEmpty()) {
69- return @sequence
70- }
71- val nextShortest =
72- candidates.minBy { it.asSequence().zipWithNext().sumOf { (from, to) -> weight(from, to) } }
73- yield (nextShortest)
74- previousPaths.addLast(nextShortest)
75- k++
76- }
77- }
78- }
79-
80- /* *
81- * Implementation of Dijkstra's Algorithm
82- * @param start the starting vertex
83- * @param vertices the set of all vertices
84- * @param neighbors a function that returns the neighbors of a vertex
85- * @param weight a function that returns the weight of an edge
86- * @return the shortest paths from the start vertex to all other vertices
87- */
88- fun <V > shortestPaths (
89- start : V ,
90- neighbors : (V ) -> List <V >,
91- weight : (V , V ) -> Double = { _, _ -> 1.0 }
92- ): ShortestPaths <V > {
93- val pred = mutableMapOf<V , V >()
94- val dist = mutableMapOf<V , Double >()
95- val visited = mutableSetOf<V >()
96- dist[start] = 0.0
97- val queue = PriorityQueue { l: V , r: V ->
98- compareDoubles(
99- dist.getOrDefault(l, Double .MAX_VALUE ),
100- dist.getOrDefault(r, Double .MAX_VALUE )
101- )
102- }
103- queue.add(start)
104- while (queue.isNotEmpty()) {
105- val vertex = queue.poll()
106- visited.add(vertex)
107- val distanceToVertex = dist.getOrDefault(vertex, Double .MAX_VALUE )
108- neighbors(vertex).filter { it !in visited }.forEach { neighbor ->
109- val distanceThroughVertex = distanceToVertex + weight(vertex, neighbor)
110- if (distanceThroughVertex < dist.getOrDefault(neighbor, Double .MAX_VALUE )) {
111- pred[neighbor] = vertex
112- dist[neighbor] = distanceThroughVertex
113- queue.add(neighbor)
114- }
115- }
116- }
117- return ShortestPaths (start, dist, pred)
118- }
23+ private fun <V > MutableMap <V , Double >.distanceTo (vertex : V ) = getOrPut(vertex) { Double .POSITIVE_INFINITY }
11924
12025 /* *
12126 * Implementation of Dijkstra's Algorithm
27+ *
12228 * @param start the starting vertex
123- * @param vertices the set of all vertices
12429 * @param neighbors a function that returns the neighbors of a vertex
125- * @param weight a function that returns the weight of an edge
126- * @return the shortest paths from the start vertex to all other vertices
30+ * @param distance a function that returns the distance between two vertices
31+ * @return a {@link ShortestPaths} object containing the shortest paths from the start vertex to all other vertices
12732 */
12833 fun <V > shortestPaths (
12934 start : V ,
130- vertices : Set <V >,
13135 neighbors : (V ) -> List <V >,
132- weight : (V , V ) -> Double = { _, _ -> 1.0 }
36+ distance : (V , V ) -> Double = { _, _ -> 1.0 }
13337 ): ShortestPaths <V > {
13438 val pred = mutableMapOf<V , V >()
13539 val dist = mutableMapOf<V , Double >()
13640 val visited = mutableSetOf<V >()
137- vertices.forEach { vertex -> dist[vertex] = Double .POSITIVE_INFINITY }
13841 dist[start] = 0.0
13942 val queue = PriorityQueue { l: V , r: V ->
140- compareDoubles(
141- dist.getOrDefault(l, Double .MAX_VALUE ),
142- dist.getOrDefault(r, Double .MAX_VALUE )
143- )
43+ dist.distanceTo(l).compareTo(dist.distanceTo(r))
14444 }
14545 queue.add(start)
14646 while (queue.isNotEmpty()) {
14747 val vertex = queue.poll()
148- require(vertex in vertices)
14948 visited.add(vertex)
150- val distanceToVertex = dist.getOrDefault (vertex, Double . MAX_VALUE )
49+ val distanceToVertex = dist.distanceTo (vertex)
15150 neighbors(vertex).filter { it !in visited }.forEach { neighbor ->
152- require(neighbor in vertices)
153- val distanceThroughVertex = distanceToVertex + weight(vertex, neighbor)
154- if (distanceThroughVertex < dist[neighbor]!! ) {
51+ val distanceThroughVertex = distanceToVertex + distance(vertex, neighbor)
52+ if (distanceThroughVertex < dist.distanceTo(neighbor)) {
15553 pred[neighbor] = vertex
15654 dist[neighbor] = distanceThroughVertex
15755 queue.add(neighbor)
@@ -167,13 +65,14 @@ object Graphs {
16765 val visited = mutableSetOf<V >()
16866 val queue = mutableListOf (Reachable (0 , start))
16967 while (queue.isNotEmpty()) {
170- val reachable = queue.removeLast ()
68+ val reachable = queue.removeFirst ()
17169 visited + = reachable.vertex
17270 if (reachable.steps < maxSteps) {
173- val ns = neighbors(reachable.vertex)
174- .filter { it !in visited }
175- .map { Reachable (reachable.steps + 1 , it) }
176- queue.addAll(ns)
71+ neighbors(reachable.vertex)
72+ .filter { it !in visited }
73+ .map { Reachable (reachable.steps + 1 , it) }
74+ .forEach { queue.addLast(it) }
75+
17776 }
17877 }
17978 return visited
0 commit comments