Problem 92 - コラッツみたいな

メモ化が無いと辛そうかなと思ってたけど、メモ化しても辛かった。

Problem 92 - PukiWiki

各桁の数を2乗して足し合わせて新たな数を作ることによって、作られる数字の列を考える。

例えば
  44 → 32 → 13 → 10 → 1 → 1
  85 → 89 → 145 → 42 → 20 → 4 → 16 → 37 → 58 → 89
のような列である。結果として1か89で無限ループに陥っている。
ここで、ある数から始めた列は1か89に到達することが分かっている。

では、10,000,000より小さい数で89に到達する数はいくつあるか。

なんかの数を分割して、二乗して足すと、1か89に収束する。

ふむふむ。コラッツ予想みたいな感じだね。


コラッツ不思議だ(これが言いたかっただけ)


で、89に到達する数を数えるんだけど、10,000,000^n回は絶対計算したくない訳。

そこで、メモ化が必要だと考えて、SICPを読み進めたんだけど、これでもかなりきつい。

(require (lib "69.ss" "srfi"))

(define (number->list n . base)
  (letrec ((b (if (null? base) 10 (car base)))
           (iter (lambda (acc n)
                   (let ((q (quotient n b))
                         (m (cons (modulo n b) acc)))
                        (if (zero? q)
                            m
                            (iter m q))))))
          (iter '() n)))


(define (problem92 n)
  (letrec ((square (lambda (n) (* n n)))
           (memoize (lambda (f)
                      (let ((table (make-hash-table)))
                        (lambda x
                          (let ((prev-result (and (hash-table-exists? table x)
                                                  (hash-table-ref table x))))
                            (or prev-result
                                (let ((result (apply f x)))
                                  (hash-table-set! table x result)
                                  result)))))))
           (collatz-like (memoize
                           (lambda (n)
                             (cond ((= n 1) 0)
                                   ((= n 89) 1)
                                   (else
                                     (collatz-like (apply + (map square (number->list n)))))))))
           (iter (lambda (n acc)
                   (if (zero? n)
                       acc
                       (iter (- n 1) (+ (collatz-like n) acc))))))
    (iter n 0)))

(problem92 100000)   ; 85623
(problem92 1000000)  ; 856929
(problem92 10000000) ; 8581146

一瞬でメモリを食い尽くす。お・恐ろしいぜ。

8割位は89に到達するっぽいので、何か法則があるっぽい。