本人做的SICP习题第1章,如有错误请指正,用的解释器是Racket

练习1.1

计算代码如下

;; Exercise 1.1
#lang racket
10(+ 5 3 4)(- 9 1)(/ 6 2)(+ (* 2 4) (- 4 6))(define a 3)(define b (+ a 1))(+ a b (* a b))(= a b)(if (and (> b a) (< b (* a b)))ba)(cond ((= a 4) 6)((= b 4) (+ 6 7 a))(else 25))(+ 2 (if (> b a) b a))(* (cond ((> a b) a)((< a b) b)(else -1))(+ a 1))

10

12

8

3

6

a

b

19

#f

4

16

6

16

练习1.2

;; Exercise 1.2
#lang racket(/ (+ 5 4 (- 2 (- 3 (+ 6 (/ 4 5)))))(* 3 (- 6 2) (- 2 7))

答案为-37/150

练习1.3

;; Exercise 1.3
;; 返回三个数中较大两个数的平方和
#lang racket;; 返回两个输入的较小数
(define (min a b)(if (< a b)ab));; 返回三个数中最小的一个
(define (min-three a b c)(min (min a b)(min b c)));; 求平方
(define (square x)(* x x));; 返回三个数中较大两个数的平方和
;; 先计算三个数的平方和,再减去最小数的平方
(define (two-larger-square-sum a b c)(- (+ (square a) (square b) (square c))(square (min-three a b c))))

练习1.4

讲道理嘛,这个看函数名儿都能看出来

不过这里可以看出,操作符也可以作为一个combination的返回值

;; Exercise 1.4
#lang racket(define (a-plus-abs-b a b)((if (> b 0) + -) a b))

a + abs(b)

练习1.5

这题我卡了非常久,第一次做的时候没有仔细想就随便过去了,做到后面1.20的时候觉得不对,又回来看1.5和1.6两题,然后画了一天理解什么是应用序和正则序,具体的思路整理在另一篇博客里刷SICP遇到的问题——深入学习理解正则序和应用序

看一下题目里的这段代码

;; Exercise 1.5
#lang racket;; 定义一个死循环递归
(define (p) (p));; 检测解释器是应用序还是正则序
(define (test x y)(if (= x 0)0y))(test 0 (p))

(define (p) (p))定义了一个死循环函数,这是一个最简单的递归,所以一旦求解(p)这个表达式,就是进入这个递归死循环

如果是应用序,在执行(test 0 (p))的时候就会去求解p,我试了MIT-Scheme和Racket两个解释器,都会卡住,显然都是应用序

如果是正则序,会正常返回0,在执行(test 0 (p))后就替换为(if (= 0 0) 0 (p)),条件表达式满足,(p)就被忽略掉了,所以会正常返回0,我试了lazyracket这个惰性求值的解释器(实在找不到正则序的),可以返回0。切换到lazyracket很简单,开头那里定义成#lang lazy就行

练习1.6

这段代码

;; Exercise 1.6
#lang racket;; 定义新if
(define (new-if predicate then-clause else-clause)(cond (predicate then-clause)(else else-clause)));; 求平方
(define (square x)(* x x));; 检测猜测值精度
(define (good-enough? guess x)(< (abs (- (square guess) x)) 0.001));; 牛顿法改进猜测值
(define (improve guess x)(average guess (/ x guess)));; 求平均值
(define (average x y)(/ (+ x y) 2));; 不断改进猜测值直到精度满足需求
(define (sqrt-iter guess x)(begin(new-if (good-enough? guess x)guess(sqrt-iter (improve guess x) x))));; 牛顿法求平方根
(define (sqrt x)(sqrt-iter 1.0 x))

对于应用序,必然会卡死啊,在执行一个new-if的时候求解每个参数的值,这样就陷入了sqrt-iter的无限递归

对于正则序,展开一个看看,(sqrt-iter 1.0 9)展开为

(cond ((good-enough? 1.0 9) guess)
          (else (sqrt-iter (improve guess x)
                                  x))))

后面的不展开了,如果cond也是特殊处理过的,所以在某次good-enough?返回为真的情况下,不再继续往下展开sqrt-iter,程序可以正确执行,所以我猜测在正则序下这个代码是可以运行的

验证一下,用lazyracket

妥妥滴

练习1.7

对于很小的数,精度不足,这个非常好理解

对于很大的数,会卡死,因为浮点数的位数是有限的,无法达到0.001这么小的精度差,比如2130895720398745908273049857902374590723904570237409572394750937252394892817059712093847098129034709823904812093875091620934213089572039874590827304985790237459072390457023740957239475093725这个数就会在原来的sqrt程序那里卡死

我知道怎么退出了,在运行窗口连按两个C-c

改进good-enough?,监视猜测值guess,如果improve出的新guess和原guess差小于某个值就停止迭代,代码如下

