dijkstra.scm 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. (library (dijkstra)
  2. (export dijkstra-shortest-path)
  3. (import (except (rnrs base)
  4. let-values
  5. map)
  6. (only (guile)
  7. lambda* λ
  8. when
  9. simple-format)
  10. (ice-9 pretty-print)
  11. (srfi srfi-1) ; lists
  12. (srfi srfi-69) ; hash tables
  13. ;; Functional Sets
  14. (pfds sets)
  15. ;; Priority Search Queues
  16. (pfds psqs)
  17. ;; Bounded Balance Trees
  18. (pfds bbtrees))
  19. (define make-empty-set
  20. (λ (less?)
  21. (make-set less?)))
  22. (define set-insert-multiple
  23. (λ (myset items)
  24. (cond
  25. [(null? items) myset]
  26. [else
  27. (set-insert-multiple (set-insert myset (car items))
  28. (cdr items))])))
  29. (define set-empty?
  30. (λ (set)
  31. (= (set-size set) 0)))
  32. (define dijkstra-shortest-path
  33. (lambda* (start-node
  34. nodes
  35. get-neighbors
  36. get-neighbor-distance
  37. node<
  38. #:key
  39. (distance< <))
  40. "Find the shortest paths between the START-NODE and all other NODES,
  41. given:
  42. GET-NEIGHBORS: A function, that maps from an actual node struct to a list
  43. of node structures.
  44. GET-NEIGHBOR-DISTANCE: A function that returns the distance from one
  45. node to another.
  46. NODE<: A less function, that introduces an order to nodes. This is
  47. important for the underlying functional set implementation.
  48. DISTANCE<: A less function for comparing distances. This is useful for
  49. comparing distances, that are not mere numbers, but might consist of
  50. several attributes. For example how many times a person needs to
  51. change means of transportation to arrive at a destination."
  52. (define init-unvisited-nodes (set-insert-multiple (make-empty-set node<) nodes))
  53. (define init-visited-nodes (make-empty-set node<))
  54. (define init-distances
  55. (alist->hash-table
  56. (map (λ (node)
  57. (if (equal? node start-node)
  58. (cons node 0)
  59. (cons node +inf.0)))
  60. nodes)))
  61. ;; Set distance from start node to itself to 0.
  62. (define init-routes-table
  63. (alist->hash-table (list (cons start-node start-node))
  64. eq?))
  65. ;; (hash-table-set! init-distances start-node 0)
  66. ;; (hash-table-set! init-routes-table start-node start-node)
  67. ;; Visit an unvisited node with shortest known distance from start
  68. ;; node. Initially the start node, since all other nodes still have infinite
  69. ;; distance.
  70. (let iter ([current-node start-node]
  71. [distances° init-distances]
  72. [unvisited° init-unvisited-nodes]
  73. [visited° init-visited-nodes]
  74. [routes° init-routes-table])
  75. (cond
  76. ;; Stop, if there are no more unvisited nodes.
  77. [(set-empty? unvisited°)
  78. (values distances° routes°)]
  79. [else
  80. ;; Calculate distance to every unvisited neighbor from the start
  81. ;; node. The distance is the distance to current node plus distance to
  82. ;; the unvisted neighbor).
  83. (let* ([neighbors (get-neighbors current-node)]
  84. ;; Only look at unvisited neighbors.
  85. [unvisited-neighbors
  86. (filter (λ (neighbor) (set-member? unvisited° neighbor))
  87. neighbors)])
  88. (cond
  89. ;; If this particular node does not have any unvisited neighbors, go
  90. ;; back to visiting the next node.
  91. [(null? unvisited-neighbors)
  92. ;; Repeat until all nodes visited.
  93. (iter (set-fold (λ (node acc)
  94. (cond [(null? acc) node]
  95. [(distance< (hash-table-ref distances° node)
  96. (hash-table-ref distances° acc))
  97. node]
  98. [else acc]))
  99. '()
  100. unvisited°)
  101. distances°
  102. ;; Mark current node as visited.
  103. (set-remove unvisited° current-node)
  104. (set-insert visited° current-node)
  105. routes°)]
  106. [else
  107. ;; Look at the distances to all neighbors and update distances and
  108. ;; routes accordingly.
  109. (for-each (λ (neighbor)
  110. (let ([start-to-neighbor-distance
  111. (+ (hash-table-ref distances° current-node)
  112. (get-neighbor-distance current-node neighbor))])
  113. ;; If distance from the start node to a neighbor node
  114. ;; is less than previously known distance for that
  115. ;; node, update that distance in the distances
  116. ;; table. If a distance is updated, also update what
  117. ;; that node's previous node on the path to the node
  118. ;; is (the current node).
  119. (when (distance< start-to-neighbor-distance
  120. (hash-table-ref distances° neighbor))
  121. ;; WARNING: Mutation here! Need
  122. ;; something like a functional hash
  123. ;; table here ...
  124. (hash-table-set! distances°
  125. neighbor start-to-neighbor-distance)
  126. (hash-table-set! routes°
  127. neighbor current-node))))
  128. unvisited-neighbors)
  129. ;; Continue with unvisted nodes.
  130. (iter (set-fold (λ (node acc)
  131. (cond [(null? acc) node]
  132. [(distance< (hash-table-ref distances° node)
  133. (hash-table-ref distances° acc))
  134. node]
  135. [else acc]))
  136. '()
  137. unvisited°)
  138. distances°
  139. ;; Mark current node as visited.
  140. (set-remove unvisited° current-node)
  141. (set-insert visited° current-node)
  142. routes°)]))])))))