Recursion 如何改进这段代码?

Recursion 如何改进这段代码?,recursion,scheme,sicp,Recursion,Scheme,Sicp,我的SICP解决方案是: (define (f n) (if (< n 3) n (+ (f (- n 1)) (* 2 (f (- n 2))) (* 3 (f (- n 3)))) )) (定义(f n) (如果(

我的SICP解决方案是:

(define (f n)
  (if (< n 3)
   n
   (+ (f (- n 1)) (* 2 (f (- n 2))) (* 3 (f (- n 3))))
   ))
(定义(f n)
(如果(

正如预期的那样,像(f 100)这样的评估需要很长时间。我想知道是否有一种方法可以改进这段代码(不需要前面提到递归),和/或利用多核box。我使用的是“mit scheme”。

我不确定如何最好地在scheme中对其进行编码,但在类似的事情上提高速度的一种常见技术是使用。简而言之,其思想是缓存f(p)的结果(可能是看到的每一个p,或者可能是最后的n个值),以便下次调用f(p)时,返回保存的结果,而不是重新计算。通常,缓存是从元组(表示输入参数)到返回类型的映射。

有关使用函数编程开发快速斐波那契函数的良好教程,请参阅。它使用CommonLisp,这在某些方面与Scheme略有不同,但您应该能够使用它。您的实现相当于文件顶部附近的
bogo-fig
函数。

此练习告诉您编写两个函数,一个“通过递归过程”计算
f
,另一个“通过迭代过程”计算
f
。你做了一个递归的。由于此函数与链接部分示例中给出的
fib
函数非常相似,您应该能够通过查看
fib
函数的递归和迭代示例来了解这一点:

; Recursive
(define (fib n)
  (cond ((= n 0) 0)
        ((= n 1) 1)
        (else (+ (fib (- n 1))
                 (fib (- n 2))))))

; Iterative
(define (fib n)
  (fib-iter 1 0 n))

(define (fib-iter a b count)
  (if (= count 0)
      b
      (fib-iter (+ a b) a (- count 1))))
在这种情况下,您将定义一个
f-iter
函数,该函数将采用
a
b
c
参数以及
count
参数

这是f-iter的
。请注意与fib iter的相似性:

(define (f-iter a b c count)
  (if (= count 0)
      c
      (f-iter (+ a (* 2 b) (* 3 c)) a b (- count 1))))
通过一些尝试和错误,我发现
a
b
c
应该分别初始化为
2
1
0
,这也遵循了
fib
函数初始化
a
b
1
0
的模式。所以
f
看起来像这样:

(define (f n)
  (f-iter 2 1 0 n))

注:
f-iter
仍然是一个递归的函数,但由于Scheme的工作方式,它作为一个迭代的过程运行,并在
O(n)
时间和
O(1)
空间中运行,这与您的代码不同,它不仅是一个递归函数,而且是一个递归过程。我相信这就是练习1.1的作者想要的。

好吧,如果你问我,像数学家一样思考。我看不懂scheme,但如果你在编写一个斐波那契函数,而不是递归地定义它,解决这个递归,并用一个封闭的形式定义它。例如,对于斐波那契序列,可以找到闭合形式。那会快得多

编辑:哎呀,没看到你们说放弃递归。在这种情况下,您的选择将非常有限。

换句话说:

要获得尾部递归,递归调用必须是过程所做的最后一件事

递归调用嵌入在*和+表达式中,因此它们不是尾部调用(因为*和+是在递归调用之后计算的。)

Jeremy Ruten的
f-iter
版本是尾部递归而非迭代的(即,它看起来像一个递归过程,但与迭代等价物一样有效。)

但是,您可以使迭代显式:

(define (f n)
  (let iter
    ((a 2) (b 1) (c 0) (count n))
    (if (<= count 0)
      c
      (iter (+ a (* 2 b) (* 3 c)) a b (- count 1)))))
(定义(f n)
(让我们看看国际热核实验堆
((a2)(b1)(c0)(计数n))

(如果(这个特定的练习可以通过使用尾部递归来解决,而不是等待每个递归调用返回(就像您介绍的简单解决方案中的情况一样),您可以在参数中累积答案,以使递归在其占用的空间方面与迭代的行为完全相同。例如:

(define (f n)
  (define (iter a b c count)
    (if (zero? count)
        c
        (iter (+ a (* 2 b) (* 3 c))
              a
              b
              (- count 1))))
  (if (< n 3)
      n
      (iter 2 1 0 n)))
(定义(f n)
(定义(iter a b c计数)
(如果(零?计数)
C
(国际热核实验堆(+a(*2 b)(*3 c))
A.
B
(-count 1)))
(如果(
Argh。难怪我不能得到结果……我在向后旋转结果——我的递归调用是(f-iter(*…)b a…)不是b>\u Hello Jeremy,谢谢你帮我解决了这个问题。但是,下面Charlie建议了我要找的。在scheme中,为了这个目的定义局部函数也是惯用的,尤其是在编写循环时使用命名let。例如:(define(fib n)(let fib iter((a 1)(b 0)(count n))(if(=count 0)b(fib iter(+ab)a(-count 1‘‘)’))你好,杰克。即使没有“编辑”,谢谢你的建议。我一直在重新学习“重现方程”。你的建议帮助我对它进行了更多的思考。
(define (f n)
  (define (iter a b c count)
    (if (zero? count)
        c
        (iter (+ a (* 2 b) (* 3 c))
              a
              b
              (- count 1))))
  (if (< n 3)
      n
      (iter 2 1 0 n)))