018-bottom-up-approach.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. ;; ==================
  2. ;; BOTTOM UP APPROACH
  3. ;; ==================
  4. ;; OK, lets try the constructive bottom up approach.
  5. (import
  6. (except (rnrs base) let-values map)
  7. (only (guile)
  8. lambda* λ
  9. ;; printing
  10. display
  11. simple-format
  12. ;; command line arguments
  13. command-line
  14. current-input-port
  15. current-output-port)
  16. (ice-9 textual-ports)
  17. (ice-9 match) ; let-match
  18. (ice-9 exceptions)
  19. (ice-9 pretty-print)
  20. (srfi srfi-1)
  21. ;; for functional structs (not part of srfi-9 directly)
  22. (srfi srfi-9 gnu)
  23. ;; hash tables
  24. (srfi srfi-69)
  25. (fileio)
  26. (list-helpers)
  27. (function-combinators))
  28. ;; =====
  29. ;; MODEL
  30. ;; =====
  31. (define make-inappropriate-value-exception
  32. (record-constructor
  33. (make-exception-type '&inappropriate-value
  34. &programming-error
  35. '(val))))
  36. (define-immutable-record-type <position>
  37. (construct-position row col)
  38. position?
  39. (row position-row set-position-row)
  40. (col position-col set-position-col))
  41. (define-immutable-record-type <path-element>
  42. (construct-path-element pos val)
  43. path-element?
  44. (pos path-element-pos set-path-element-pos)
  45. (val path-element-val set-path-element-val))
  46. (define-immutable-record-type <path>
  47. (construct-path elems sum)
  48. path?
  49. (elems path-elements set-path-element-elems)
  50. (sum path-sum set-path-element-sum))
  51. (define make-position
  52. (λ (row col)
  53. (cond
  54. [(not (integer? row))
  55. (raise-exception
  56. (make-exception
  57. (make-inappropriate-value-exception row)
  58. (make-exception-with-message "row must be a positive integer")
  59. (make-exception-with-irritants (list row))
  60. (make-exception-with-origin 'make-position)))]
  61. [(not (integer? col))
  62. (raise-exception
  63. (make-exception
  64. (make-inappropriate-value-exception col)
  65. (make-exception-with-message "col must be a positive integer")
  66. (make-exception-with-irritants (list col))
  67. (make-exception-with-origin 'make-position)))]
  68. [else
  69. (construct-position row col)])))
  70. (define position=?
  71. (λ (p1 p2)
  72. (and (= (position-row p1) (position-row p2))
  73. (= (position-col p1) (position-col p2)))))
  74. (define make-path-element
  75. (λ (pos val)
  76. (cond
  77. [(not (position? pos))
  78. (raise-exception
  79. (make-exception
  80. (make-inappropriate-value-exception pos)
  81. (make-exception-with-message "pos must be a <position>")
  82. (make-exception-with-irritants (list pos))
  83. (make-exception-with-origin 'make-path-element)))]
  84. [else
  85. (construct-path-element pos val)])))
  86. (define make-path
  87. (λ (elems sum)
  88. (construct-path elems sum)))
  89. (define make-empty-path
  90. (λ ()
  91. (make-path '() 0)))
  92. (define path-prepend
  93. (λ (path elem)
  94. (make-path (cons elem (path-elements path))
  95. (+ (path-sum path)
  96. (path-element-val elem)))))
  97. ;; navigating
  98. (define go-up-left
  99. (λ (pos)
  100. (make-position (- (position-row pos) 1)
  101. (- (position-col pos) 1))))
  102. (define go-up
  103. (λ (pos)
  104. (make-position (- (position-row pos) 1)
  105. (position-col pos))))
  106. ;; triangle abstraction
  107. (define triangle-dimensions
  108. (λ (triangle)
  109. (array-dimensions triangle)))
  110. (define in-triangle?
  111. (λ (triangle pos)
  112. (match-let ([(height width) (triangle-dimensions triangle)]
  113. [row (position-row pos)]
  114. [col (position-col pos)])
  115. (and (< row height)
  116. (< col width)
  117. (<= col row)
  118. (>= col 0)
  119. (>= row 0)))))
  120. (define triangle-ref
  121. (λ (triangle pos)
  122. (array-ref triangle
  123. (position-row pos)
  124. (position-col pos))))
  125. (define triangle-height
  126. (λ (triangle)
  127. (match-let ([(height _) (triangle-dimensions triangle)])
  128. height)))
  129. (define triangle-width
  130. (λ (triangle)
  131. (match-let ([(_ width) (triangle-dimensions triangle)])
  132. width)))
  133. (define triangle-up-element
  134. (λ (triangle pos)
  135. (let* ([element-pos (make-position (- (position-row pos) 1) (position-col pos))]
  136. [element-val (triangle-ref triangle element-pos)])
  137. (make-path-element element-pos element-val))))
  138. (define triangle-up-left-element
  139. (λ (triangle pos)
  140. (let* ([element-pos
  141. (make-position (- (position-row pos) 1)
  142. (- (position-col pos) 1))]
  143. [element-val
  144. (triangle-ref triangle element-pos)])
  145. (make-path-element element-pos element-val))))
  146. ;; =========
  147. ;; ALGORITHM
  148. ;; =========
  149. (define max-parent-element
  150. (λ (triangle pos)
  151. ;; Problem: cannot go up left for example, from col 0.
  152. (let ([up-left-pos (go-up-left pos)]
  153. [up-pos (go-up pos)])
  154. (cond
  155. ;; If both positions should exist in the triangle, return the maximum
  156. ;; element.
  157. [(and (in-triangle? triangle up-left-pos)
  158. (in-triangle? triangle up-pos))
  159. (cond
  160. [(> (triangle-ref triangle up-left-pos)
  161. (triangle-ref triangle up-pos))
  162. (list
  163. (make-path-element up-pos (triangle-ref triangle up-left-pos)))]
  164. ;; If both elements are equal, we need to consider both paths
  165. ;; up the triangle, in order to not leave out any potentially
  166. ;; maximum path.
  167. [(= (triangle-ref triangle up-left-pos)
  168. (triangle-ref triangle up-pos))
  169. (list
  170. (make-path-element up-pos (triangle-ref triangle up-left-pos))
  171. (make-path-element up-left-pos (triangle-ref triangle up-pos)))]
  172. [else
  173. (list
  174. (make-path-element up-pos (triangle-ref triangle up-pos)))])]
  175. ;; Otherwise return the one that does exist.
  176. [(in-triangle? triangle up-left-pos)
  177. (list
  178. (make-path-element up-left-pos (triangle-ref triangle up-left-pos)))]
  179. [(in-triangle? triangle up-pos)
  180. (list
  181. (make-path-element up-pos (triangle-ref triangle up-pos)))]
  182. ;; Otherwise raise an exception.
  183. [else
  184. (raise-exception
  185. (make-exception
  186. (make-exception-with-message "unexpected error")
  187. (make-exception-with-irritants (list triangle pos))
  188. (make-exception-with-origin 'max-parent-element)))]))))
  189. (define parent-elements
  190. (λ (triangle pos)
  191. (let ([up-left-pos (go-up-left pos)]
  192. [up-pos (go-up pos)])
  193. (cond
  194. ;; If both positions should exist in the triangle, return the maximum
  195. ;; element.
  196. [(and (in-triangle? triangle up-left-pos)
  197. (in-triangle? triangle up-pos))
  198. (list
  199. (make-path-element up-pos (triangle-ref triangle up-pos))
  200. (make-path-element up-left-pos (triangle-ref triangle up-left-pos)))]
  201. ;; Otherwise return the one that does exist.
  202. [(in-triangle? triangle up-left-pos)
  203. (list
  204. (make-path-element up-left-pos (triangle-ref triangle up-left-pos)))]
  205. [(in-triangle? triangle up-pos)
  206. (list
  207. (make-path-element up-pos (triangle-ref triangle up-pos)))]
  208. ;; Otherwise raise an exception.
  209. [else
  210. (raise-exception
  211. (make-exception
  212. (make-exception-with-message "unexpected error")
  213. (make-exception-with-irritants (list triangle pos))
  214. (make-exception-with-origin 'max-parent-element)))]))))
  215. (define triangle-base-paths
  216. (λ (triangle)
  217. (match-let ([(height width) (triangle-dimensions triangle)])
  218. (let ([base-row (- height 1)])
  219. (let iter ([col 0])
  220. (cond
  221. [(>= col width) '()]
  222. [else
  223. (let* ([pos (make-position base-row col)]
  224. [val (triangle-ref triangle pos)]
  225. [one-elem-path
  226. (path-prepend (make-empty-path) (make-path-element pos val))])
  227. (cons one-elem-path (iter (+ col 1))))]))))))
  228. (define reduce-paths
  229. (λ (paths)
  230. "Idea: If paths have the same top element, but different sums,
  231. then keep only the one with the maximum sum. If there are equal sums,
  232. keep only one of them."
  233. (let ([pos-to-max-path-table (make-hash-table position=?)])
  234. (for-each (λ (path)
  235. (let ([pos-key (path-element-pos (first (path-elements path)))])
  236. (hash-table-update!/default pos-to-max-path-table
  237. pos-key
  238. ;; Compare with the
  239. ;; old path.
  240. (λ (old-path)
  241. (if (> (path-sum path)
  242. (path-sum old-path))
  243. path
  244. old-path))
  245. ;; Set the current
  246. ;; path as the first
  247. ;; value for a
  248. ;; position in the
  249. ;; hash table.
  250. path)))
  251. paths)
  252. (hash-table-values pos-to-max-path-table))))
  253. (define expand-paths
  254. (λ (triangle paths)
  255. (flatten
  256. ;; For each path prepend parent elements to the path.
  257. (map (λ (path)
  258. (match-let ([(elem . others) (path-elements path)]
  259. [sum (path-sum path)])
  260. (let ([parents
  261. ;; There can be multiple maximum parents, if both
  262. ;; parents have the same value.
  263. (parent-elements triangle (path-element-pos elem))])
  264. ;; Prepend the maximum parent element(s) to the path.
  265. (map (λ (parent) (path-prepend path parent))
  266. parents))))
  267. paths))))
  268. (define find-max-sum-bottom-up
  269. (λ (triangle)
  270. (match-let ([(height width) (triangle-dimensions triangle)])
  271. ;; Iterate through the rows from bottom to top.
  272. (let iter ([current-row (- height 1)]
  273. [paths (triangle-base-paths triangle)])
  274. ;; The higher we get in the triangle, the thinner the triangle gets and
  275. ;; there are no more numbers on higher indices we need to look at.
  276. (let ([current-width (+ current-row 1)])
  277. (cond
  278. ;; If all rows have been looked at, we will know the maximum sum
  279. ;; path. In fact, there should only be one path left in the list of
  280. ;; paths.
  281. [(= current-row 0)
  282. (let iter-max ([remaining-paths (drop paths 1)]
  283. [max-path (first paths)])
  284. (cond
  285. [(null? remaining-paths) max-path]
  286. [else
  287. (let ([path (first remaining-paths)])
  288. (cond
  289. [(> (path-sum path)
  290. (path-sum max-path))
  291. (iter-max (drop remaining-paths 1) path)]
  292. [else
  293. (iter-max (drop remaining-paths 1) max-path)]))]))]
  294. ;; Otherwise go to the next column.
  295. [else
  296. ;; TODO: next idea: take all parents and implement reduce
  297. ;; for paths to keep paths from becoming too many.
  298. (let ([updated-paths (expand-paths triangle paths)])
  299. (iter (- current-row 1)
  300. updated-paths))]))))))
  301. ;; read in the data
  302. (define input-lines (get-lines-from-file (cadr (command-line))))
  303. (define input
  304. (let* ([as-list-of-lists
  305. (map (combine (λ (line) (string-split line (λ (c) (char=? c #\space))))
  306. (λ (num-strs) (map string->number num-strs)))
  307. input-lines)]
  308. [max-width
  309. (fold (λ (acc elem) (max elem acc))
  310. 0
  311. (map length as-list-of-lists))]
  312. [padded-list-of-lists
  313. (map (λ (lst) (list-right-pad lst max-width 0))
  314. as-list-of-lists)]
  315. [as-array
  316. (list->array '(0 0) padded-list-of-lists)])
  317. as-array))
  318. (display
  319. (simple-format
  320. #f "max sum (bottom-up): ~a\n"
  321. (find-max-sum-bottom-up input)))