;; Exercise 1.7
;; 改进的牛顿迭代法
#lang racket;; 平方
(define (square x)(* x x));; 检测猜测值精度,当猜测值和上一次猜测值相比改进小于0.1%时停止迭代
(define (good-enough? old-guess guess)(< (abs (- old-guess guess)) (* guess 0.001)));; 改进猜测值
(define (improve guess x)(average guess (/ x guess)));; 平均值
(define (average x y)(/ (+ x y) 2));; 牛顿迭代法
(define (sqrt-iter old-guess guess x)(begin(if (good-enough? old-guess guess)guess(sqrt-iter guess (improve guess x) x))));; 改进的求平方根
(define (better-sqrt x)(sqrt-iter 0.0 1.0 x))

练习1.8

代码如下

;; Exercise 1.8
#lang racket;; 立方
(define (cube x)(* x x x));; 猜测值精度检测
(define (good-enough? guess x)(< (abs (- (cube guess) x)) 0.001));; 迭代改进猜测值
(define (improve guess x)(/ (+ (/ x (* guess guess)) (* 2 guess)) 3));; 牛顿迭代法
(define (cube-root-iter guess x)(begin(if (good-enough? guess x)guess(cube-root-iter (improve guess x) x))));; 求立方根
(define (cube-root x)(cube-root-iter 1.0 x))(cube-root 729)

练习1.9

第一个程序

;; Exercise1.9
;; 递归加法
#lang racket;; 减一
(define (dec a)(- a 1));; 加一
(define (inc a)(+ a 1));; 递归加法
(define (add a b)(if (= a 0)b(inc (add (dec a) b))))

展开:

(+ 4 5)

(inc (+ 3 5))

(inc (inc (+ 2 5)))

(inc (inc (inc (1 5))))

(inc (inc (inc (inc 5))))

递归

第二个程序

;; Exercise1.9
;; 循环加法
#lang racket;; 减一
(define (dec a)(- a 1));; 加一
(define (inc a)(+ a 1));; 循环加法
(define (add a b)(if (= a 0)b(add (dec a) (inc b))))

展开:

(+ 4 5)

(+ 3 6)

(+ 2 7)

(+ 1 8)

(+ 0 9)

循环

练习1.10

代码如下,用于验证计算结果

;; Exercise 1.10
;; 阿克曼函数
#lang racket;; 阿克曼函数
(define (A x y)(cond ((= y 0) 0)((= x 0) (* 2 y))((= y 1) 2)(else (A (- x 1)(A x (- y 1))))))

(A 1 10) :

(A 1 10) = (A 0 (A 1 9)) = (* 2 (A 1 9))

(A 1 9) = (A 0 (A 1 8)) = (* 2 (A 1 8))

以此类推 (A 1 10) = (* 512 (A 1 1)) = 1024

因此可以看出来(A 1 n)就是2^n

(A 2 4):

(A 2 4) = (A 1 (A 2 3))

(A 2 3) = (A 1 (A 2 2))

(A 2 2) = (A 1 (A 2 1))

所以(A 2 4) = 2^(2^(2^2))) = 65536

可以看出来(A 2 n)就是n个2这样 2^(2^(2^……)))

(A 3 3):

(A 3 3) = (A 2 (A 3 2)))

(A 3 2) = (A 2 (A 3 1)))  = (A 2 2) = 4

所以(A 3 3) = (A 2 4) = 65536

(define (f n) (A 0 n)):

(A 0 n) = (* 2 n) = 2n

(define (g n) (A 1 n)):

(A 1 n) = (A 0 (A 1 (n -1))) = (* 2 (A 1 (n - 1))) = …… = 2^(n-1) * (A 1 1) = 2^n

(define (h n) (A 2 n)):

(A 2 n) = (A 1 (A 2 (n - 1))) = 2^(A 2 (n - 1)) = …… = 2^(2^(2^……(A 2 1)))  = 2^(2^(2^……)))(共n个2)

(define (k n) (* 5 n n)):

5n^2啦

练习1.11

递归的代码

;; Exercise 1.11
;; 递归求f函数值
#lang racket;; 递归求f
(define (f n)(cond ((< n 3) n)(else (+ (f (- n 1)) (* 2 (f (- n 2))) (* 3 (f (- n 3)))))))

迭代的代码

;; Exercise 1.11
;; 迭代求f函数值
#lang racket;; 迭代求f
(define (f-iterative f1 f2 f3 n)(cond ((< n 3) n)((= n 3) f1)(else (f-iterative (+ f1 (* 2 f2) (* 3 f3)) f1 f2 (- n 1)))));; 求f
(define (f n)(f-iterative 4 2 1 n))

练习1.12

row和col从1开始,严格来说这个函数是不严谨的,没有考虑输入不合法的情况

;; Exercise 1.12
;; 求pascal三角中的数值
#lang racket;; 求第row行第col列的pascal三角数值
(define (pascal row col)(cond ((or (= col 1) (= col row)) 1)(else (+ (pascal (- row 1) col) (pascal (- row 1) (- col 1))))))

练习1.13

先用数学归纳法证明,在我遥远模糊的记忆中数学归纳法的格式差不多应该是这样,如果不符合规范,反正是我初中老师就这么教的

1° 当n = 0,代入数值计算可得

当n = 1,代入数值计算可得

2° 假设存在

以及

则有

3° 由1°、2°,可得

公式真难写,赶快保存一下

现在证明最接近的整数是

计算可得,因此

由二项式的展开式可得,必然不是整数

