【问题标题】:Solving "n-rooks" with tail recursion用尾递归解决“n-rooks”
【发布时间】:2016-08-29 04:25:48
【问题描述】:

我试图用尾递归来解决 n rooks 问题,因为它比标准递归更快,但我无法弄清楚如何让它全部工作。我查看了这个问题背后的理论,发现解决方案是由一个叫做“电话号码”的东西给出的,它由以下等式给出:

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

我创建了一个递归函数来评估这个方程,但它只能在 T(40) 之前快速运行,我需要它来计算 n > 1000 的位置,目前据我估计需要数天的计算时间。

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

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

【问题讨论】:

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


    【解决方案1】:

    尾递归只是表示为递归的循环。

    当你有一个“分叉”的递归定义时,天真的实现很可能是时间指数,从 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 不强制执行尾调用优化。

    【讨论】:

    • 这太棒了!但由于某种原因,输出数字看起来不像预期的那样。 (即(电话 10)应为 9496,但此代码为 1890。)
    • @user3745602,正确的版本如下:(defun telephone-number (x) (check-type x (integer 1)) (if (&lt; x 3) x (do ((n 3 (1+ n)) (t-n-1 1 t-n) (t-n 2 (+ t-n (* t-n-1 (1- n))))) ((= n (1+ x)) t-n))))
    • @Renzo:你应该让n跟踪当前的n,所以从2开始,乘以n,比较nx。感谢您的更正,我会编辑(但仍未经测试)。
    猜你喜欢
    • 2020-07-13
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 2016-03-21
    • 1970-01-01
    • 2014-06-26
    • 1970-01-01
    • 2017-10-10
    相关资源
    最近更新 更多