014-improved.scm 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  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 reduce-init)
  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. MAP-PROC is applied to the
  81. SEGMENTS using map. When the MAP-PROC calls for all segments
  82. finished and returned values, the REDUCE-PROC is applied to
  83. the map result using reduce and the REDUCE-INIT argument."
  84. (let ([futures
  85. (map (λ (seg)
  86. (make-future
  87. ;; Need to wrap in a thunk, to not
  88. ;; immediately start evaluating.
  89. (λ () (map-proc seg))))
  90. segments)])
  91. (let ([segment-results (map touch futures)])
  92. (reduce reduce-proc
  93. reduce-init
  94. segment-results)))))
  95. (let* ([start 1]
  96. [end (expt 10 6)]
  97. [num-cores 64]
  98. [segments (segment start end num-cores)])
  99. (display
  100. (simple-format
  101. #f "longest sequence length (number . length): ~a\n"
  102. (run-in-parallel segments
  103. (λ (seg)
  104. (find-longest-collatz-sequence (segment-start seg)
  105. (segment-end seg)))
  106. (λ (acc current)
  107. (if (> (cdr acc) (cdr current))
  108. acc
  109. current))
  110. -inf.0))))