一个非整数减去一个绝对值小于0.5的非整数,所得到的整数必然是最接近这个非整数的整数嘛(取整有两个方向,向上取整或向下取整,现在朝一个取整方向的距离小于0.5,那到另一个取整方向的距离必然大于0.5了)

练习1.14

我非常无聊地真的画了出来,第一次画完发现,我把硬币的大小弄反了,题目代码是从大到小,我变成从小到大,导致多了非常多步骤,不管,错误的图我也要贴出来

底下这张是正确的,我并没有检查,应该也没有无聊的人会去检查,一共标红的4种情况是可以的

space是,step是

练习1.15

a. ,所以调用了4次p函数

b. space和step都是

练习1.16

;; Exercise 1.16
;; 迭代求幂
#lang racket
(provide (all-defined-out));; 迭代求幂
(define (exp-iter a b n)(if (= n 0)a(if (even n) (exp-iter a (square b) (/ n 2))(exp-iter (* a b) b (- n 1)))));; 快速求幂
(define (fast-exp b n)(exp-iter 1 b n));; 判断是否为偶数
(define (even n)(= (remainder n 2) 0));; 平方
(define (square x)(* x x))

练习1.17

;; Exercise 1.17
;; 快速乘法
#lang racket;; 倍增
(define (double x)(+ x x));; 减半
(define (halve x)(/ x 2));; 检测是否为偶数
(define (even x)(= (remainder x 2) 0));; 快速乘法
(define (fast-mult a b)(cond ((= b 0) 0);; 处理b为负的情况((< b 0) (- 0 (fast-mult a (- 0 b))))((even b) (double (fast-mult a (halve b))))(else (+ a (fast-mult a (- b 1))))))

练习1.18

递归改循环很简单,不要对递归调用的返回值做额外计算就是迭代了

;; Exercise 1.18
;; 迭代快速乘法
#lang racket;; 倍增
(define (double x)(+ x x));; 减半
(define (halve x)(/ x 2));; 检测是否为偶数
(define (even x)(= (remainder x 2) 0));; 快速乘法
(define (fast-mult-iterative a b)(cond ((= b 0) 0);; 处理b为负的情况((< b 0) (- 0 (fast-mult-iterative a (- 0 b))));; 改递归为迭代((even b) (fast-mult-iterative (double a) (halve b)))(else (+ a (fast-mult-iterative a (- b 1))))))

练习1.19

首先证明,变换

两次变换相当于一次变换

则一次变换相当于两次变换,其中

对于裴波那契数,则有

相当于变换,两次变换可以用一次变换代替,即

代码如下

;; Exercise 1.19
;; 快速求解裴波那契数
#lang racket;; 求解裴波那契数
(define (fast-fib n)(fib-iter 1 0 0 1 n));; 迭代求解
(define (fib-iter a b p q count)(cond ((= count 0) b)((even? count)(fib-iter ab(+ (square p) (square q))(+ (* 2 p q) (square q))(/ count 2)))(else (fib-iter (+ (* b q) (* a q) (* a p))(+ (* b p) (* a q))pq(- count 1)))));; 平方
(define (square x)(* x x));; 判断是否为偶数
(define (even? x)(= (remainder x 2) 0))

练习1.20

正则序会比应用序多调用remainder灰常多次,因为a、b在正则序里都会展开为remainder求值

用下面这段程序在racket和MIT-Scheme两个解释器上测试

;; Exercise 1.20
;; 计算欧几里得算法调用remainder函数次数
#lang racket;; 重新定义remainder函数,每次调用都打出一个yes
(define (remainder-count a b)(display "yes\n")(remainder a b));; 欧几里得算法
(define (gcd a b)(if (= b 0)a(gcd b (remainder-count a b))))(gcd 206 40)

racket是应用序,一共输出4个yes,就是调用4次remainder

正则序展开,我只展开了一点,完全展开的话,画面太美……

(gcd 206 40)

(if (= b 0) a ((if (= (remainder a b) 0) b (gcd (remainder a b) (remainder b (remainder a b))))))))

……

总之,非常多remainder调用

这个我用lazyracket也试了一下,照样返回4个yes,所以惰性求值和正则序还是有区别的,不会简单粗暴地一撸到底

现在来计算次数,我是不会展开的,用了另一种方法来做

调用的过程是(gcd 206 40) → (gcd 40 6) → (gcd 6 4) → (gcd 4 2) ,逆推回去

(gcd 2 0)执行了:

(if (= 0 0)

2))

这里调用1个0,1个2,0通过(remainder 4 2)得到,2通过(remainder 6 4)得到,4通过(remainder 40 6)得到,6通过(remainder 206 40)得到

所以(gcd 2 0)里面,0通过6次remainder得到,2通过4次remainder,4通过2次remainder,6通过1次remainder,一共调用了13次

对于其他的gcd过程,尾递归的(gcd b (remainder a b))中a和b只是传递到了下一层,最终传递到(gcd 2 0)才开始规约,所以不需要考虑,if条件的a分支在条件判断后就被跳过,真正调用了remainder的只有条件判断里的b

对于(gcd 4 2),判断2是否为0,通过4次remainder得到

对于(gcd 6 4),判断4是否为0,通过1次remainder得到

