【发布时间】:2010-09-20 08:49:54
【问题描述】:
我是 Lisp 初学者。我正在尝试记忆一个递归函数,用于计算Collatz sequence 中的项数(对于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 秒),但需要更改原始功能。是否有不涉及更改原始功能的更清洁的解决方案?
【问题讨论】:
标签: lisp common-lisp memoization