SICP 部分习题答案

1.1

10 , 12 , 8 , 3 , 10 6, a , b , 19 , #f , 4 , 16 , 6 , 16

1.2


(/(+  5   4  (-  2  (-  3  (+  6 (/  4   5 )))))(*  3  (-  6   2 )(-  2   7 )))

or

(/(+  5   4  (-  2  (-  3  (+  6   4 / 5 ))))(*  3  (-  6   2 )(-  2   7 )))

1.3

这个问题中文版的翻译是错的,参看原文是求平方和而不是“和”。

(define (square(x)(* x x)))
(define (max x y)(if (< x y) y x))
(define (func x y z)
  (+ (square (max x y))
     (square (max (min x y) z))))

1.4

a+|b| 

<=>

1  #  in python
2  def  a_plus_abs_b(a,b):
3       if  b > 0 :
4          x  =  a  +  b
5       else :
6          x  =  a  -  b
7       return  x

1.5

在网上看了很多答案,都认为“应用序”的实现会导致死循环,我非常困惑。反复看了中文版和英文版,觉得大家这样认为可能是书中说lisp的实现是“应用序”,而在scheme中跑这段代码会死循环,就先入为主的认为“应用序”的实现会死循环。其实对照正文,我们可以看到“正则序”停止展开的条件是“只包含基本运算符的表达式”,而对于

(define (p) (p))

是无论如何也没法完全展开的,因为它会不断递归,所以“正则序”才会死循环。

而对于“应用序”的实现,则会这样展开