对于(gcd 206 40),判断40是否为0,没有调用remainder

所以加起来是13 + 4 + 1 = 18

网上有人做了完全展开,结果也是18,见这篇博文SICP_exercise_1.20

练习1.21

求解代码

;; 求输入的最小因子
#lang racket
(provide (all-defined-out));; 平方
(define (square x)(* x x));; 求最小因子
(define (smallest-divisor n)(find-divisor n 2));; 迭代
(define (find-divisor n test-divisor)(cond ((> (square test-divisor) n) n)((divides? test-divisor n) test-divisor)(else (find-divisor n (+ test-divisor 1)))));; 判断是否可以除尽
(define (divides? a b)(= (remainder b a) 0))(smallest-divisor 199)
(smallest-divisor 1999)
(smallest-divisor 19999)

199和1999都是质数,19999最小因子是7

练习1.22

我用的是racket,没有runtime,用了current-inexact-milliseconds代替

引用了练习1.21的最小因数函数

先写一个判断是否是质数的代码

;; 通过求解最小因数,判断是否是质数
#lang racket
(provide (all-defined-out))
(require "smallest-divisor.rkt");; 最小因数等于本身,证明为质数
(define (prime-test-by-smallest-divisor x)(and (> x 1) (= (smallest-divisor x) x)))

然后写一个搜索质数,现在计算机太快,题目里那么小的数一会就算完了,所以加大了很多倍

;; Exercise 1.22
;; 搜索给定范围的质数,并打印时间
;; 判断质数使用最小因子法
#lang racket
(require "prime-test-by-smallest-divisor.rkt");; 平方
(define (square x)(* x x));; 搜索指定数量和范围的质数并打印
(define (start-prime-test n start-time number)(cond ((= number 0) (report-prime (- (current-inexact-milliseconds) start-time)))((prime-test-by-smallest-divisor n)(display n)(display " ")(start-prime-test (+ n 1) start-time (- number 1)))(else (start-prime-test (+ n 1) start-time number))));; 打印程序耗费时间
(define (report-prime elapsed-time)(display "*** ")(display elapsed-time)(newline));; 开始质数搜索,并记录当前时间
(define (searchForPrimes startNumber number)(start-prime-test startNumber (current-inexact-milliseconds) number))
;; 求幂的模
(define (expmod base n m)(cond ((= n 0) 1)((odd? n) (remainder (* base (expmod base (- n 1) m)) m))  (else (remainder (square (expmod base (/ n 2) m)) m))))(searchForPrimes 10000000000 3)
(searchForPrimes 100000000000 3)
(searchForPrimes 1000000000000 3)
(searchForPrimes 10000000000000 3)
(searchForPrimes 100000000000000 3)
n 时间 比例
10000000000 9.894  
100000000000 28.444 2.874874
1000000000000 74.334 2.613346
10000000000000 302.888 4.07469
100000000000000 989.38 3.266488

好像并不是十分地严格啊,当n增加比例会相对于近似理论值

练习1.23

改进的最小因子求解代码如下

;; Exercise 1.23
;; 改进的求最小因子函数
;; 当不能被2整除,跳过所有偶数因子的尝试
#lang racket
(provide (all-defined-out));; 迭代搜索最小因子
(define (fast-divisor-iter n divisor)(cond ((> (* divisor divisor) n) n)((divides? n divisor) divisor)(else (fast-divisor-iter n (next divisor)))));; 改进的求解最小因子函数
(define (fast-smallest-divisor n)(fast-divisor-iter n 2));; 判断是否可以除尽
(define (divides? a b)(= (remainder a b) 0))(define (next divisor)(if (= divisor 2)3(+ divisor 2)))

改进后的质数测试

;; Exercise 1.23
;; 改进版通过求解最小因数,判断是否是质数
#lang racket
(provide (all-defined-out))
(require "fast-smallest-divisor.rkt");; 最小因数等于本身,证明为质数
(define (fast-prime-test-by-smallest-divisor x)(= (fast-smallest-divisor x) x))

改进后的质数搜索

;; Exercise 1.22
;; 搜索给定范围的质数,并打印时间
;; 判断质数使用最小因子法
#lang racket
(require "fast-prime-test-by-smallest-divisor.rkt");; 平方
(define (square x)(* x x));; 搜索指定数量和范围的质数并打印
(define (start-prime-test n start-time number)(cond ((= number 0) (report-prime (- (current-inexact-milliseconds) start-time)))((fast-prime-test-by-smallest-divisor n)(display n)(display " ")(start-prime-test (+ n 1) start-time (- number 1)))(else (start-prime-test (+ n 1) start-time number))));; 打印程序耗费时间
(define (report-prime elapsed-time)(display "*** ")(display elapsed-time)(newline));; 开始质数搜索,并记录当前时间
(define (searchForPrimes startNumber number)(start-prime-test startNumber (current-inexact-milliseconds) number))
;; 求幂的模
(define (expmod base n m)(cond ((= n 0) 1)((odd? n) (remainder (* base (expmod base (- n 1) m)) m))  (else (remainder (square (expmod base (/ n 2) m)) m))))(searchForPrimes 10000000000 3)
(searchForPrimes 100000000000 3)
(searchForPrimes 1000000000000 3)
(searchForPrimes 10000000000000 3)
(searchForPrimes 100000000000000 3)

