solution.scm 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. ;;; Longest Collatz sequence
  2. ;;; Problem 14
  3. ;;; The following iterative sequence is defined for the set
  4. ;;; of positive integers:
  5. ;;; n -> n/2 (n is even)
  6. ;;; n -> 3n + 1 (n is odd)
  7. ;;; Using the rule above and starting with 13, we generate
  8. ;;; the following sequence:
  9. ;;; 13 -> 40 -> 20 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1
  10. ;;; It can be seen that this sequence (starting at 13 and
  11. ;;; finishing at 1) contains 10 terms. Although it has not
  12. ;;; been proved yet (Collatz Problem), it is thought that
  13. ;;; all starting numbers finish at 1.
  14. ;;; Which starting number, under one million, produces the
  15. ;;; longest chain?
  16. ;;; NOTE: Once the chain starts the terms are allowed to go
  17. ;;; above one million.
  18. (import
  19. (except (rnrs base) let-values map)
  20. (only (guile)
  21. lambda* λ
  22. ;; printing
  23. display
  24. simple-format)
  25. (ice-9 futures)
  26. (srfi srfi-69) ; hash tables
  27. (srfi srfi-1) ; reduce
  28. (lib math)
  29. (lib segment))
  30. (define collatz-step
  31. (λ (num)
  32. (cond
  33. [(even? num) (/ num 2)]
  34. [else (+ (* 3 num) 1)])))
  35. (define collatz-sequence-length
  36. (λ (seq-start-num)
  37. ;; (display (simple-format #f "Start of sequence: ~a\n" seq-start-num))
  38. (let ([seen-numbers (make-hash-table =)])
  39. (let loop ([sequence-index 1] [num-in-seq seq-start-num])
  40. ;; (display (simple-format #f "Number in sequence: ~a\n" num-in-seq))
  41. (cond
  42. ;; If the number has already been seen, stop and
  43. ;; return the sequence length.
  44. [(hash-table-ref/default seen-numbers num-in-seq #f)
  45. sequence-index]
  46. [else
  47. (hash-table-set! seen-numbers num-in-seq #t)
  48. (loop (+ sequence-index 1)
  49. (collatz-step num-in-seq))])))))
  50. (define find-longest-collatz-sequence
  51. (λ (start limit)
  52. ;; Calculate the maximum of sequence lengths for all
  53. ;; numbers from start to limit.
  54. (let iter-sequence-start ([seq-start start]
  55. [longest-seq-len 0]
  56. [number-with-longest-seq 0])
  57. (cond
  58. [(<= seq-start limit)
  59. (let ([seq-len (collatz-sequence-length seq-start)])
  60. (cond
  61. [(> seq-len longest-seq-len)
  62. ;; (display (simple-format #f "found new longest with length: ~a\n" seq-len))
  63. (iter-sequence-start (+ seq-start 1)
  64. seq-len
  65. seq-start)]
  66. [else
  67. (iter-sequence-start (+ seq-start 1)
  68. longest-seq-len
  69. number-with-longest-seq)]))]
  70. [else
  71. (display
  72. (simple-format
  73. #f "number with longest sequence in segment ~a-~a: ~a (with length ~a)\n"
  74. start limit number-with-longest-seq longest-seq-len))
  75. (cons number-with-longest-seq longest-seq-len)]))))
  76. ;; (define run-in-parallel
  77. ;; (λ (segments map-proc reduce-proc)
  78. ;; "Use futures to run a procedure in parallel, if multiple
  79. ;; cores are available. Take a list of SEGMENTS as input, which
  80. ;; are ranges of values to work on using the given
  81. ;; MAP-PROC. When the MAP-PROC calls for all segments finished
  82. ;; and returned values, the REDUCE-PROC is applied using apply
  83. ;; to the results."
  84. ;; (let ([futures
  85. ;; (map (λ (seg)
  86. ;; ;; (display (simple-format #f "making future for segment: ~a\n" seg))
  87. ;; (future (map-proc seg)))
  88. ;; segments)])
  89. ;; (display (simple-format #f "futures: ~a\n" futures))
  90. ;; (let ([segment-results (map touch futures)])
  91. ;; (display (simple-format #f "segment results: ~a\n" segment-results))
  92. ;; #;(apply reduce-proc segment-results)))))
  93. ;; (let* ([start 1]
  94. ;; [end (expt 10 6)]
  95. ;; [num-cores 8]
  96. ;; [segments (segment start end 8)])
  97. ;; ;; (display (simple-format #f "segments: ~a\n" segments))
  98. ;; (let ([result
  99. ;; (run-in-parallel segments
  100. ;; (λ (seg)
  101. ;; (find-longest-collatz-sequence
  102. ;; (segment-start seg)
  103. ;; (segment-end seg)))
  104. ;; max)])
  105. ;; (display
  106. ;; (simple-format
  107. ;; #f "longest sequence length: ~a\n"
  108. ;; result))))
  109. ;; (define max-with-proc
  110. ;; (λ (proc elem . other)
  111. ;; (let loop ([remaining-elements (cons elem other)]
  112. ;; [maximum-elem elem]
  113. ;; [maximum -inf.0])
  114. ;; (let* ([current-elem (car remaining-elements)]
  115. ;; [current-elem-value (proc current-elem)])
  116. ;; (cond
  117. ;; [(null? remaining-elements) maximum]
  118. ;; [(> current-elem-value maximum)
  119. ;; (loop (cdr remaining-elements)
  120. ;; current-elem
  121. ;; current-elem-value)]
  122. ;; [else
  123. ;; (loop (cdr remaining-elements)
  124. ;; maximum-elem
  125. ;; maximum)])))))
  126. (display
  127. (simple-format
  128. #f "longest sequence length (number . length): ~a\n"
  129. (let* ([start 1]
  130. [end (expt 10 6)]
  131. [num-cores 16]
  132. [segments (segment start end num-cores)])
  133. (let ([futures
  134. (map (λ (seg)
  135. (future
  136. (find-longest-collatz-sequence (segment-start seg)
  137. (segment-end seg))))
  138. segments)])
  139. (reduce (λ (prev-max-pair current-pair)
  140. (if (> (cdr prev-max-pair) (cdr current-pair))
  141. prev-max-pair
  142. current-pair))
  143. -inf.0
  144. (map touch futures))))))