课本提供的一些代码
(define (sqrt x)
(sqrt-iter 1.0 x))
(define (improve guess x)
(average guess (/ x guess)))
(define (average x y)
(/ (+ x y) 2))
(define (expt b n)
(if (= n 0)
1
(* b (expt b (- n 1)))))
(define (expt-iter b counter product)
(if (= counter 0)
product
(expt-iter b
(- counter 1)
(* b product))))
(define (fast-expt b n)
(cond ((= n 0) 1)
((even? n) (square (fast-expt b (/ n 2))))
(else (* b (fast-expt b (- n 1))))))
(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))
(define (prime? n)
(= n (smallest-divisor n)))
(define (expmod base exp m)
(cond ((= exp 0) 1)
((even? exp)
(remainder (square (expmod base (/ exp 2) m))
m))
(else
(remainder (* base (expmod base (- exp 1) m))
m))))
(define (fermat-test n)
(define (try-it a)
(= (expmod a n n) a))
(try-it (+ 1 (random (- n 1)))))
(define (fast-prime? n times)
(cond ((= times 0) true)
((fermat-test n) (fast-prime? n (- times 1)))
(else false)))
(define (timed-prime-test n)
(newline)
(display n)
(start-prime-test n (runtime)))
(define (start-prime-test n start-time)
(if (prime? n)
(report-prime (- (runtime) start-time))))
(define(report-prime elapsed-time)
(display "***")
(display elapsed-time))
(define tolerance 0.00001)
(define (fixed-point f first-guess)
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) tolerance))
(define (try guess)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(try next))))
(try first-guess))
1.1
手打一遍,运行就可以了
10
12
8
3
6
19
#f
4
16
6
16
1.2
还是脑袋过一遍,再手打
(/ (+ 5 4 (- 2 (- 3 (+ 6 (/ 4 5)))))
(* 3 (- 6 2) (- 2 7)))
1.3
简单用cond来进行选择
(define (max-tri-sum a b c)
(cond ((and (< a b) (< a c)) (+ b c))
((and (< b a) (< b c)) (+ a c))
((and (< c b) (< c a)) (+ a b))))
1.4
(define (a-plus-abs-b a b)
((if (> b 0) + -) a b))
如果b>0进行a+b的运算
反之进行a-b运算,这样做其实是把函数也当作参数传递了
1.5
(define (p) (p))
(define (test x y)
(if (= x 0)
0
y))
(test 0 (p))
应用序是传入的参数立刻进行求值,而正则序是有需要时才求值,所以应用序在调用该函数时会陷入无限循环里,而正则序不会。
1.6
计算会报错,主要是因为递归深度太深导致的栈溢出,尽管scheme自带尾递归优化,但是这里的new-if并不是尾递归(sqrt-iter的值需要传入new-if里)
1.7
在极小极大时候,都输出不正确,极小有数值偏差,极大又陷入计算循环中
这里看了习题解,定义了新的guess和sqrt
(define (good-enough? old-guess new-guess)
(> 0.01
(/ (abs (- new-guess old-guess))
old-guess)))
(define (sqrt-iter guess x)
(if (good-enough? guess (improve guess x))
(improve guess x)
(sqrt-iter (improve guess x)
x)))
1.8
注意的是给出的公式是比求平均数更精确的方法,因此改进improve就行,这题没有要求在大数量和小数量较准确,所以就可以沿用先前的good-enough?了。
(define (abs x)
(cond ((< x 0) (- x))
((= x 0) x)
((> x 0) x)))
(define (improve guess x)
(/ (+ (/ x (* guess guess)) (* 2 guess))
3))
(define (good-enough? guess x)
(< (abs (- (* guess guess guess) x))
0.01))
(define (curt-iter guess x)
(if (good-enough? guess x)
guess
(curt-iter (improve guess x)
x)))
(define (curt x)
(curt-iter 1.0 x))
1.9
见习题集的回答
递归
(define (plus a b)
(if (= a 0)
b
(inc (plus (dec a) b))))
(plus 3 5)
(inc (plus 2 5))
(inc (inc (plus 1 5)))
(inc (inc (inc (plus 0 5))))
(inc (inc (inc 5)))
(inc (inc 6))
(inc 7)
8
迭代
(define (plus a b)
(if (= a 0)
b
(plus (dec a) (inc b))))
(plus 3 5)
(plus 2 6)
(plus 1 7)
(plus 0 8)
8
1.10
(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) -> 1024
(A 2 4) -> 65536
(A 3 3) -> 65536
RTFSC我们可以知道,(f n)是2n,(g n)是2^n,由前面我们可以得到(h n)是2的2次幂的2次幂以此循环…..
1.11
递归
(define (c-finb n)
(cond ((< n 3) n)
(else (+ (c-finb (- n 1)) (* 2 (c-finb (- n 2))) (* 3 (c-finb (- n 3)))))))
迭代
(define( d-finb-iter first second third n)
(if (= n 3)
(+ (* 3 first) (* 2 second) third)
(d-finb-iter
second
third
(+ (* 3 first) (* 2 second) third)
(- n 1)) ))
(define (d-finb n)
(if (< n 3)
n
(d-finb-iter 0 1 2 n)))
1.12
不知道题目什么意思,看了习题解发现是翻译错误。。。
以下是习题解答案
(define (pascal row col)
(cond ((> col row)
(error "unvalid col value"))
((or (= col 0) (= row col))
1)
(else (+ (pascal (- row 1) (- col 1))
(pascal (- row 1) col)))))
1.13
证明就不细致自己写了想不出,以下复刻习题解的回答
1.14
这种图都没什么好说的(直接抄把)
1.15
a:演算可以得到为5次
b:程序为递归程序,所以时间和空间复杂度都是O(loga)
1.16
(define (square x)
(* x x))
(define (even? n)
(= (remainder n 2) 0))
(define (fast-expt-d b n)
(fast-expt-iter b n 1))
(define (fast-expt-iter b n product )
(cond ((= n 0) product)
((even? n) (fast-expt-iter (square b) (/ n 2) product ))
(else (fast-expt-iter b (- n 1) (* b product) ))))
主要卡在了偶数时只更新b的值,我刚开始一并更新了product的值导致过大
1.17
实际上只是仿写一下幂乘就行了,even?在上题里有,而且它也是自带的,可以直接调用。
(define (double x)
(* 2 x))
(define (halve x)
(/ x 2))
(define (mul a b)
(cond((= b 0) 0)
((even? b) (mul (double a) (halve b)))
(else (+ a (mul a (- b 1))))
))
1.18
这就是1.17的迭代形式
(define (double x)
(* 2 x))
(define (halve x)
(/ x 2))
(define (fast-mul a b)
(fast-mul-iter a b 0)
)
(define (fast-mul-iter a b product)
(cond ((= b 0) product)
((even? b) (fast-mul-iter (double a) (halve b) product))
(else (fast-mul-iter a (- b 1) (+ product a)))))
1.19
证明:
a \rightarrow bq + aq + ap
\rightarrow (bp + aq)q + (bq + aq + ap)q + (bq + aq + ap)p
= b(2pq + q^2) + a(2pq + q^2) + a(q^2 + p^2)
b \rightarrow bp+aq
\rightarrow (bp+aq)p+(bq+aq+ap)q
=b(q^2+p^2)+a(2pq+q^2)
所以我们可以得出
p’=(q^2+p^2)
q’=(2pq+q^2)
通过这个我们可以试着去实现我们的代码:
(define (fib n)
(fib-iter 1 0 0 1 n))
(define (fib-iter a b p q n)
(cond ((= n 0)
b)
((even? n)
(fib-iter a
b
(+ (square p) (square q))
(+ (* 2 p q) (square q))
(/ n 2)))
(else
(fib-iter (+ (* b q) (* a q) (* a p))
(+ (* b p) (* a q))
p
q
(- n 1)))))
其实就是把我们计算的结果应用在函数上就行了
1.20
自己过一遍模拟过程就行了,这里就参考解题集了写的比我好
应用序:
(gcd 206 40)
(gcd 40 6) ; (gcd 40 (remainder 206 40)
(gcd 6 4) ; (gcd 6 (remainder 40 6))
(gcd 4 2) ; (gcd 4 (remainder 6 4))
(gcd 2 0) ; (gcd 2 (remainder 2 2))
2
一共调用了5步
而正则序呢在迭代的过程需要计算条件即if中的判断,模拟完可知计算条件一共调用了14次,再加上过程调用的4次,共18次。
1.21
(smallest-divisor 199)
199
(smallest-divisor 1999)
1999
(smallest-divisor 19999)
7
1.22
专门设一个参数来记录开始时间,另一个参数来记录已经找到的素数个数,完成查找三个时输出完整运行时间(差值)。采用奇数生成器来节省时间,测试素数的函数可以复用上面的内容,
(define (next-odd n)
(if(odd? n)
(+ 2 n)
(+ 1 n)))
(define (third-prime n w starttime)
(cond ((= w 0)
(display (- (runtime) starttime))
)
((prime? n)
(third-prime n (- w 1) starttime)
)
(else (third-prime (next-odd n) w starttime))))
(define (search-for-prime n)
(third-prime n 3 (runtime)))
(search-for-prime 1000)
7
(search-for-prime 10000)
12
(search-for-prime 100000)
25
(search-for-prime 1000000)
104(70-150)
这里测试发现误差还挺大的(
但是倍数不是\sqrt{10}所以在机器上运行的时间不正比于计算的步数。
1.23
(define (next x)
(if (= x 2)
3
(+ x 2)
))
(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 (next test-divisor)))))
对timed-prime-test进行修改
(define (start-prime-test n start-time)
(smallest-divisor n)
(report-prime (- (runtime) start-time)))
修改前:(timed-prime-test 10000) 3
修改后:(timed-prime-test 10000) 3
发现误差不大,原来是runtime的记录精度不够,习题解用的是real-time-clock,修改后测试发现运行更快了,但也不是一倍。
1.24
直接修改prime?为fast-prime?就好了
(define (start-prime-test n start-time)
(if(fast-prime? n 10)
(report-prime (- (runtime) start-time))))
测试可知速度与之前相比确实提升了,但是也不是严格按常数级别增长。一部分也有误差的原因。
1009->4 1000003->33(22-35大概…)
最多慢了八倍,最快慢了五倍,我们也可以得出来,时间复杂度在现实还要考虑到计算机运行速度,占用资源等等一系列误差。
1.25
理论上可行,但是我们快速检查时有时也需要处理非常大的数值,它的乘幂就很大,容易超出空间。而每次取余就控制了数的大小使得其便于计算,而且提高了速度。
1.26
这个原因在于
(* (expmod base (/ exp 2) m)
(expmod base (/ exp 2) m))
计算了两次(expmod base (/ exp 2) m),(相当于是独立的进程被进行了两次),而(square (expmod base (/ exp 2) m))相当于自乘的是一个数值,而不是一个仍需要计算的进程,因此时间变慢了,这其实有点像编译器的常量传播。
1.27
(define (carmichael-test n)
(test 1 n))
(define (test t-number n)
(cond((= t-number n)
true)
((expmod? t-number n)
(test (+ t-number 1) n))
(else false)))
(define (expmod? t-number n)
(= (expmod t-number n n) t-number))
用脚注47的数据测试得输出结果都为#t
1.28
(define (expmod base exp m)
(cond ((= exp 0) 1)
((sqrt? base m) 0)
((even? exp)
(remainder (square (expmod base (/ exp 2) m))
m) )
(else
(remainder (* base (expmod base (- exp 1) m))
m)
)))
(define (r-test n)
(define (try-it a)
(= (expmod a (- n 1) n) 1))
(try-it (+ 1 (random (/ (- n 1) 2)))))
(define (sqrt? a n)
(and(not (= a 1))
(not (= a (- n 1)))
(= 1 (remainder (square a) n))))
主要还是照着题目的要求来写代码,在expmod里新增了一个模块sqrt?用来检测非平凡根。测试时候发现有时候会#t时会#f,还以为自己写错了,查资料才发现原来是这个检查也是概率函数,至少测试n/2次后结果才稳定下来。
1.29
把一些函数封装在simpson里面,调用书本上的sum。
(define (cube x)
(* x x x))
(define (sum term a next b)
(if (> a b)
0
(+ (term a) (sum term (next a) next b))))
(define (simpson f a b n)
(define h
(/ (- b a) n))
(define (fact k)
(cond ((or (= k 0) (= k n)) 1)
((odd? k) 4)
((even? k) 2)))
(define (y k)
(f (+ a (* k h))))
(define (term k)
(/ (* h (y k) (fact k)) 3)
)
(define (next k)
(+ k 1))
(sum term 0 next n)
)
我的DrRaket输出时不是小数而是分数,没法判断精确程度,但是按理来说是更精确的。
1.30
把改变的量改为用参数形式传入就能把递归修改为迭代了。
(define(sum term a next b)
(define (iter a result)
(if (> a b)
result
(iter (next a) (+ (term a) result))))
(iter a 0))
1.31
这里将递归和迭代形式和对应的实现pi函数分隔开来
递归
(define (product-recursion start term next end)
(if (> start end)
1
(* (term start) (product-recursion (next start) term next end))))
(define (pi-recursion n)
(define (next x)
(+ x 1))
(define (ration k)
(if (odd? k)
(/ (+ k 1) (+ k 2))
(/ (+ k 2) (+ k 1))))
(* 4 (product-recursion 1 ration next n)))
迭代
(define (product-iter start term next end)
(define (iter start result)
(if (> start end)
result
(iter (next start) (* result (term start)))))
(iter start 1))
(define (pi-iter n)
(define (ration k)
(if (odd? k)
(/ (+ k 1) (+ k 2))
(/ (+ k 2) (+ k 1))))
(define (next x)
(+ x 1))
(* 4 (product-iter 1 ration next n)))
1.32
递归
(define (accumblate combiner null-value term a next b)
(if (> a b)
null-value
(combiner
(term a)
(accumblate combiner null-value term (next a) next b))))
迭代
(define (accumblate combiner null-value term a next b)
(if(> a b)
null-value
(accumblate combiner (combiner (term a) null-value) term (next a) next b)))
(define (sum-accumblates term a b n)
(accumblate + 0 term a next b))
(define (product-accumblates term a b n)
(accumblate * 1 term a next b))
1.33
递归
(define (filtered-accumblate filter combiner null-value term a next b)
(cond ((> a b) null-value)
((filter a)(combiner
(term a)
(filtered-accumblate filter combiner null-value term (next a) next b)))
(else (combiner
null-value
(filtered-accumblate filter combiner null-value term (next a) next b)))))
迭代
(define (filtered-accumblate filter combiner null-value term a next b)
(cond((> a b) null-value)
((filter) (filtered-accumblate filter combiner (combiner (term a) null-value) term (next a) next b))
(else (accumblate combiner null-value term (next a) next b))))
题2需要两个参数所以用之前的模板不好表达,我们最好引入下文提到的lambda表达式来解决
(define (sum-prime a b)
(define (term x) x)
(define (next x) (+ x 1))
(filtered-accumblate prime? + 0 term a next b))
(define (co-prime? a b)
(and (< a b) (= (gcd a b) 1)))
(define (mul-co-prime n)
(define (term x) x)
(define (next x) (+ x 1))
(filtered-accumblate (lambda (x) (coprime? x n)) * 1 term 1 next n))
1.34
(define (f g)
(g 2))
(f f)
=>(f 2)
=>(2 2)
1.35
我们设有这么一个正方形,宽为1,长为\Phi
在里面截取一个边长为1的正方形,新矩形的长为 1,宽为 \Phi -1,且\frac{\Phi}{1}=\frac{1}{\Phi -1}即\Phi ^2=\Phi +1
也即x^2=x+1,x=1+\frac{1}{x}
即黄金分割率为此的映射
(fixed-point (lambda (x) (+ 1 (/ 1 x))) 1.0)
1.36
(define tolerance 0.00001)
(define (fixed-point f first-guess)
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) tolerance))
(define (try guess)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(begin
(display next)
(newline)
(try next)))))
(try first-guess))
(fixed-point (lambda (x) (/ (log 1000) (log x))) 2.0) ;steps=34
(fixed-point (lambda (x) (average x (/ (log 1000) (log x)))) 2.0) ;steps=9
1.37
递归形式,k=11时能达到精确程度0.618。
(define (cont-frac n d k)
(define (next x)
(+ x 1))
(define (recursion n d k j)
(if (= j k)
(/ (n j) (d j))
(/ (n j) (+ (d j) (recursion n d k (next j))))))
(recursion n d k 1))
(cont-frac (lambda (i) 1.0) (lambda (i) 1.0) 11)
迭代,我们可以反向操作
“`scheme
(define (cont-frac n d k)
(define (next x)
(+ x 1))
(define (iter n d k j sum)
(if (= j k)
(/ (n (- k j)) (+ (d (- k j)) sum))
(iter n d k (next j) (/ (n (- k j)) (+ (d (- k j)) sum)))))
(iter n d (- k 1) 0 0))
“`
1.38
(define (e-text k)
(define (d-number x)
(if (= (remainder (+ x 1) 3) 0)
(* 2 (/ (+ x 1) 3))
1))
(+ 2 (cont-frac (lambda (i) 1.0) d-number k)))
1.39
递归
(define (tan-cf x k)
(define (next x)
(+ x 1))
(define (recursion x d k j)
(cond ((= j 1)
(/ x (- (d j) (recursion x d k (next j)))))
((= j k)
(/ (* x x) (d j)))
(else
(/ (* x x) (- (d j) (recursion x d k (next j)))))))
(recursion x (lambda (i) (- (* 2 i) 1)) k 1))
迭代
(define (tan-cf x k)
(define (next x)
(- x 1))
(define (iter x d k sum)
(cond ((= k 1)
(/ x (- (d k) sum)))
(else
(iter x d (next k) (/ (* x x) (- (d k) sum))))))
(iter x (lambda (i) (- (* 2 i) 1)) k 0))
1.40
(define (cubic a b c)
(lambda (x) (+ (* x x x) (* a x x) (* b x) c)))
1.41
事实上就是要注意lambda向外捕获参数的过程
(define (double f)
(lambda (x) (f (f x))))
(((double (double double)) inc) 5)=>21
1.42
(define (compose f g)
(lambda (x) (f (g x))))
1.43
(define (repeated f n)
(if (= n 0)
(lambda (x) x)
(lambda (x) ((repeated f (- n 1)) (f x)))
))
1.44
(define (smooth f)
(define dx 0.00001)
(lambda (x) (/ (+ (f x) (f (- x dx)) (f (+ x dx))) 3)))
((repeated (smooth f) n) x)
1.45
\log _2n的由来和算法参考了习题解
(define (square x)
(* x x))
(define tolerance 0.00001)
(define (fixed-point f first-guess)
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) tolerance))
(define (try guess)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(try next))))
(try first-guess))
(define (repeated f n)
(if (= n 0)
(lambda (x) x)
(lambda (x) ((repeated f (- n 1)) (f x)))
))
(define (average a b)
(/ (+ a b) 2))
(define (average-damp f)
(lambda (x) (average x (f x))))
(define (fast-expt b n)
(cond ((= n 0) 1)
((even? n) (square (fast-expt b (/ n 2))))
(else (* b (fast-expt b (- n 1))))))
(define (n-root n)
(define (lg w)
(cond ((> (/ w 2) 1)
(+ 1 (lg (/ w 2))))
((< (/ w 2) 1)
0)
(else
1)))
(lambda (x)
(fixed-point
(repeated (average-damp
(lambda (y)
(/ x (fast-expt y (- n 1))))
) (lg n))
1.0))) ;返回的是一个过程
1.46
这里主要在思考初始的猜测值怎么引入,但没有写好,习题解对fixed-point的改变形式让我耳目一新,而且也提醒了我在lambda里面可以继续定义函数,这里就展示习题解的答法:
(define (iterative-improve close-enough? improve)
(lambda (first-guess)
(define (try guess) ;有效解决了猜测量引入和迭代的问题
(let ((next (improve guess)))
(if (close-enough? guess next)
next
(try next))))
(try first-guess)))
(define (sqrt x)
(define dx 0.00001)
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) dx))
(define (improve guess)
(average guess (/ x guess)))
(define (average x y)
(/ (+ x y) 2))
((iterative-improve close-enough? improve) 1.0))
define (fixed-point f first-guess)
(define tolerance 0.00001)
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) tolerance))
(define (improve guess)
(f guess))
((iterative-improve close-enough? improve) first-guess))