私はLisp初心者です。Collatz列の項数を計算するための再帰関数をメモしようとしています ( Project Eulerの問題 14 用)。私のコードはまだです:
(defun collatz-steps (n)
(if (= 1 n) 0
(if (evenp n)
(1+ (collatz-steps (/ n 2)))
(1+ (collatz-steps (1+ (* 3 n)))))))
(defun p14 ()
(defvar m-collatz-steps (memoize #'collatz-steps))
(let
((maxsteps (funcall m-collatz-steps 2))
(n 2)
(steps))
(loop for i from 1 to 1000000
do
(setq steps (funcall m-collatz-steps i))
(cond
((> steps maxsteps)
(setq maxsteps steps)
(setq n i))
(t ())))
n))
(defun memoize (fn)
(let ((cache (make-hash-table :test #'equal)))
#'(lambda (&rest args)
(multiple-value-bind
(result exists)
(gethash args cache)
(if exists
result
(setf (gethash args cache)
(apply fn args)))))))
memoize 関数は、 On Lispブックで提供されているものと同じです。
このコードは、メモ化されていないバージョンと比較して実際にはスピードアップしません。これは、関数のメモ化されていないバージョンを呼び出す再帰呼び出しが原因であると考えられますが、これは一種の目的を無効にします。その場合、ここでメモ化を行う正しい方法は何ですか? 元の関数へのすべての呼び出しでメモ化されたバージョン自体を呼び出して、特別な m-collatz-steps シンボルの必要性をなくす方法はありますか?
編集:コードを修正しました
(defvar m-collatz-steps (memoize #'collatz-steps))
これは私のコードにあったものです。編集の前に、私が誤って入れたもの:
(defvar collatz-steps (memoize #'collatz-steps))
そのエラーを見て別のアイデアが浮かび、この最後の defvar 自体を使用して、再帰呼び出しを次のように変更してみました
(1+ (funcall collatz-steps (/ n 2)))
(1+ (funcall collatz-steps (1+ (* 3 n))))
これはメモ化 (約 60 秒から 1.5 秒への高速化) を実行しているように見えますが、元の関数を変更する必要があります。元の機能を変更する必要のない、よりクリーンなソリューションはありますか?