Recursion 解决;“n-rooks”;带尾部递归
我试图用尾部递归来解决n rooks问题,因为它比标准递归快,但是我很难弄清楚如何使它全部工作。我查阅了这个问题背后的理论,发现解决方案是由一个叫做“电话号码”的东西给出的,它由以下等式给出: 其中T(1)=1,T(2)=2 我已经创建了一个递归函数来计算这个方程,但是它只在T(40)之前快速工作,我需要它来计算其中的n>1000,根据我的估计,目前需要几天的计算时间 尾部递归似乎是我最好的选择,但我希望这里的人可能知道如何使用尾部递归编程这个关系,因为我并不真正理解它Recursion 解决;“n-rooks”;带尾部递归,recursion,lisp,common-lisp,tail-recursion,gnu-common-lisp,Recursion,Lisp,Common Lisp,Tail Recursion,Gnu Common Lisp,我试图用尾部递归来解决n rooks问题,因为它比标准递归快,但是我很难弄清楚如何使它全部工作。我查阅了这个问题背后的理论,发现解决方案是由一个叫做“电话号码”的东西给出的,它由以下等式给出: 其中T(1)=1,T(2)=2 我已经创建了一个递归函数来计算这个方程,但是它只在T(40)之前快速工作,我需要它来计算其中的n>1000,根据我的估计,目前需要几天的计算时间 尾部递归似乎是我最好的选择,但我希望这里的人可能知道如何使用尾部递归编程这个关系,因为我并不真正理解它 我使用的是LISP,但
我使用的是LISP,但我愿意使用任何支持尾部递归的语言。尾部递归只是一个表示为递归的循环 当您有一个递归定义“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))))
当尾部递归被优化时,它会像循环一样缩放(但常数因子可能不同)。请注意,Common Lisp并不要求尾部调用优化。这太棒了!但由于某些原因,输出数据似乎与预期不符。(即,(电话10)应为9496,但此代码为1890。)@user3745602,正确的版本如下:
(定义电话号码(x)(检查类型x(整数1))(如果(
@Renzo:您应该保持n
跟踪当前n
,因此从2开始,乘以n
,将n
与x
进行比较。感谢您的更正,我将进行编辑(尽管尚未测试)。
(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)))