012.scm 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. ;;; Highly divisible triangular number
  2. ;;; Problem 12
  3. ;;; The sequence of triangle numbers is generated by adding the
  4. ;;; natural numbers. So the 7th triangle number would be 1 + 2 + 3 + 4
  5. ;;; + 5 + 6 + 7 = 28. The first ten terms would be:
  6. ;;; 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...
  7. ;;; Let us list the factors of the first seven triangle numbers:
  8. ;;; 1: 1
  9. ;;; 3: 1,3
  10. ;;; 6: 1,2,3,6
  11. ;;; 10: 1,2,5,10
  12. ;;; 15: 1,3,5,15
  13. ;;; 21: 1,3,7,21
  14. ;;; 28: 1,2,4,7,14,28
  15. ;;; We can see that 28 is the first triangle number to have over five
  16. ;;; divisors.
  17. ;;; What is the value of the first triangle number to have over five
  18. ;;; hundred divisors?
  19. (import
  20. (except (rnrs base)
  21. let-values
  22. map)
  23. (only (guile)
  24. lambda* λ)
  25. (segment)
  26. #;(ice-9 match)
  27. (ice-9 futures))
  28. (define divides?
  29. (λ (num div)
  30. (= (remainder num div) 0)))
  31. ;; Gaussian sum allows to efficiently calculate sums without
  32. ;; calculating the sum for the previous number. This will help with
  33. ;; big sums.
  34. (define gaussian-sum
  35. (λ (n)
  36. "Calculate the sum from 1 to n using Gauß' sum formula."
  37. (/ (* n (+ n 1))
  38. 2)))
  39. (define calculate-triangular-number
  40. (λ (n)
  41. (gaussian-sum n)))
  42. (define number-of-factors
  43. (λ (n)
  44. (let ([limit (floor (sqrt n))])
  45. (let loop ([potential-factor 1] [factors 0])
  46. (cond
  47. [(> potential-factor limit) factors]
  48. [else
  49. (if (divides? n potential-factor)
  50. (loop (+ potential-factor 1)
  51. ;; If the number is divisable by the
  52. ;; potential-factor, it means that there is a
  53. ;; second factor, with which multiplied, the
  54. ;; potential-factor will result in the
  55. ;; number. This second factor must be greater than
  56. ;; the square root of the number. The existence of
  57. ;; the second factor greater than the square root
  58. ;; allows us to add 2 to the number of factors,
  59. ;; without actually looking at the second factor
  60. ;; and stopping to check for more factors at a
  61. ;; potential-factor greater than the square
  62. ;; root. Without this optimization, the
  63. ;; calculation needs too much time.
  64. (+ factors 2))
  65. (loop (+ potential-factor 1)
  66. factors))])))))
  67. (define sufficient-factors?
  68. (λ (n target-num-factors)
  69. (let ([factors-count (number-of-factors n)])
  70. ;; (display (simple-format #f "~a has ~a factors\n" n factors-count))
  71. (> factors-count target-num-factors))))
  72. (define next
  73. (λ (n)
  74. (+ n 1)))
  75. (define find-triangular-number
  76. (lambda* (num-factors #:key (limit #f) (num-procs 8))
  77. "Find the smallest triangular number, which has more than
  78. NUM-FACTORS factors."
  79. (define find-from-to
  80. (lambda* (nth limit #:key (default +inf.0))
  81. "Find the smallest triangular number, which has more than
  82. NUM-FACTORS factors, within the specified range of NTH and LIMIT."
  83. ;; (when (= (remainder nth 1000) 0)
  84. ;; (display (simple-format #f "nth: ~a\n" nth)))
  85. (let ([triangular-number (calculate-triangular-number nth)])
  86. (cond
  87. ;; Return the given default value, if within the specified
  88. ;; range, no triangular number with sufficient factors can
  89. ;; be found.
  90. [(> nth limit) default]
  91. ;; If a triangular number with sufficient factors is found,
  92. ;; return that and do not recur.
  93. [(sufficient-factors? (calculate-triangular-number nth)
  94. num-factors)
  95. triangular-number]
  96. ;; Otherwise continue with the next triangular number.
  97. [else
  98. (find-from-to (next nth) limit)]))))
  99. (cond
  100. [limit
  101. (display (simple-format #f "limit specified, running in parallel\n"))
  102. (let ([segments (segment 1 limit num-procs)])
  103. (let ([futures
  104. (map (λ (seg)
  105. (make-future
  106. (λ ()
  107. (display (simple-format #f "segment ~a starting\n" seg))
  108. (find-from-to (segment-start seg)
  109. (segment-end seg)))))
  110. segments)])
  111. (apply min (map touch futures))))]
  112. [else
  113. (display (simple-format #f "no limit given, running sequentially\n"))
  114. (find-from-to 1 +inf.0)])))
  115. (display
  116. (simple-format
  117. #f "~a\n"
  118. (find-triangular-number 500
  119. #:limit (* 2 (expt 10 5))
  120. #:num-procs 12)))