方案:递归过程比迭代快得多

时间:2019-08-18 11:06:44

标签: scheme lisp sicp

我正在研究SICP,并编写了两个过程来计算1 / n ^ 2的总和,第一个过程生成一个递归过程,第二个过程生成一个迭代过程:

(define (sum-rec a b)
  (if (> a b)
      0
      (exact->inexact (+ (/ 1 (* a a)) (sum-rec (1+ a) b)))))

(define (sum-it a b)
  (define (sum_iter a tot)
    (if (> a b)
        tot
        (sum_iter (1+ a) (+ (/ 1 (* a a)) tot))))
  (exact->inexact (sum_iter a 0)))

我测试了两个过程在使用较小值b调用时给出的结果完全相同,并且随着b的增大,结果接近$ pi ^ 2/6 $。

但是令人惊讶的是,调用(sum-rec 1 250000)几乎是瞬时的,而调用(sum-it 1 250000)却要花很长时间。

对此有解释吗?

2 个答案:

答案 0 :(得分:5)

如评论中所述,sum-it的当前形式是使用精确算术加数字,这比sum-rec中使用的不精确算术要慢。要进行等效比较,这是您应如何实现的方式:

(define (sum-it a b)
  (define (sum_iter a tot)
    (if (> a b)
        tot
        (sum_iter (1+ a) (+ (/ 1.0 (* a a)) tot))))
  (sum_iter a 0))

请注意,将1替换为1.0会强制解释器使用不精确的算术。现在,这将立即返回:

(sum-it 1 250000)
=> 1.6449300668562465

答案 1 :(得分:0)

您可以重新构造这两个版本,以便仅通过控制它们使用零表示什么值并依靠感染规则,就可以正确地执行精确或不精确的算术运算。这两个在Racket中,默认情况下没有1+,但是对于默认值的可选参数确实有很好的语法:

(define (sum-rec low high (zero 0.0))
  (let recurse ([i low])
    (if (> i high)
      zero
      (+ (/ 1 (* i i)) (recurse (+ i 1))))))

(define (sum-iter low high (zero 0.0))
  (let iterate ([i low] [accum zero])
    (if (> i high)
        accum
        (iterate (+ i 1) (+ (/ 1 (* i i)) accum)))))

这样做的好处是您可以轻松查看两个版本的性能差异。缺点是您需要一个真正聪明的编译器才能在此处优化数值运算(我想,即使知道lowhigh是机器整数,它也必须推断出zero将是某种数字类型,并为所有可能的类型生成函数主体的副本。