解决" n-rooks"尾递归

时间:2016-05-03 22:56:57

标签: recursion lisp common-lisp tail-recursion gnu-common-lisp

我试图通过尾递归来解决n rooks问题,因为它比标准递归更快,但是我很难弄清楚如何使它全部工作。我已经查明了这个问题背后的理论,并发现解决方案是通过一个名为“电话号码”的东西给出的,"由等式给出:

Telephone/N-rooks relation

其中T(1)= 1且T(2)= 2。

我已经创建了一个递归函数来评估这个等式,但它只能快速运行到T(40),我需要它来计算n> 1000,目前根据我的估计需要几天的计算。

尾递归似乎是我最好的选择,但我希望这里有人可能知道如何使用尾递归编程这种关系,因为我不太了解它。

我在LISP工作,但愿意使用支持尾递归的任何语言

1 个答案:

答案 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不要求尾部调用优化。