替换之后,时间如下

n 时间 时间/改进前时间
10000000000 4.064 41.08%
100000000000 16.261 57.17%
1000000000000 43.798 58.92%
10000000000000 186.143 61.46%
100000000000000 603.598 61.01%

时间大约缩短了一小半,貌似越大的数缩小的越少

练习1.24

先写好费马小定理对应的质数检验函数

;; Exercise 1.24
;; 通过费马小定理检验是否为质数
#lang racket
(provide (all-defined-out));; 求幂的取模
(define (expmod base n m)(cond ((= n 0) 1)((odd? n) (remainder (* base (expmod base (- n 1) m)) m))  (else (remainder (square (expmod base (/ n 2) m)) m))));; 平方
(define (square x)(* x x));; 判断是否为奇数
(define (odd? n)(= (remainder n 2) 1));; 一次费马小定理的质数检验
(define (fermat-test n)(define (try-it a)(= (expmod a n n) a))(try-it (+ 1 (random (- n 1)))));; 费马小定理检验质数
(define (prime-test-by-fermat-iter n times)(cond ((= times 0) true);; 若一次检验通过,次数减一,再次检验((fermat-test n) (prime-test-by-fermat-iter n (- times 1)))(else false)));; 费马小定理检验是否为质数,n表示检验次数
(define (prime-test-by-fermat n)(prime-test-by-fermat-iter n 10))

然后编写改进后的质数搜索函数,因为时间实在太小,把数增加到很大,寻找质数的个数也设为了3000

;; Exercise 1.24
;; 通过费马小定理检验质数法,搜索给定范围内的质数
#lang racket
(require "prime-test-by-fermat.rkt");; 平方
(define (square x)(* x x));; 搜索指定数量和范围的质数并打印
(define (start-prime-test n start-time number)(cond ((= number 0) (report-prime (- (current-inexact-milliseconds) start-time)))((prime-test-by-fermat n)(start-prime-test (+ n 1) start-time (- number 1)))(else (start-prime-test (+ n 1) start-time number))));; 打印程序耗费时间
(define (report-prime elapsed-time)(display "*** ")(display elapsed-time)(newline));; 开始质数搜索,并记录当前时间
(define (searchForPrimes startNumber number)(start-prime-test startNumber (current-inexact-milliseconds) number))(searchForPrimes 1000000 3000)
(searchForPrimes 100000000 3000)

运行了三次,结果如下,理论上呢大输入的运行时间应该是小输入的两倍,实际是比两倍少一些

这个原因大家应该都很清楚,程序的用时不是严格等于执行的步数,还有很多其他因素,这里不多说

练习1.25

这段代码理论上是可以执行的,实践中,对于较小的数下面程序可以运行

但是输入增加后,幂增加到很大,最后的计算结果是一个灰常大的整数,scheme处理灰常大的整数会很卡

而之前的函数可以很快返回结果

因为之前的函数每迭代一次求幂,就取模一次,所以幂(的余数)一直限制在一个很小的范围

练习1.26

因为expmod在每次迭代过程中都计算了两次

讨论个理想情况,假设,在计算expmod的时候的调用如下:

调用了2次

调用了2次

……

调用了2次

总调用次数为: = 2N - 1

对于不完全等于2次幂的N来说,也有类似的结果,证明这个程序是

练习1.27

做了十次费马小定理检验,这6个数字都通过了

这种数叫啥正合成数,也叫伪素数,他们是可以通过费马小定理的检验的

练习1.28

修改一下square函数,在平方后做一下检测

;; Exercise 1.28
;; 通过Miller-Rabin测试检验是否为质数
#lang racket;; 求幂的取模,若检测到某次迭代幂模为1,返回0,其余情况返回幂的取模结果
(define (expmod base n m)(cond ((= n 0) 1)((odd? n) (remainder (* base (expmod base (- n 1) m)) m))  (else (remainder (square-and-check (expmod base (/ n 2) m) m) m))));; 平方,检测平方结果对m的模,为1则返回0,其余情况返回平方结果
(define (square-and-check x m)(define res (* x x))(cond ((or (= x 1) (= x (- m 1))) res)((= (remainder res m) 1) 0)(else res)));; 判断是否为奇数
(define (odd? n)(= (remainder n 2) 1));; 一次质数检验
(define (fermat-test n)(define (try-it a)(= (expmod a n n) a))(try-it (+ 1 (random (- n 1)))));; 质数检验
(define (prime-test-by-miller-rabin-iter n times)(cond ((= times 0) true);; 若一次检验通过,次数减一,再次检验((fermat-test n) (prime-test-by-miller-rabin-iter n (- times 1)))(else false)));; 检验是否为质数,n表示检验次数
(define (prime-test-by-miller-rabin n)(prime-test-by-miller-rabin-iter n 10))

练习1.29

不要以a作为自变量,转变思路,用k做自变量写起来更加方便

