首页 > 代码库 > SICP 习题 (1.36)解题总结
SICP 习题 (1.36)解题总结
SICP 习题 1.36 要求我们修改fixed-point函数,使它能够打印出计算中产生的近似值序列,使用练习1.22展示的newline和display方法。而后通过找出变换x => log (1000)/log(x)的不动点的方式确定x^x=1000的一个根(书中还提示你使用Scheme的基本过程log,用于计算自然对数值)。然后比较一下使用平均阻尼和不用平均阻尼的计算步数。要注意的是不能使用1作为初始猜测去启动fixed-point,因为log(1)=0,会导致0除数错误。
从题目来看,第一步比较简单,就是加多一些输出而已,这个我们程序员在行,平时调试就经常玩这一手。第二步求x^x=1000的根问题也不是太大,前提是你完成了习题1.35,最好是看完我的1.35题解题总结。第三步是比较使用平均阻尼和不使用平均阻尼的计算步数,就是做两次调用,一次使用平均阻尼,一次不使用,然后比较计算步数,计算步数这一块因为有第一步的输出命令变得比较简单,就是在控制台数一数就好了,对于平均阻尼,理解了习题1.35后也是比较简单。
总结而言,习题1.36是比较简单那种,主要是加深你对函数不动点和平均阻尼的理解。
开始吧,第一步就是修改fixed-point函数,使它能够打印出计算中产生的近似值序列。
因为fixed-point 主要是递归调用try函数产生不断逼近的近似值,所以我们只需要在try函数中将guess值打印出来就好了。
我在try函数里加了下面两句:
(newline) (display (+ 0.0 guess))
最后结果是这样的:
(define tolerance 0.00001) (define (fixed-point f first-guess) (define (close-enough? v1 v2) (< (abs (- v1 v2 )) tolerance)) (define (try guess) (newline) (display (+ 0.0 guess)) (let (( next (f guess))) (if (close-enough? guess next) (+ 0.0 next) (try next)))) (try first-guess))
修改完以后可以用习题1.35中求黄金分割率的语句进行测试,就是:
(fixed-point (lambda (x) (+ 1 (/ 1 x))) 1)
测试结果如下,成功输出所有猜测值:
1. 2. 1.5 1.6666666666666667 1.6 1.625 1.6153846153846154 1.619047619047619 1.6176470588235294 1.6181818181818182 1.6179775280898876 1.6180555555555556 1.6180257510729614 1.6180371352785146 ;Value: 1.618032786885246
我之所以在display输出的时候加一个(+ 0.0 guess),是想让Scheme输出一个小数值,不加这个直接输出guess的话会显示一个分数形式。
接着就是第二步,寻找变换x => log (1000)/log(x)的不动点,先不使用平均阻尼,直接使用变换:
调用过程是:
(fixed-point (lambda (x) (/ (log 1000) (log x))) 2)
从2开始猜,就像题目提示的,不能用1开始猜。
运算结果如下,结果比较长,慢慢看:
1 ]=> (fixed-point (lambda (x) (/ (log 1000) (log x))) 2) 2. 9.965784284662087 3.004472209841214 6.279195757507157 3.759850702401539 5.215843784925895 4.182207192401397 4.8277650983445906 4.387593384662677 4.671250085763899 4.481403616895052 4.6053657460929 4.5230849678718865 4.577114682047341 4.541382480151454 4.564903245230833 4.549372679303342 4.559606491913287 4.552853875788271 4.557305529748263 4.554369064436181 4.556305311532999 4.555028263573554 4.555870396702851 4.555315001192079 4.5556812635433275 4.555439715736846 4.555599009998291 4.555493957531389 4.555563237292884 4.555517548417651 4.555547679306398 4.555527808516254 4.555540912917957 ;Value: 4.555532270803653
然后再来一个使用平均阻尼的,调用过程如下:
(fixed-point (lambda (x) (/ (+ (/ (log 1000) (log x)) x) 2)) 2)
就是把变换变为 x => (x + log (1000)/log(x))/2
运算结果如下,可以发现输出很快逼近我们要的结果:
1 ]=> (fixed-point (lambda (x) (/ (+ (/ (log 1000) (log x)) x) 2)) 2) 2. 5.9828921423310435 4.922168721308343 4.628224318195455 4.568346513136242 4.5577305909237005 4.555909809045131 4.555599411610624 4.5555465521473675 ;Value: 4.555537551999825
解题结束!平均阻尼术真得很强!!
有兴趣的同学还可以尝试去证明为什么变换x => log (1000)/log(x)的不动点就是x^x=1000的根,对数运算哟!