我试图通过尾递归来解决n rooks问题,因为它比标准递归更快,但是我很难弄清楚如何使它全部工作。我已经查明了这个问题背后的理论,并发现解决方案是通过一个名为“电话号码”的东西给出的,"由等式给出:
其中T(1)= 1且T(2)= 2。
我已经创建了一个递归函数来评估这个等式,但它只能快速运行到T(40),我需要它来计算n> 1000,目前根据我的估计需要几天的计算。
尾递归似乎是我最好的选择,但我希望这里有人可能知道如何使用尾递归编程这种关系,因为我不太了解它。
我在LISP工作,但愿意使用支持尾递归的任何语言
答案 0 :(得分:3)
尾递归只是一个表示为递归的循环。
当你有一个递归定义" forks"时,天真的实现很可能是指数时间,从T(n)到T(n-1)和T(n-2),那么T(n-2),T(n-3),T(n-3),T(n-4),使每一步的计算加倍。
技巧是逆转计算,以便从T(1)和T(2)构建。这只需要每个步骤的恒定时间,因此整体计算是线性的。
从
开始(let ((n 2)
(t-n 2)
(t-n-1 1))
…)
让我们使用do
循环进行更新:
(do ((n 2 (1+ n))
(t-n 2 (+ t-n (* n t-n-1)))
(t-n-1 1 t-n))
…)
现在,您需要在达到所需的n
时停止:
(defun telephone-number (x)
(do ((n 2 (1+ n))
(t-n 2 (+ t-n (* n t-n-1)))
(t-n-1 1 t-n))
((= n x) t-n)))
要完成,请检查您的输入:
(defun telephone-number (x)
(check-type x (integer 1))
(if (< x 3)
x
(do ((n 2 (1+ n))
(t-n 2 (+ t-n (* n t-n-1)))
(t-n-1 1 t-n))
((= n x) t-n))))
此外,还要编写测试并添加文档,以及如何使用它。这还没有经过考验。
当写这个尾递归时,你用新值递归:
(defun telephone (x)
(labels ((tel-aux (n t-n t-n-1)
(if (= n x)
t-n
(tel-aux (1+ n)
(+ t-n (* n t-n-1))
t-n))))
(tel-aux 2 2 1)))
当优化尾递归时,它会像循环一样扩展(但常数因子可能不同)。请注意,Common Lisp不要求尾部调用优化。