;; Exercise 1.29
;; Simpson's Rule计算integral
#lang racket
(require "cube.rkt")
(require "sum.rkt");; Simpson's Rule计算integral
(define (integral-by-simpson f a b n);; 定义h(define h (/ (- b a) n));; 定义h/3(define h-divide-3 (/ h 3));; 以k作为自变量,比用a方便(define (next k)(+ k 1));; k的每项计算(define (term k)(define yk (* h-divide-3 (f (+ a (* k h))))) (cond ((or (= k 0) (= k n)) yk)((even? k) (* 2 yk))(else (* 4 yk))));; 调用sigma函数(sum term 0 next n));; 判断是否为偶数
(define (even? x)(= (remainder x 2) 0))(integral-by-simpson cube 0 1 100)
(integral-by-simpson cube 0 1 1000)

cube就是求立方的代码很简单,如下

;; 立方计算
#lang racket
(provide (all-defined-out))(define (cube x)(* x x x))

sum是求sigma的

;; sigma计算
#lang racket
(provide (all-defined-out))(define (sum term a next b)(if (> a b)0(+ (term a)(sum term (next a) next b))))

n是100或者1000出来的结果都是1/4

练习1.30

写出迭代的程序,技巧就是递归调用的时候,外面不要再套其他操作

;; Exercise 1.30
;; 迭代版的sigma
#lang racket(define (sum term a next b)(define (sum-iter a res)(if (> a b)res(sum-iter (next a) (+ res (term a)))))(sum-iter a 0))

练习1.31

a.

递归的,答案是3.141592810665997

;; Exercise 1.31
;; 递归的product
#lang racket;; 递归的product
(define (product term a next b)(if (> a b)1(* (product term (next a) next b)(term a))));; 判断是否为偶数
(define (even? x)(= (remainder x 2) 0));; 计算pi
(define (pi-product n)(define (next k)(+ k 1))(define (term k)(/ (if (even? k)(+ k 2.0)(+ k 1.0))(if (even? k)(+ k 1.0)(+ k 2.0))))(* (product term 1 next n)4))(pi-product 10000000)

b.

迭代的,答案略有不同,是3.1415928106682323,因为乘的先后顺序不一样了

;; Exercise 1.31
;; 迭代的product
#lang racket;; 迭代的product
(define (product term a next b)(define (product-iter a res)(if (> a b)res(product-iter (next a) (* res (term a)))))(product-iter a 1));; 判断是否为偶数
(define (even? x)(= (remainder x 2) 0));; 计算pi
(define (pi-product n)(define (next k)(+ k 1))(define (term k)(/ (if (even? k)(+ k 2.0)(+ k 1.0))(if (even? k)(+ k 1.0)(+ k 2.0))))(* (product term 1 next n)4))(pi-product 10000000)

练习1.32

a.

递归版本

;; Exercise 1.32
;; 递归的accumulate
#lang racket;; 递归accumulate
(define (accumulate combiner null-value term a next b)(if (> a b)null-value(combiner (accumulate combiner null-value term (next a) next b)(term a))))

b.

迭代版本

;; Exercise 1.32
;; 迭代的accumulate
#lang racket;; 迭代accumulate
(define (accumulate combiner null-value term a next b)(define (accumulate-iter a res)(if (> a b)res(accumulate-iter (next a) (combiner res (term a)))))(accumulate-iter a null-value))

练习1.33

加条件判断的accumulate,简单

;; Exercise 1.33
;; 带filter的accumulate,迭代计算
#lang racket
(provide (all-defined-out));; 迭代filtered-accumulate
(define (filtered-accumulate combiner null-value term a next b filter?)(define (accumulate-iter a res)(cond ((> a b) res)((filter? a) (accumulate-iter (next a) (combiner res (term a))))(else (accumulate-iter (next a) res))))(accumulate-iter a null-value))

a.

加一个素数判断,用上之前的代码

;; Exercise 1.33
;; 素数累加
#lang racket
(require (file "../1.2.6 Testing for Primality/prime-test-by-smallest-divisor.rkt"))
(require "filtered-accumulate.rkt")(define (prime-accumulate a b)(define (next x)(+ x 1))(filtered-accumulate + 0 identity a next b prime-test-by-smallest-divisor))(prime-accumulate 1 10)

b.

也很简单,调用之前写的欧几里得算法

;; Exercise 1.33
;; 所有与n互质且小于n的正整数累乘
#lang racket
(require "filtered-accumulate.rkt");; 欧几里得算法
(define (gcd a b)(if (= b 0)a(gcd b (remainder a b))));; n以内,与n互质的正整数乘积
(define (prime-to-n-accumulate n)(define (filter? x)(= (gcd x n) 1))(define (next x)(+ x 1))(filtered-accumulate * 1 identity 1 next n filter?))

练习1.34

这段代码会报错,实际上最终调用了(2 2),由于2不是一个procedure,报错

练习1.35

黄金分割满足,两边同除得

;; Exercise 1.35
;; 利用fixed-point计算黄金分割
#lang racket
(require "fixed-point.rkt");; 计算近似黄金分割
(define (golden-ratio)(fixed-point (lambda(x) (+ 1.0 (/ 1 x))) 1))(golden-ratio)

练习1.36