(test 0 (p))
(if (= 0 0) 0 (p))
(if #t 0 (p))

; 0

解决这个问题主要是“正则序”(Normal order)以及“应用序”(Applicative order)展开一个组合式的规则,仔细研究了MIT 6.001课程讲义,网上的各种答案,以及中英文版。我认为,正则序以类似广度优先的方式进行展开。而应用序优先计算子表达式,类似与深度优先。那么对于这个问题, 正则序 会展开为
= > (if ( =   0   0 0  (p))
= > (if #t  0  (p))
接着,由于这是一个if的special form(特殊形式),就会被展开为
0
而应用序,由于(p)一直可以递归代换,从一开始就会进入一个无限递归中去。
简言之,由于应用序的原因,在 test 表达式 还没有展开为 if 特殊形式(special forms)时, (p)已经陷入了无限递归。

1.6


会递归直到堆栈溢出。
原因是 在 new-if 还没有展开为 cond special forms 时,else-clause 子式已经陷入了无限递归。 做了以下实验,可以验证
(define (new-if pred thenc elsec)
  (cond (pred thenc)
        (else elsec)))

(define (iter x y)
  (new-if (
=  x y)
          
0
          (iter (+ x 
1 ) y)))

(define (iter-if x y)
  (if (
=  x y)
      
0
      (iter-if (+ x 
1 ) y)))

(define (iter-cond x y)
  (cond ((
=  x y)  0 )
        (else (iter-cond (+ x 
1 ) y))))

对于 (iter 1 10), (iter-if 1 10), (iter-cond 1 10)
其中 iter 会 导致堆栈溢出,而 iter-cond 和 iter-if 并不会。

此题同1.5
在1.5中,由于应用序的原因,在 test 表达式 还没有展开为 if 特殊形式(special forms)时, (p)已经陷入了无限递归。


对于正文中的 good-enough? 谓词,设所求 x 的真实平方根为 xt,那么我们的依据是当 guess 从 1 趋向与 xt 的平方差 小于 c(0.001) 时,guess 近似于 xt。实际当 xt^2 也就是 x 足够小时, guess 会 逐渐稳定在 √c 附近。从下面实验可以看出:

> (sqrt (square  0.1 ))
0.10032578510960607
> (sqrt (square 
0.05 ))
0.054237622808967656
> (sqrt (square 
0.01 ))
0.03230844833048122
> (sqrt (square 
0.005 ))
0.031515954454847304
> (sqrt (square 
0.001 ))
0.031260655525445276
> (sqrt (square 
0.0001 ))
0.03125010656242753
>
 
因为 guess^2 < c + x, 当 x < c 时,guess 就更多的依赖于 c 了。

对于特别大的数,由于浮点数在特别大时,离散性非常明显,相邻的两个数之间的差距会非常大,导致 |guess^2 - x| 始终 大于 c,计算便进入了 无限循环。

比如计算 (sqrt 1e300)

使用变化率改进后的代码如下:

(define (sqrt-new x)
  (sqrt-iter-new x 
1.0  x))

(define (sqrt-iter-new s1 s2 x)
  (if (enuf-new? s1 s2)
      s2
      (sqrt-iter-new s2 (improve s2 x) x)))

(define (enuf-new? s1 s2)
  (< (/ (abs (- s1 s2)) s1) 
0.001 ))

可以测算几个数,验证结果还是比较好的。

> (sqrt-new (square  1e150 ))
1.0000000000084744e+150
> (sqrt-new (square 
1e-150 ))
1.0000000000084744e-150


1.8


采用1.7中的变化率为终止检测。

(define (cube-root x)
  (cube-root-iter x 
1.0  x))

(define (cube-root-iter last-guess guess x)
  (if (enuf? last-guess guess)
      guess
      (cube-root-iter guess (improve guess x) x)))

(define (enuf? x y)
  (< (/ (abs (- x y)) y) 
0.001 ))

(define improve (lambda (y x)
                  (/ (+ (/ x (* y y)) (* 
2  y))  3 )))

1.9

很显然,第一个是递归的,第二个是迭代的。

(+  4   5 )
(if (
=   4   0 5  (inc (+ (dec  4 5 )))
(inc (+ 
3   5 ))
(inc (if (
=   3   0 5  (inc (+  2   5 ))))
(inc (inc (if (
=   2   0 5  (inc (+  1   5 )))))
(inc (inc (inc (if (
=   1   0 5  (inc (+  0   5 ))))))
(inc (inc (inc (inc (if (
=   0   0 5  (inc (+ (dec  0 5 )))))))
(inc (inc (inc (inc 
5 ))))
(inc (inc (inc 
6 )))
(inc (inc 
7 ))
(inc 
8 )
9

(+ 
4   5 )
(if (
=   4   0 5  (+  3   6 ))
(if (
=   3   0 6  (+  2   7 ))
(if (
=   2   0 7  (+  1   8 ))
(if (
=   1   0 8  (+  0   9 ))
(if (
=   0   0 9  (+ (dec  0 ) (inc  9 )))
9

            0 ,    y  =   0 ;
f(x , y)  =     2y ,   x  =   0 ;
             2 ,    y  =   1 ;
            f(x- 1 ,  f(x ,  y- 1 )) ;

那么,对于 f(0,n), n>=0
当   n >= 1 时, f(0,n) = 2n ,
而当 n =  0 时, f(0,0) = 0 = 2*0, 也满足 2n ,
故 f(0,n) = 2n, n>=0.

对于f(1,n), n>=1
当 n > 1 时,有 f(1,n) = f(0, f(1, n-1)) = 2*f(1,n-1),
设 f(1,n) = 2^n
if   n = 1,    f(1,1)    = 2 = 2^1
when n > 1, if f(1,n-1)  = 2^(n-1)
then f(1,n) = 2*f(1,n-1) = 2*(2^(n-1)) = 2^n
故 f(1,n) = 2^n, n>0.

对于f(2,n), n>0
if n > 1 ,then f(2,n) = f(1, f(2, n-1)) = 2^f(2,n-1),
设 f(2,n) = 2^(2^(... (n 个 2)
if   n = 1, then f(2,1)  = 2
when n > 1, if f(2, n-1) = 2^(2^(...        (n-1)
then f(2,n) = 2^f(2,n-1) = 2^(2^(

这样我们对于 (A 1 10) = 2^10 = 1024, (A 2 4) = 2^(2^(2^2)) = 2^16 = 65536
而 (A 3 3) = (A 2 A(3 2)) = A(2 A(2 A(2 1))) = (A 2 4) = 2^16 = 65536

(f n) = (A 0 n) = 2n
(g n) = (A 1 n) = 2^n
(h n) = (A 2 n) = 2^(2^(...      (n个2)

---------------------------------------------
在网上可以找到关于 Ackermann 函数的讨论,主要是针对这个双递归如何用迭代来实现,Ackermann 函数是 德国数学家W.Ackermann 在1928年提出的。在 WikiPedia 英文版上可以搜索 Ackermann function词条,有详细介绍,不过这个Ackermann function略有不同。

1.11
递归计算过程为

(define func-recu
  (lambda(n)
    (cond ((< n 
3 ) n)
          (else (+ (func-recu (- n 
1 ))
                   (* 
2  (func-recu (- n  2 )))
                   (* 
3  (func-recu (- n  3 ))))))))
迭代计算过程为
方法(1)
(define (func n)
  (fun 1 2 3 n))
(define (fun fir sed thi mun)
  (cond ((= mun 1) fir)
        ((= mun 2) sed)
        ((= mun 3) thi)
        (else (fun sed
                   thi
                   (+ (* 3 fir)
                      (* 2 sed)
                      (* 1 thi))
                   (- mun 1)))))
方法(2)
(define func-iter
  (lambda(a b c n)
    (if (
=  n  0 )
        a
        (func-iter b c (+ (* 
3  a) (*  2  b) c) (- n  1 )))))

(define (func n) (func-iter 
0   1   2  n))

1.12
中文版原题翻译有误,应为计算pascal三角中的元素。
; pas(n,k)
;
if (k=1) or (k=n), then pas(n,k) = 1
;
else pas(n,k) = pas(n-1,k-1) + pas(n-1,k)

(define pas (lambda(n k)
              (if (or (
=  k  1 ) ( =  k n))
                  
1
                  (+ (pas (- n 
1 ) (- k  1 ))
                     (pas (- n 
1 ) k)))))

1.13
中文版原题翻译遗漏 提示 :ψ=(1-√5)/2
已知,φ^2 = φ + 1, 那么 φ^n = φ^(n-1) + φ^(n-2)
同理, ψ^2 = ψ + 1, 那么 ψ^n = ψ^(n-1) + ψ^(n-2)
φ-ψ = (1+√5)/2 - (1-√5)/2 = √5

when n=0, Fib(0) = 0 =
(φ^0-ψ^0)/ √5
when n=1, Fib(1) = 1 = √5 / √5 = (φ-ψ)/ √5
when n>2, Fib(n) = Fib(n-1) + Fib(n-2) = (φ^(n-1)-ψ^(n-1))/
√5 + (φ^(n-2)-ψ^(n-2))/ √5
                 = ((
φ^(n-1)+(φ^(n-2))) - ( ψ^(n-1) +ψ^(n-2)))/ √5
                 = ( φ^n - ψ^n)/ √5

又 -1<
ψ < 0, 故 -0.5< -1/ √5 < ψ^n/ √5 < 1 / √5 <0.5, 而 φ^n /√5 = ψ^n/√5 + Fib(n)

可知 |
φ^n /√5 - Fib(n)| < 0.5, Fib(n)是最接近 φ^n /√5的整数。

1.14
计算过程的树如下:


很容易看出,计算过程的空间需求,也就是树的深度,取决于最左边的子树,即(n 1),它的深度是n+6,O(n).

然后对于计算步数,也就是树的节点数,我们知道对于一个二叉树,树的节点数 = 左子树节点数 + 右子树节点数 + 1.
先来看 (n 1) 子树,设它的节点数是f(n), 而且总有,非叶节点左子树节点数为1
当 n=1,f(1) = 3
   n>1, f(n) = 1 + f(n-1) + 1 = f(n-1) + 2 = f(n-2) + 2*2
             = f(n-(n-1)) + 2*(n-1) = 2n + 1
             = O(n)

再来看 (n 2) 子树,设它的节点数 g(n), 设 ┌ n/5 ┐ = A
g(n) = f(n) + g(n-5) + 1 = f(n) + f(n-5) + g(n-5*2) + 2
     = f(n) + ... + f(n-5*(A-1)) + g(n-5*A) + 2A
     = O(n^2)

依此类推,可以得出结论 (n 5) 的计算步数增长的阶 为 O(n^5)

1.15
a) 12.15 连除 5次 3 小于 0.1 ,所以是 5次
b) 可以看出每调用一次 p 过程,需要递归1次 sine ,空间加1,计算步数加2,关键是p的次数:
   对于a,调用次数t,那么 a*3^(-t) < 0.1 , 即 10a < 3^t ==> lg(10a)/lg3 < t,
   所以增长阶 空间和时间 都为 O(log a)

1.16

(define (fast-expt x n)
  (fast-expt-iter x n 
1 ))

(define (fast-expt-iter x n a)
  (cond ((
=  n  0 ) a)
        ((even? n) (fast-expt-iter (square x) 
                                   (/ n 
2 )
                                   a))
        (else (fast-expt-iter x 
                              (- n 
1 )
                              (* a x)))))

1.17
(define (multi x y)
  (if (
=  y  0 )
      
0
      (+ x (multi x (- y 
1 )))))

(define (fast-multi x y)
  (cond ((
=  y  0 0 )
        ((even? y) (double (fast-multi x
                               (half y))))
        (else (+ x (fast-multi x (- y 
1 ))))))

(define (double x) (+ x x))
(define (half y) (/ y 
2 ))

1.18
(define (double x) (+ x x))
(define (half y) (/ y 
2 ))

(define (fast-multi x y)
  (fast-multi-iter x y 
0 ))

(define (fast-multi-iter x y p)
  (cond ((
=  y  0 ) p)
        ((even? y) (fast-multi-iter (double x)
                                    (half y)
                                    p))
        (else (fast-multi-iter x (- y 
1 ) (+ p x)))))

1.19
; T(pq):(a,b)=>(bq+aq+ap, bp+aq)
;
T(pq):(bq+aq+ap, bp+aq)=>((bp+aq)q + (bq+aq+ap)q + (bq+aq+ap)p,
;
                          (bp+aq)p + (bq+aq+ap)q)
;
      => (2bpq+2aqq+bqq+2apq+app, bpp+2apq+bqq+aqq)
;
      => (b(2pq+qq)+a(2pq+qq)+a(qq+pp), b(qq+pp)+a(2pq+qq))
;
q' = 2pq+qq
;
p' = qq+pp
;
(define (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 a
                   b
                   (+ (* p p) (* q q))
                   (+ (* 
2  p q ) (* q q))
                   (/ count 
2 )))
        (else (fib-iter (+ (* b q) (* a q) (* a p))
                        (+ (* b p) (* a q))
                        p
                        q
                        (- count 
1 )))))
1.20
下面用 % 表示过程 remainder,那么正则序的过程如下:
 (gcd  206   40 )
 (if (
=   40   0 206  (gcd  40  (%  206   40 )))
 (gcd 
40  (%  206   40 ))
 (if (
=  (%  206   40 0 ; <##  1> [6]
      40  
     (gcd (% 
206   40 ) (%  40  (%  206   40 ))
 (gcd (% 
206   40 ) (%  40  (%  206   40 )))
 (if (
=  (%  40  (%  206   40 ))  0 )    ; <##  2> [4]
     (%  206   40 )
     (gcd (% 
40  (%  206   40 )) (% (%  206   40 ) (%  40  (%  206   40 )))))
 (gcd (% 
40  (%  206   40 )) (% (%  206   40 ) (%  40  (%  206   40 ))))
 (if (
=  (% (%  206   40 ) (%  40  (%  206   40 )))  0 ; <## 4>[2]
     (%  40  (%  206   40 ))
     (gcd (% (% 
206   40 ) (%  40  (%  206   40 )))
          (% (% 
40  (%  206   40 )) (% (%  206   40 ) (%  40  (%  206   40 ))))))
 (gcd (% (% 
206   40 ) (%  40  (%  206   40 )))
      (% (% 
40  (%  206   40 )) (% (%  206   40 ) (%  40  (%  206   40 ))))))
 (if (
=  (% (%  40  (%  206   40 )) (% (%  206   40 ) (%  40  (%  206   40 )))))  0 ; <## 7>[0]
     (% (%  206   40 ) (%  40  (%  206   40 )))
     (gcd ...)
 (% (% 
206   40 ) (%  40  (%  206   40 )))   ; <## 4>[2]

可以看出需要调用 remainder 共 1+2+4+7+4 = 18 次。

应用序计算过程如下:
(gcd  206   40 )
(if (
=   40   0 206  (gcd  40  (%  206   40 )))  ; <##>
(gcd  40   6 )
(if (
=   6   0 40  (gcd  6  (%  40   6 )))       ; <##>
(gcd  6   4 )
(if (
=   4   0 6  (gcd  4  (%  6   4 )))         ; <##>
(gcd  4   2 )
(if (
=   2   0 4  (gcd  2  (%  4   2 )))         ; <##>
(gcd  2   0 )
(if (
=   0   0 2  (gcd  0  (%  2   0 )))
2

可以看出共需 4 次。

1.21

> (smallest-divisor  199 )
199
> (smallest-divisor 
1999 )
1999
> (smallest-divisor 
19999 )
7
>

1.22
在 DrScheme 中没有对应的 runtime 过程,我们用内建的 current-milliseconds来代替,这个过程返回的是系统的 ms 数。
同时,在 Windows 下无法精确表示 16ms 以下的精度(可能时间片的大小是 16ms ),所以这里用比较大的数来测试。
代码如下

(define (even? x) ( =  (remainder x  2 0 ))

(define (runtime) (current-milliseconds))

(define (start-prime-test n start-time)
  (and (prime? n)
       (report-prime (- (runtime) start-time))))

(define (report-prime elapsed-time)
  (display 
"  ***  " )
  (display elapsed-time))

(define (search-iter cur-num index start-time)
  (newline)
  (display cur-num)
  (if (not (
=  index  0 ))
      (if (start-prime-test cur-num start-time)
          (search-iter (+ cur-num 
2 ) (- index  1 ) start-time)
          (search-iter (+ cur-num 
2 ) index start-time))))

(define (search-for-primes start-number)
  (if (even? start-number)
      (search-iter (+ start-number 
1 3  (runtime))
      (search-iter start-number 
3  (runtime))))

这里用到了 prime? 谓词,代码不再复述。
                            
|------+--------+--------+-------|
|10^9  | 10^10  | 10^11  | 10^12 | => start-number
|------+--------+--------+-------|
|31    | 250    | 609    | 1953  | (ms)
|47    | 406    | 1203   | 3844  |
|78    | 625    | 1859   | 5719  |
|------+--------+--------+-------|

从上表可以看出,除了前两列之间,后列的数字都是前列数字的 3 倍左右,近似于 √10 ≈ 3.16。实际上,随着 n 的不断增大,这个数会逐渐逼近 √10。

1.23

(define (next n)
  (if (
=  n  2 3  (+ 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)))))

计算结果如下:
|--------+--------+-------|
| 10^10  | 10^11  | 10^12 | => start-number
|--------+--------+-------|
| 141    | 297    | 1078  | (ms)
| 219    | 609    | 2093  |
| 313    | 984    | 3140  |
|--------+--------+-------|

可以看出这个比值大约在 1.8(1/0.55) 左右,可能原因是其它的计算需要时间,但当 n 不断增大,其它计算是常数阶,这个比值会不断接近2。
1.24
对于Fermat检查,因为具有log n 的增长阶,所以对于 n^2 和 n 的检查的时间比 理论上应该是 2:1, 实际上,经过测试也比较接近,当n比较大时。
若与预计不符,可能因为 n 比较小,或者字长发生变化,比如 n > 2^32 (参见下题)

1.25
仅从理论分析,Alyssa 的改动不会引起增长阶的变化,但实际上当 Fermat 检查的 n 稍微大一点,速度就会很慢。主要原因 就是 base^exp 是一个非常大的数,可能远远超过 一个32位机字的表示范围 2^32 ,在 scheme 里可能用若干个 32-bit 靠软件实现运算,这将导致计算急速增长。无论是传递、运算还是求模。
实际上 1.22、1.23、1.24 的几个题目可能都会遇到字长变化引起的计算速度突变。

1.26
Fermat 检查正是因为 连续求平方的求幂方法,使得的增长阶变为 log n, 而这均来源于 b^(2n) = (b^n)^2,
Louis 的方法让求幂又变成了连乘,b^(2n) = b^n*b^n = (b*b*...*b)*(b*b*...*b),求幂的增长阶变成了 O(n),Fermat 检查的增长阶自然也变成了 O(n)。

1.27
(define (fermat-test n)
  (fermat-iter (- n 
1 ) n))

(define (fermat-iter a n)
  (cond ((
=  a  0 ) #t)
        ((
=  (expmod a n n) a) (fermat-iter (- a  1 ) n))
        (else #f)))

1.28
首先来看,Fermat 小定理的一个变形:

p 是素数, 1<a<p, 有 a^p % p = a
==> a^p = kp + a ==> a^p - a = kp ==> a(a^(p-1)-1) = kp ==> a^(p-1) -1 = k'p
==> a^(p-1) % p = 1

这个变形就是题目中提到的,这个形式和费马小定理是等价的(但是奇怪的是,我没有发现已知的几个Carmichael数能够躲过这个变形的检查,有待研究

再来看,miller-rabin 素性测试的原理:

p 是素数, 1<a<p, 且 a^2 % p = 1
==> (a^2-1) % p = 0 ==> (a+1)(a-1) % p =0
那么 a+1 % p = 0 或者 a-1 % p =0,
又 a<p 且 p 是素数,所以
a = 1 或者 a = p-1 (这两个叫做 1模n的平凡平方根)

代码如下:
(define (check-nontrivial-sqrt-of-one a n)
  (define (check-
1? t)
    (if (and (> a 1
)
             (< a (- n 
1 ))
             (
=  t  1 ))
        
0  t))
  (check-
1 ? (remainder (square a) n)))

(define (expmod base exp m)
  (cond ((
=  exp  0 1 )
        ((even? exp)
         
; (remainder (square (expmod base (/ exp 2) m)) m))
         (check-nontrivial-sqrt-of-one (expmod base (/ exp  2 ) m) m))
        (else
         (remainder (* base (expmod base (- exp 
1 ) m)) m))))

(define (miller-rabin-test n)
  (define (iter x n)
    (cond ((
=  x  0 ) #t)
          ((
=  (expmod x (- n  1 ) n)  1 ) (iter (- x  1 ) n))
          (else #f)))
  (iter (- n 
1 ) n))

① 对于 Carmichael 数 n ,实际上不能完全通过 a^(n-1)%n = 1 的检查 ,除非 a 与 n 互素,当 a 为 n 的素因子时,不能通过,比如 Carmichael 第一个 561 = 3*11*17, 而 3^560%561 = 375 ≠ 1 。可以程序验证这个。 所以我认为,a^(n-1)%n = 1 的检查比 a^n%n = a 的检查更严格,是不是不存在合数通过完全的 a^(n-1)%n = 1 的检查呢?我不敢说。但把这个结论暂时记在这里,希望能得到帮助或者反驳。2008-04-02 23:56



1.29


(define (simpson f a b n)
  (define (get-h) (/ (- b a) n))
  (define (get-y k) (f (+ a (* k (get-h)))))
  (define (simpson-term k)
    (cond ((
=  k  0 ) (get-y k))
          ((
=  k n) (get-y k))
          ((
=  (remainder k  2 0 ) (*  2.0  (get-y k)))
          (else (* 
4.0  (get-y k)))))
  (define (simpson-next k) (+ k 
1 ))
  (* (/ (get-h) 
3.0 ) (sum simpson-term  0  simpson-next n))) 

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

; ;递归
(define (product-re term a next b)
  (if (> a b)
      
1
      (* (term a)
         (product-re term (next a) next b))))
; ;迭代
(define (product term a next b)
  (define (iter a result)
    (if (> a b)
        result
        (iter (next a) (* result (term a)))))
  (iter a 
1 ))

(define (pi-product b)
  (define (pi-term k) (/ (* (- k 
1 ) (+ k  1 )) k k))
  (define (pi-next k) (+ k 
2 ))
  
; ;(* 4.0 (product-re pi-term 3.0 pi-next b))) ;;递归
  (*  4.0  (product pi-term  3.0  pi-next b)))       ; ;迭代


1.32

(define (sum term a next b)
  (accumulate + 
0  term a next b))

(define (product term a next b)
  (accumulate * 
1  term a next b))

; ;递归
(define (accumulate-re combiner null-value term a next b)
  (if (> a b)
      null-value
      (combiner (term a)
                (accumulate-re combiner null-value term (next a) next b))))

; ;迭代
(define (accumulate combiner null-value term a next b)
  (define (iter a result)
    (if (> a b)
        result
        (iter (next a) (combiner (term a) result))))
  (iter a null-value))

1.33

(define (filtered-accumulate combiner null-value term a next b filter?)
  (define (iter a result)
    (if (> a b)
        result
        (if (filter? (term a))
            (iter (next a) (combiner (term a) result))
            (iter (next a) result))))
  (iter a null-value))

(define (sum-prime a b)
  (define (sum-prime-term k) k)
  (define (sum-prime-next k) (+ k 
1 ))
  (filtered-accumulate + 
0  sum-prime-term a sum-prime-next b prime?))

(define (relatively-prime-product n)
  (define (relatively-prime? k) (
=  (gcd k n)  1 ))
  (define (term k) k)
  (define (next k) (+ k 
1 ))
  (filtered-accumulate * 
1  term  2  next (- n  1 ) relatively-prime?))


1.34

这个展开过程为:

(f f)
(f 2)
(2 2)

解释器将报错,‘2’是一个未定义过程。

1.35

若 φ=0 , 则 φ^2=φ+1 不成立 , 故 φ≠0
φ^2 = φ+1 ==>
φ = (φ+1)/φ = 1 + (1/φ)

(fixed-point (lambda(x) (+ 1 (/ 1 x))) 1.0)

1.36

(define tolerance  0.00001 )

(define (fixed-point f first-guess)
  (define (close-enough? x y)
    (< (abs (- x y)) tolerance))
  (define (try guess)
    (let ((next (f guess)))
      (display next)
      (newline)
      (if (close-enough? guess next)
          next
          (try next))))
  (try first-guess))

平均阻尼法和不用平均阻尼分别如下,它们步数分别为 9 和 34 。

(fixed-point (lambda(x) (/ (+ x (/ (log  1000 ) (log x)))  2 ))  2.0 )
(fixed-point (lambda(x) (/ (log 
1000 ) (log x)))  2.0 )

1.37

(define (cont-frac-r n d k)
  (define (redu i)
    (if (
=  i k)
        (/ (n i) (d i))
        (/ (n i) (+ (d i) (redu n d (+ i 
1 ))))))
  (redu 
1 ))

(define (cont-frac n d k)
  (define (iter i result)
    (if (
=  i  0
        result
        (iter (- i 
1 ) (/ (n i) (+ (d i) result)))))
  (iter k 
0 ))

(define (get-phai k)
  (/ 
1  (cont-frac (lambda(i)  1.0 ) (lambda(i)  1.0 ) k)))

(define (get-k)
  (define (iter i)
    (if (< (abs (- (get-phai i) 
1.6180 ))  0.00005 )
        i
        (iter (+ i 
1 ))))
  (iter 
1 ))

k = 11 时,精度满足 4 位 十进制数。

1.38

(define (euler-d i)
  (cond ((
=  i  2 2.0 )
        ((and (> i 
2 ) ( =   0  (remainder (- i  2 3 )))
         (* (/ (+ i 
1 3.0 2.0 ))
        (else 
1.0 )))

(define (get-e k)
  (+ 
2  (cont-frac (lambda(i)  1.0 ) euler-d k)))

1.39

(define (tan-cf x k)
  (define (tan-n i)
    (if (
=   1  i)
        x
        (- (* x x))))
  (cont-frac tan-n (lambda(i) (- (* i 
2.0 1.0 )) k))
; ;;;;;;;;;;
;
1.41
(define (double f)
  (lambda(x) (f (f x))))
; ;(((double (double double)) inc) 5) = 5+16 =21

; ;;;;;;;;;;;;
;
1.42
(define (compose f g)
  (lambda(x) (f (g x))))

; ;;;;;;;;;;;;;;
;
1.43
(define (repeated f n)
  (if(
=  n  1 ) f
     (compose f (repeated f (- n 
1 )))))

; ;;;;;;;;;;;;;;;
;
1.44
(define (smooth f)
  (lambda(x) (/ (+ (f (- x dx))
                   (f x)
                   (f (+ x dx)))
                
3 )))
(define (smooth-n f)
  (repeated f n))
(define (smooth-n f n)
  ((repeated smooth n) f))


2.01

(define (make-rat x y)
  (let ((g (gcd x y)))
    (if (< y 
0 )
        (cons (/ (- x) g) (/ (- y) g))
        (cons (/ x g) (/ y g))))) 

2.02

(define (make-point x y) (cons x y))
(define (x-point p) (car p))
(define (y-point p) (cdr p))

(define (make-segment p1 p2) (cons p1 p2))
(define (start-seg line) (car line))
(define (end-seg line) (cdr line))

(define (midpoint-segment line)
  (make-point (/ (+ (x-point (start-seg line)) (x-point (end-seg line))) 
2.0 )
              (/ (+ (y-point (start-seg line)) (y-point (end-seg line))) 
2.0 ))) 

2.04

; ;;;;;;;;;;;;;;;;;;;;;;;;
;
 (cdr+ (cons+ x y) = ((cons+ x y) (lambda(p q) p)))
;
            = (lambda(m)(m x y) (lambda(p q) p)))
;
            = ((lambda(p q) p) x y)
;
            = x
(define (cons+ x y)
  (lambda(m) (m x y)))
(define (car+ z)
  (z (lambda(p q) p)))
(define (cdr+ z)
  (z (lambda(p q) q))) 

2.05

   2 ^a *  3 ^b  =   2 ^c *  3 ^d (a! = c && b! = d)
  
2 ^a/ 2 ^c  =   3 ^d/ 3 ^b
  
2 ^(a-c)  =   3 ^(d-b)
  a
= c && d = b

2.06

(define one (lambda(f) (lambda(x) (f x))))
(define two (lambda(f) (lambda(x) (f (f x))))) 

2.07

(define (upper-bound pair)
  (if (> (car pair) (cdr pair))
      (car pair)
      (cdr pair)))
(define (lower-bound pair)
  (if (> (car pair) (cdr pair))
      (cdr pair)
      (car pair))) 

2.08

(define (sub-interval x y)
  (add-interval x (make-interval (- (upper-bound y))
                                 (- (lower-bound y)))))
2.17

(define (last-pair lst)
  (if (null? (cdr lst))
      (list (car lst))
      (last-pair (cdr lst))))

2.18


(define (reverse lst)
  (define (iter lst-o lst-d)
    (if (null? lst-o)
        lst-d
        (iter (cdr lst-o) (cons (car lst-o) lst-d))))
  (iter lst null))

2.20

(define (same-parity x . lst)
  (define (filter lst ok?)
    (if (null? lst)
        ()
        (if (ok? (car lst))
            (cons (car lst) (filter (cdr lst) ok?))
            (filter (cdr lst) ok?))))
  (if (even? x)
      (cons x (filter lst (lambda(x) (= 0 (remainder x 2)))))
      (cons x (filter lst (lambda(x) (= 1 (remainder x 2)))))))

2.21

(define (square-list- items)
  (if (null? items)
      ()
      (cons (* (car items) (car items))
            (square-list- (cdr items)))))

(define (square-list items)
  (map (lambda(x) (* x x)) items))

2.22

第一种每次取出首元素平方后前插到新表,象reverse过程类似,所以是反的。
第二种只不过是把新表前插到元素前,得到的甚至不是一个list,而是
  ((((() . 1) . 4) . 9) . 16)

2.23

(define (for-each proc items)
  (if (not (null? items))
      ((lambda() (proc (car items))
       (for-each proc (cdr items))))))




2.25

(car (cdaddr (list  1   3  (list  5   7 9 )))
(caar (list (list 
7 )))
(cadadr (cadadr(cadadr (list 
1  (list  2  (list  3  (list  4  (list  5  (list  6   7 )))))))))

2.26

( 1   2   3   4   5   6 )
((
1   2   3 4   5   6 )
((
1   2   3 ) ( 4   5   6 ))
2.27
(define (deep-reverse lst)
  (define (iter lst-o lst-d)
    (cond ((null? lst-o) 
           lst-d)
          ((not (pair? (car lst-o))) 
           (iter (cdr lst-o)
                 (cons (car lst-o) lst-d)))
          (else 
           (iter (cdr lst-o) 
                 (cons (deep-reverse (car lst-o))
                       lst-d)))))
  (iter lst null))

2.28
(define (fringe x)
  (define (iter tree lst)
    (cond ((null? tree) lst)
          ((not (pair? tree)) (cons tree lst))
          (else (iter (car tree) (iter (cdr tree) lst)))))
  (iter x null))

2.30
(define (square-tree- x)
  (cond ((null? x) null)
        ((not (pair? x)) (* x x))
        (else (cons (square-tree- (car x))
                    (square-tree- (cdr x))))))
(define (square-tree x)
  (map (lambda(subtree)
         (if (pair? subtree)
             (square-tree subtree)
             (* subtree subtree)))
       x))

2.31
(define (tree-map proc tree)
  (map (lambda(subtree)
         (if (pair? subtree)
             (tree-map proc subtree)
             (proc subtree)))
       tree))
(define (square-tree+ tree)
  (tree-map (lambda(x) (* x x)) tree))

2.32
(define (subsets s)
  (if (null? s)
      (list null)
      (let ((rest (subsets (cdr s))))
        (append rest (map (lambda(x) (cons (car s) x)) rest)))))

和换零钱问题的思路是一样的,对于一个集合的所有子集的集合,可以分为两部分,含有第一个元素和不含第一个元素的集合。而且含第一个元素的所有子集除去第一个元素,恰好正是所有不含第一个元素的子集。

也可以换个思路,对于集合A,设它可以表示为 (a1)∪(a2,...,an) ,而 (a2,...,an) 的所有子集的集合是 B=(B1,...Bm),那么可以证明A的所有子集的集合 C=B∪((A1)∪B1,(A1)∪B2,...,(A1)∪Bm);
证明:设 X 是 A 的一个子集,那么如果 a1∈X,那么 X
((A1)∪B1,(A1)∪B2,...,(A1)∪Bm),否则 X ∈B,所以
   
X ∈C


2.33

(define (map+ p seque)
  (accumulate (lambda(x y) (cons (p x) y)) null seque))

(define (append seq1 seq2)
  (accumulate cons seq2 seq1))

(define (length seque)
  (accumulate (lambda(x y) (+ 
1  y))  0  seque))

2.34

(define (horner-eval x coeff-seque)
  (accumulate (lambda(this-coeff higher-coeff) 
                (+ this-coeff (* x higher-coeff)))
              
0  coeff-seque))

2.35

(define (count-leaves+ t)
  (accumulate + 
0  (map (lambda(x)
                         (if (pair? x) (count-leaves+ x) 
1 ))
                       t)))
2.36
(define s (list (list  1   2   3 )(list  4   5   6 )(list  7   8   9 )(list  10   11   12 )))
(define (accumulate-n op init seqs)
  (if (null? (car seqs)) null
      (cons (accumulate op init (map# (lambda(x) (car x)) seqs))
            (accumulate-n op init (map# (lambda(x) (cdr x)) seqs)))))

2.38
; > (fold-right / 1 (list 1 2 3))
;
3/2
;
> (fold-left / 1 (list 1 2 3))
;
1/6
;
> (fold-right list null (list 1 2 3))
;
(1 (2 (3 ())))
;
> (fold-left list null (list 1 2 3))
;
(((() 1) 2) 3)

;  (fold-right op i (a b c)) = (op a (op b (op c i)))
;
 (fold-left op i (a b c))  = (op (op (op i a) b) c)

要 fold-right 和 fold-left 得到相同的结果,显然需要 op 满足交换律。

2.39
(define (reverse- 1  seqs)
  (fold-right (lambda(x y) (append y (list x))) null seqs))
(define (reverse-
2  seqs)
  (fold-left (lambda(x y) (cons y x)) null seqs))

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值