我正在研究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)
却要花很长时间。
对此有解释吗?
答案 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)))))
这样做的好处是您可以轻松查看两个版本的性能差异。缺点是您需要一个真正聪明的编译器才能在此处优化数值运算(我想,即使知道low
和high
是机器整数,它也必须推断出zero
将是某种数字类型,并为所有可能的类型生成函数主体的副本。