;; Exercise 1.36
;; 利用fixed-point计算x^x = 1000,并打印中间结果
#lang racket;; 设定精度
(define tolerance 0.00001);; fixed-point计算
(define (step-display-fixed-point f first-guess)(define (close-enough? v1 v2)(< (abs (- v1 v2)) tolerance))(define (try guess)(let ((next (f guess)))(display next)(newline)(if (close-enough? guess next)next(try next))))(try first-guess))(step-display-fixed-point (lambda (x) (/ (log 1000) (log x))) 4)

打印出来的中间结果如下

练习1.37

a.

递归版本的如下

;; Exercise 1.37
;; 递归,近似计算无限连续小数
#lang racket;; 计算无限连续小数
(define (cont-frac-recursive d n k)(define (cont-frac-recursive-iter i)(if (> i k)0(/ (n i)(+ (d i)(cont-frac-recursive-iter (+ i 1))))))(cont-frac-recursive-iter 1))(cont-frac-recursive (lambda (i) 1.0)(lambda (i) 1.0)11)

测下来k=11的时候才开始得到0.61805,约等于0.6181,4位小数精度

b.

迭代版本

;; Exercise 1.37
;; 迭代,近似计算无限连续小数
#lang racket;; 计算无限连续小数
(define (cont-frac-iterative d n k)(define (cont-frac-iterative-iter i res)(if (= i 0)res(cont-frac-iterative-iter (- i 1)(/ (n i)(+ (d i) res)))))(cont-frac-iterative-iter k 0))(cont-frac-iterative (lambda (i) 1.0)(lambda (i) 1.0)11)

练习1.38

;; Exercise 1.38
;; 计算e-2
#lang racket
(require "cont-frac-iterative.rkt");; 计算d,规律为121,141,161
(define (d x)(if (< (remainder x 3) 2)1.0(* 2.0 (/ (+ x 1) 3))));; 计算e-2
(cont-frac-iterative d(lambda(x) 1.0)10)

练习1.39

;; Exercise 1.39
;; 近似计算tan
#lang racket
(require "cont-frac-iterative.rkt");; 计算近似tan
(define (tan-cf x k)(/ (cont-frac-iterative (lambda(y) (- (* y 2) 1.0))(lambda(y) (- (* x x)))k)(- x)))

练习1.40

;; Exercise 1.40
;; 牛顿法求解立方方程
#lang racket
(require "newtons-method.rkt");; 求解立方方程
(define (cubic-root a b c);; 定义cubic函数(define (cubic a b c)(lambda(x)(+ (* x x x)(* a x x)(* b x)c)));; 求解(newtons-method (cubic a b c) 1))

练习1.41

我天真地以为结果会是8+5=13,然而结果是21

;; Exercise 1.41
;; 执行两次某函数
#lang racket;; 执行两次输入函数
(define (double f)(lambda(x) (f (f x))));; inc
(define (inc x)(+ x 1))(((double (double double)) inc) 5)

来展开一下

(double (double double))展开为((double double) (double double))

每一个(double double)展开是(lambda(x) (double (double x)))),将一个操作进行4次,就是将第二个操作进行4次

而第二个操作将inc进行4次,4*4就是16次inc

练习1.42

;; Exercise 1.42
;; 依次执行函数
#lang racket;; 返回复合函数
(define (compose f g)(lambda(x) (f (g x))))

练习1.43

;; Exercise 1.43
;; 重复执行某函数
#lang racket
(require "compose.rkt");; 执行n次f
(define (repeated f n)(define (repeated-iter n res)(if (= n 0)res(repeated-iter (- n 1) (compose f res))))(repeated-iter n identity))

练习1.44

;; Exercise 1.44
;; smooth
#lang racket
(require "repeated.rkt");; 定义dx
(define dx 0.00001);; smooth
(define (smooth f)(define (avg a b c)(/ (+ a b c) 3))(lambda(x) (avg (f (- x dx)) (f x) (f (+ x dx)))));; n次smooth
(define (n-fold-smooth f n)((repeated smooth n) f))

练习1.45

代码很好写,关键在于如何根据n确定要使用average-dump的次数,虽然题目只是要求expertiment结果,网上也很多人做了,结果就是,我就偷懒了

;; Exercise 1.45
;; 求解n次方根
#lang racket
(require (file "../1.3.3 Procedures as General Methods/fixed-point.rkt"))
(require (file "../1.2.4 Exponentiation/fast-exp.rkt"))
(require "repeated.rkt");; average damp
(define (average-damp f)(lambda(x) (/ (+ (f x) x) 2)));; 根据n,求解需要进行多少次average dump才能使fixed-point收敛
(define (average-times n);; 迭代求解log2(n),p为计数器(define (iter res p)(if (> n res)(iter (* 2 res) (+ p 1))p))(iter 1 0));; 使用fixed-point进行n次方根求解
(define (nth-roots x n)(define (f y)(/ x (fast-exp y (- n 1))))(fixed-point ((repeated average-damp (average-times n)) f) 1.0))

为什么average damp的次数是,参见我的另一篇博文SICP习题1.45 为什么做average damp的次数需要大于等于log2n

练习1.46

