我想知道这是否是此功能的最快版本。
(defun foo (x y)
(cond
;if x = 0, return y+1
((zp x) (+ 1 y))
;if y = 0, return foo on decrement x and 1
((zp y) (foo (- x 1) 1))
;else run foo on decrement x and y = (foo x (- y 1))
(t (foo (- x 1) (foo x (- y 1))))))
当我运行它时,我经常会出现堆栈溢出错误,所以我试图找出一种计算类似(foo 3 1000000)的方法,而不使用计算机。
从分析函数我认为它是在导致溢出的递归情况下嵌入foo(foo 3 1000000)。但是,既然你在递减y,那么步数就等于y?
编辑:从评论中删除谎言
答案 0 :(得分:3)
12年前我写了这个:
(defun ackermann (m n)
(declare (fixnum m n) (optimize (speed 3) (safety 0)))
(let ((memo (make-hash-table :test #'equal))
(ncal 0) (nhit 0))
(labels ((ack (aa bb)
(incf ncal)
(cond ((zerop aa) (1+ bb))
((= 1 aa) (+ 2 bb))
((= 2 aa) (+ 3 (* 2 bb)))
((= 3 aa) (- (ash 1 (+ 3 bb)) 3))
((let* ((key (cons aa bb))
(val (gethash key memo)))
(cond (val (incf nhit) val)
(t (setq val (if (zerop bb)
(ack (1- aa) 1)
(ack (1- aa) (ack aa (1- bb)))))
(setf (gethash key memo) val)
val)))))))
(let ((ret (ack m n)))
(format t "A(~d,~d)=~:d (~:d calls, ~:d cache hits)~%"
m n ret ncal nhit)
(values ret memo)))))
正如您所看到的,我使用了一个明确的小a
公式和更大a
的记忆。
但请注意,此函数增长得如此之快,以至于尝试计算实际值没有多大意义;你会更快地耗尽宇宙中的原子 - 记忆与否。
答案 1 :(得分:2)
从概念上讲,堆栈溢出与速度无关,但它们与 space 的使用有关。例如,请考虑length
的以下实现。第一个将进入长列表的堆栈溢出。第二个也是,除非你的Lisp实现尾调用优化。第三个不会。但是,它们具有相同的时间复杂度(速度);它们在列表的长度上是线性的。
(defun length1 (list)
(if (endp list)
0
(+ 1 (length1 (rest list)))))
(defun length2 (list)
(labels ((l2 (list len)
(if (endp list)
len
(l2 (rest list) (1+ len)))))
(l2 list 0)))
(defun length3 (list)
(do ((list list (rest list))
(len 0 (1+ len)))
((endp list) len)))
你可以为你的代码做类似的事情,尽管你仍然会有一个递归调用,这将有助于堆栈空间。由于这似乎是Ackermann function,我将使用zerop
代替zp
和ack
而不是foo
。因此,您可以这样做:
(defun foo2 (x y)
(do () ((zp x) (+ 1 y))
(if (zp y)
(setf x (1- x)
y 1)
(psetf x (1- x)
y (foo x (1- y))))))
由于x
在每次迭代时减少1
,并且唯一的条件更改在y
,因此您可以将其简化为:
(defun ack2 (x y)
(do () ((zerop x) (1+ y))
(if (zerop y)
(setf x (1- x)
y 1)
(psetf x (1- x)
y (ack2 x (1- y))))))
由于y
是迭代期间唯一有条件改变的东西,因此您可以进一步将其简化为:
(defun ack3 (x y)
(do ((x x (1- x))
(y y (if (zerop y) 1 (ack3 x (1- y)))))
((zerop x) (1+ y))))
这是一项昂贵的计算功能,这会让你更进一步,但你仍然无法获得,例如(ackN 3 1000000)
。所有这些定义都可以从http://pastebin.com/mNA9TNTm轻松复制和粘贴。
答案 2 :(得分:2)
通常,memoization是您在此类计算中的朋友。可能不适用,因为它取决于递归中的特定参数;但这是一种有用的探索方法。