074.scm 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. ;;; Digit factorial chains
  2. ;; TODO: modify names (! for state), add comments, move useful proc to utils.scm
  3. ;; Used features: hash-table, define*, match
  4. (use-modules (srfi srfi-1)
  5. (ice-9 match)
  6. (euler utils)
  7. (euler math))
  8. (define (find-n-digit-factorial-chain-under-k n k)
  9. (fold (lambda (digit acc)
  10. (if (= n (get-chain-length digit))
  11. (cons (get-val digit) acc)
  12. acc))
  13. '()
  14. (get-factorial-chain-digits-under k)))
  15. (define (get-factorial-chain-digits-under k)
  16. (do [(i 1 (1+ i))]
  17. [(>= i k) (filter-map->list/w-digits-under k)]
  18. (find-cycle-loop i '())))
  19. (define (filter-map->list/w-digits-under k)
  20. (hash-fold
  21. (lambda (key digit acc)
  22. (if (< (get-val digit) k)
  23. (cons digit acc)
  24. acc))
  25. '()
  26. digit-map))
  27. (define digit-map (make-hash-table 100))
  28. (define (find-cycle-loop curr-val curr-chain)
  29. (let ((digit? (hashq-ref digit-map curr-val)))
  30. (match digit?
  31. [#f ; -> not seen yet
  32. (let ([digit (make-digit curr-val)])
  33. (hashq-set! digit-map curr-val digit)
  34. (find-cycle-loop (get-fact-sum digit)
  35. (cons digit curr-chain)))]
  36. [(_ _ #f) ; -> seen before in current loop
  37. (update-chain-and-cycle digit? curr-chain)]
  38. [(_ _ chain-length) ; -> seen before in a previous loop
  39. (update-chain curr-chain chain-length)])))
  40. (define (update-chain-and-cycle digit curr-chain)
  41. (call-with-values
  42. (lambda ()
  43. (split-at curr-chain
  44. (1+ (find-index curr-chain digit))))
  45. (lambda (cycle chain)
  46. (update-cycle cycle)
  47. (update-chain chain (length cycle)))))
  48. ;; setting chain-length for values in the chain's cycle
  49. (define (update-cycle cycle)
  50. (let ([cycle-length (length cycle)])
  51. (update-digits cycle
  52. (make-list cycle-length cycle-length))))
  53. ;; setting chain-length for values in the chain before a cycle
  54. (define (update-chain chain cycle-length)
  55. (update-digits chain (iota (length chain) (1+ cycle-length))))
  56. (define (update-digits digits chain-lengths)
  57. (for-each
  58. (lambda (digit chain-length)
  59. (hashq-set! digit-map
  60. (get-val digit)
  61. (set-chain-length digit chain-length)))
  62. digits chain-lengths))
  63. ;; Definitions for digit structure
  64. (define* (make-digit n #:optional (fact-sum (factorial-sum n)) (chain-length #f))
  65. (list n fact-sum chain-length))
  66. (define (factorial-sum n)
  67. (reduce + 0
  68. (map factorial (number->digits n))))
  69. (define (get-chain-length digit)
  70. (last digit))
  71. (define (set-chain-length digit chain-length)
  72. (make-digit (get-val digit)
  73. (get-fact-sum digit)
  74. chain-length))
  75. (define (get-fact-sum digit)
  76. (cadr digit))
  77. (define (get-val digit)
  78. (car digit))
  79. ;; For testing individual numbers
  80. (define (t i)
  81. (hash-clear! digit-map)
  82. (find-cycle-loop i '())
  83. (hashq-ref digit-map i))