;; Exercise 1.46
;; 利用iterative improve求sqrt
#lang racket;; 通用迭代improve
(define (iterative-improve good-enough? improve guess)(if (good-enough? guess)guess(iterative-improve good-enough? improve (improve guess))));; sqrt
(define (sqrt x)(define (good-enough? guess)(< (abs (- (* guess guess) x)) 0.0001))(define (improve guess)(/ (+ guess (/ x guess)) 2.0))(iterative-improve good-enough? improve x))

《SICP》习题第1章相关推荐

  1. 《SICP》习题第3章(施工中)

    本人做的SICP习题第3章,如有错误请指正,用的解释器是Racket 练习3.1 ;; 累加器 (define (make-accumulator initial)(lambda (x)(let (( ...

  2. SICP 习题 (2.7) 解题总结 : 定义区间数据结构

    SICP 习题 2.7 開始属于扩展练习,能够考虑不做,对后面的学习没什么影响.只是,假设上面的使用过程表示序对,还有丘奇计数你都能够理解的话,完毕这些扩展练习事实上没什么问题. 习题2.7是要求我们 ...

  3. 计量经济学第六版计算机答案,伍德里奇计量经济学导论计算机习题第六章第13题c_6.13...

    伍德里奇计量经济学导论计算机习题第六章第13题,答案和MATLAB代码 clear,clc; % c6.13 by % 打开文字文件和数据文件 importdata('meap00_01.des'); ...

  4. SICP 习题 (1.34)解题总结

    SICP 习题 1.34的题目比較特别一点.对于没有接触过高阶函数的同学们来说是个非常好的学习机会. 题目是这种,假设我们定义以下的过程: (define (f g) (g 2)) 那么就有: (f ...

  5. C++Primer 习题 第7章

    C++Primer 习题 第7章 Person.h#ifndef PERSON_H #define PERSON_H #include<iostream> #include<stri ...

  6. 计算机应用基础第七章自测题,计算机应用基础习题第七章.doc

    计算机应用基础习题第七章 第7章 如何使用 WinRAR 快速压缩当您在文件上点右键的时候,您就会看见图 中的部分就是 WinRAR 在右键中创建的快捷键. ? 图 右键菜单 想压缩文件的时候,在文件 ...

  7. 谭浩强c语言课后习题笔记[1-4章]

    c语言程序设计(第五版)谭浩强课后习题笔记 文章目录 c语言程序设计(第五版)谭浩强课后习题笔记 第一章 程序设计和c语言 1.4 打印 Hello World 1.6 输入abc求最大值 第二章 算 ...

  8. 8086微型计算机原理答案,8086微型计算机原理与应用(吴宁)习题答案(第二章)

    8086微型计算机原理与应用(吴宁)习题答案(第二章) 8086微型计算机原理与应用(吴宁)习题答案(第二章) 第二章 微处理器结构 2-14 存储空间范围 CS×16+0000H - CS×16+F ...

  9. 【考研复习】《操作系统原理》孟庆昌等编著课后习题+答案——第二章

    前言 此书在最后的附录B中,有给出部分重难点部分的参考答案.会在最后放上图片.如果想要此书习题答案,可点以下链接:为一个压缩包,以图片形式,习题图片按章节排序,答案图片按书页排序. <操作系统原 ...

  10. 《机器学习》周志华课后习题答案——第一章(1-3题完结)

    <机器学习>周志华课后习题答案--第一章 文章目录 <机器学习>周志华课后习题答案--第一章 一.表1.1中若只包含编号为1和4的两个样例,试给出相应的版本空间 二.与使用单个 ...

最新文章

  1. 具体解释可变參数列表
  2. Revit二次开发之“选择某一楼层的墙”
  3. 如何查看linux系统服务器磁盘大小和文件占用情况
  4. C++调用Python实例
  5. 最实用的logback讲解(2)—appender
  6. python回归分析实验_python线性回归实验
  7. 什么是MPLS?多协议标签交换(MPLS)基础知识和工作原理
  8. Linux下启动、停止J2SE程序(脚本)
  9. spring源码之bean加载(bean解析下篇)
  10. ASP.NET中用healthMonitor属性用法
  11. paip.提升开发效率之查询界面
  12. 天锐绿盾加密系统是做什么用的?
  13. Cisco防火墙基础介绍及配置
  14. 神经网络算法详解 03:竞争神经网络(SONN、SOFM、LVQ、CPN、ART)
  15. NOIP2012 国王游戏(贪心)
  16. Leaflet地图框架使用手册
  17. Cisco、华为、H3C交换机配置文件自动备份
  18. linux搭建本地YUM源配置详细步骤
  19. 东北大学Origin上机实验报告——应力应变图
  20. pascal(收藏)

热门文章

  1. 做独一无二的自己,颜宁西湖大学问答全记录
  2. 调整外接显示屏亮度的方法
  3. 显示器分辨率如何选择,高清,1080P,2k,4k最舒服的点距
  4. Python3-pandas
  5. Java GUI实现简单弹珠游戏(新手入门练习项目)
  6. 对Excel进行瘦身的两个方法
  7. KMS和MAK的区别
  8. 【FineBI】权限控制之动态维度/动态列
  9. Python量化策略风险指标
  10. 回答缺点?这样回答HR听完直呼大佬!