免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: win_hate
打印 上一主题 下一主题

SICP 习题参考答案,3.5 [复制链接]

论坛徽章:
0
31 [报告]
发表于 2009-02-01 11:05 |只看该作者

Exercise 3.64. 答案


  1. (define (stream-limit s t)
  2.   (define (f a s)
  3.     (let ((b (stream-car s)))
  4.       (if (< (abs (- a b)) t) b
  5.           (f b (stream-cdr s)))))
  6.   (f (stream-car s) (stream-cdr s)))


  7. (define (sqrt x tolerance)
  8.   (stream-limit (sqrt-stream x) tolerance))
复制代码


演示


  1. > (sqrt 2 0.00001)
  2. 1.41421356237469
复制代码

论坛徽章:
0
32 [报告]
发表于 2009-02-01 11:07 |只看该作者

Exerciese 3.65.

Exercise 3.65.  Use the series



to compute three sequences of approximations to the natural logarithm of 2, in the same way we did above for . How rapidly do these sequences converge?

论坛徽章:
0
33 [报告]
发表于 2009-02-01 11:16 |只看该作者

Exerciese 3.65. 答案



    1. (define harmonic-float
    2.   (stream-map (lambda (x) (/ 1.0 x)) integers))

    3. (define (alternizer s)
    4.   (let* ((a (stream-car s))
    5.         (s1 (stream-cdr s))
    6.         (b (stream-car s1)))
    7.     (cons-stream a
    8.                  (cons-stream (* -1 b)
    9.                               (alternizer (stream-cdr s1))))))
    复制代码


    alternizer 作用在一个流上,把奇数项保持不变,偶数项乘上 -1.

    harmonic-float 生成调和级数。

    把 alternizer 作用在调和级数上就得到了 ln 2 对应的级数


    1. (define log2-series
    2.   (alternizer harmonic-float))

    3. (define log2-stream
    4.   (partial-sums log2-series))
    复制代码

  • 加速和不加速的版本


    1. (define s1 log2-stream)
    2. (define s2 (euler-transform s1))
    3. (define s3 (accelerated-sequence euler-transform
    4.                                  s1))
    复制代码

  • 收敛速度对比:


    1. > (log 2)
    2. 0.693147180559945

    3. > (print-stream s1 10)
    4. 1.0
    5. 0.5
    6. 0.833333333333333
    7. 0.583333333333333
    8. 0.783333333333333
    9. 0.616666666666667
    10. 0.759523809523809
    11. 0.634523809523809
    12. 0.745634920634921
    13. 0.645634920634921

    14. > (print-stream s2 10)
    15. 0.7
    16. 0.690476190476191
    17. 0.694444444444444
    18. 0.692424242424242
    19. 0.693589743589744
    20. 0.692857142857143
    21. 0.693347338935574
    22. 0.693003341687552
    23. 0.693253968253968
    24. 0.693065750674446

    25. > (print-stream s3 10)
    26. 1.0
    27. 0.7
    28. 0.69327731092437
    29. 0.693148869332925
    30. 0.693147196073549
    31. 0.693147180663564
    32. 0.693147180560404
    33. 0.693147180559945
    34. 0.693147180559943
    35. 0.693147180559945
    复制代码

  • 继续对比


    1. > (log 2)
    2. 0.693147180559945

    3. > (stream-ref s1 10000)
    4. 0.693197173060958

    5. > (stream-ref s2 100)
    6. 0.693147296621065

    7. > (stream-ref s3 10)
    8. +nan.0                            ;; orz, what is this?

    9. > (stream-ref s3 9)
    10. 0.693147180559945

    11. > (log 2)
    12. 0.693147180559945
    复制代码


    可以看到 s3 中只用了 10 项就准确地计算出了 log 2 的 15 位小数。而未加速的 s1 用了 10000 项才得到 7 个准确的数字。

    现在看看那个 +nan.0


    1. (print-stream s3 20)
    2. 1.0
    3. 0.7
    4. 0.69327731092437
    5. 0.693148869332925
    6. 0.693147196073549
    7. 0.693147180663564
    8. 0.693147180560404
    9. 0.693147180559945
    10. 0.693147180559943
    11. 0.693147180559945
    12. +nan.0
    13. +nan.0
    14. +nan.0
    15. +nan.0
    16. +nan.0
    17. +nan.0
    18. +nan.0
    19. +nan.0
    20. +nan.0
    21. +nan.0
    复制代码


    可以看到,从第 11 项起,得到的都是 +nan.0。Euler 加速公式中有 (s(n+1)-s(n))^2,我猜测这是增量太小引发的下溢。
  • 最后我们来估计一下 s3 的复杂度。因为这个序列中的一个项,是通过多次欧拉变换得到的。为求出 s3 中的第 n 项,实际需要计算多少个项呢?

    从序列 A 欧拉变换到 B,如果有 A 中的前 n 项,可以得到 B 中的 n-2 项。设 s3 形式为:S(0), S(1), ...., S(n), ....

    则求 S(0) 需要原始序列 1 项,求 S(1) 需要原始序列 3 项,求 S(2) 需要原始序列 5 项,...,求 S(n) 需要原始序列 2n+1 项。

    所以原始序列计算 2n+1 项,然后进行 n 次欧拉变换,每次变换出来的序列比前一序列项数少 2,直到剩下 1 项。总共要计算

    (2n+1)+(2n-1)+...+5+3+1 = (n+1)^2 项。


[ 本帖最后由 win_hate 于 2009-2-1 12:00 编辑 ]

论坛徽章:
0
34 [报告]
发表于 2009-02-01 12:02 |只看该作者

Exercise 3.66.

Exercise 3.66.  Examine the stream (pairs integers integers). Can you make any general comments about the order in which the pairs are placed into the stream? For example, about how many pairs precede the pair (1,100)? the pair (99,100)? the pair (100,100)? (If you can make precise mathematical statements here, all the better. But feel free to give more qualitative answers if you find yourself getting bogged down.)

论坛徽章:
0
35 [报告]
发表于 2009-02-01 12:08 |只看该作者

Exercise 3.66. 答案

  • 分析:

    考虑 f(i,j)。设从第 i 行起,共有 T 个已处理元素。则

    设从第 n 行起往后,已处理元素个数为 S(n),有

    S(n)=0, n>i
    S(n)=S(n+1)*2+1, n<i

    容易计算出 S(1) = 2^(i-1)S(i)+2^(i-1)-1.

    如果 i=j,则 S(i)=1, S(1)=2^i-1

    如果 i<j,则 在第 i 行,有 j-i+1 个元素,在下面的行,有 (j-i+1)-2 = i-j-1 个元素。

    所以 i 以及后面的行有 S(i)=2(j-i) 个元素。

    S(1)=2^i(j-i)+2^(i-1)+1.
  • 代码

    1. (define (f i j)
    2.   (let* ((T (if (= i j) 1 (* 2 (- j i))))
    3.          (d (expt 2 (- i 1))))
    4.     (- (* (+ T 1) d) 1)))
    复制代码

  • 演示:


    1. > (stream-ref x (- (f 1 10) 1))
    2. (1 10)
    3. > (stream-ref x (- (f 2 10) 1))
    4. (2 10)
    5. > (stream-ref x (- (f 3 10) 1))
    6. (3 10)
    7. > (stream-ref x (- (f 4 10) 1))
    8. (4 10)
    9. > (stream-ref x (- (f 5 10) 1))
    10. (5 10)
    11. > (stream-ref x (- (f 6 10) 1))
    12. (6 10)
    13. > (stream-ref x (- (f 7 10) 1))
    14. (7 10)
    15. > (stream-ref x (- (f 8 10) 1))
    16. (8 10)
    17. > (stream-ref x (- (f 9 10) 1))
    18. (9 10)

    19. > (stream-ref x (- (f 10 10) 1))
    20. (10 10)
    21. > (stream-ref x (- (f 9 9) 1))
    22. (9 9)
    23. > (stream-ref x (- (f 8 8) 1))
    24. (8 8)
    25. > (stream-ref x (- (f 7 7) 1))
    26. (7 7)
    27. > (stream-ref x (- (f 6 6) 1))
    28. (6 6)
    29. > (stream-ref x (- (f 5 5) 1))
    30. (5 5)
    31. > (stream-ref x (- (f 4 4) 1))
    32. (4 4)
    33. > (stream-ref x (- (f 3 3) 1))
    34. (3 3)
    35. > (stream-ref x (- (f 2 2) 1))
    36. (2 2)
    37. > (stream-ref x (- (f 1 1) 1))
    38. (1 1)
    复制代码

  • 附带一个前 2^n 个数的分布图。
    pairs.figure.pdf (26.69 KB, 下载次数: 28)


[ 本帖最后由 win_hate 于 2009-2-1 12:12 编辑 ]

论坛徽章:
0
36 [报告]
发表于 2009-02-01 12:14 |只看该作者

Exercise 3.67.

Exercise 3.67.  Modify the pairs procedure so that (pairs integers integers) will produce the stream of all pairs of integers (i,j) (without the condition i < j). Hint: You will need to mix in an additional stream.

论坛徽章:
0
37 [报告]
发表于 2009-02-01 12:46 |只看该作者

Exercise 3.67. 答案

  • 分析:简单的处理为交替从 3 个流中取数。我们的 interleave 只混合两个流,可以把它扩充成混合多个流的版本:


    1. (define (interleaves . s)
    2.   (cons-stream
    3.    (stream-car (car s))
    4.    (apply interleaves (append (cdr s) (list (stream-cdr (car s)))))))

    5. (define (all-pairs s1 s2)
    6.   (cons-stream
    7.    (list (stream-car s1) (stream-car s2))
    8.    (interleaves
    9.     (stream-map (lambda (x) (list (stream-car s1) x)) (stream-cdr s2))
    10.     (stream-map (lambda (x) (list x (stream-car s2))) (stream-cdr s1))
    11.     (all-pairs (stream-cdr s1) (stream-cdr s2)))))
    复制代码

  • 演示


    1. > (define x (all-pairs integers integers))
    2. > (take x 10)
    3. ((1 1) (1 2) (2 1) (2 2) (1 3) (3 1) (2 3) (1 4) (4 1) (3 2))
    复制代码

  • 另外几个思路。

    • 先把两个流混合起来,再与第三个流混合。

      1. (define (pairs-all s1 s2)
      2.   (cons-stream
      3.    (list (stream-car s1) (stream-car s2))
      4.    (interleaves
      5.     (interleaves
      6.      (stream-map (lambda (x) (list (stream-car s1) x)) (stream-cdr s2))
      7.      (stream-map (lambda (x) (list x (stream-car s2))) (stream-cdr s1)))
      8.     (pairs-all (stream-cdr s1) (stream-cdr s2)))))
      复制代码

    • 直接生成两个流混合后的结果,然后与第三个混合。

      1. (define (ya-pairs-all s1 s2)
      2.   (define (f x s)
      3.     (cons-stream (list x (stream-car s))
      4.                  (cons-stream (list (stream-car s) x)
      5.                               (f x (stream-cdr s)))))
      6.   (cons-stream
      7.    (list (stream-car s1) (stream-car s2))
      8.    (interleaves
      9.     (f (stream-car s1) (stream-cdr s2))
      10.     (ya-pairs-all (stream-cdr s1) (stream-cdr s2)))))
      复制代码

    • 前一个做法的推广,既然我们能直接生成两个流的混合(没有直接使用 interleave),能不能直接生成三个流混合的结果呢? pairs 生成的流是 (i, j), 其中 i<=j; 我们只要遍历这个流,并把其中分量不等的项反转、插入,就能得到所需的结果。


      1. (define (all-pairs-1 s1 s2)
      2.   (define (f s)
      3.     (let ((h (stream-car s)))
      4.       (if (= (car h) (cadr h))
      5.           (cons-stream h (f (stream-cdr s)))
      6.           (cons-stream h
      7.                        (cons-stream (reverse h)
      8.                                     (f (stream-cdr s)))))))
      9.   (f (pairs s1 s2)))
      复制代码




[ 本帖最后由 win_hate 于 2009-2-2 12:35 编辑 ]

论坛徽章:
0
38 [报告]
发表于 2009-02-01 12:48 |只看该作者

Exercise 3.68.

Exercise 3.68.  Louis Reasoner thinks that building a stream of pairs from three parts is unnecessarily complicated. Instead of separating the pair (S0,T0) from the rest of the pairs in the first row, he proposes to work with the whole first row, as follows:

(define (pairs s t)
  (interleave
   (stream-map (lambda (x) (list (stream-car s) x))
               t)
   (pairs (stream-cdr s) (stream-cdr t))))

Does this work? Consider what happens if we evaluate (pairs integers integers) using Louis's definition of pairs.

论坛徽章:
0
39 [报告]
发表于 2009-02-01 13:05 |只看该作者

Exercise 3.68. 答案

  • 分析:

    1. (define (pairs s t)
    2.   (interleave
    3.    (stream-map (lambda (x) (list (stream-car s) x))
    4.                t)
    5.    (pairs (stream-cdr s) (stream-cdr t))))
    复制代码


    问题出在对 interleave 的调用和 pairs 的自引用上。

    • interleave 是函数,所以先求值其参数,
    • stream-map 调用 cons-stream,它是个宏,不对参数求值,所以能终止。
    • 但 interleave 对第二个参数的求值 (pairs (stream-cdr s) (stream-cdr t)) 不会终止。它会不断调用 pairs,直到耗尽所有空间。


    对比一下原来的 pairs


    1. (define (pairs s t)
    2.   (cons-stream
    3.    (list (stream-car s) (stream-car t))
    4.    (interleave
    5.     (stream-map (lambda (x) (list (stream-car s) x))
    6.                (stream-cdr t))
    7.     (pairs (stream-cdr s) (stream-cdr t)))))
    复制代码


    这里也存在 interleave 和 pairs,但此时是包含在一个 cons-stream 里的。cons-stream 是宏,不会对参数穷追猛打。
  • 其实 Louis Reasoner 的方案比我们最初的方案更为自然。但由于 scheme 的函数调用是严格的,所以他的方案行不通。
    如果我们更进一步,把 scheme 的函数调用也修改为惰性的,那么 Louis Reasoner 的方案就可行了。

    用 lazy scheme 来试一下:


    1. #lang lazy

    2. (define (interleave s1 s2)
    3.   (cons (car s1)
    4.         (interleave s2 (cdr s1))))

    5. (define (pairs s t)
    6.   (interleave
    7.    (map (lambda (x) (list (car s) x))
    8.         t)

    9.    (pairs (cdr s) (cdr t))))

    10. (define (integers-starting-from n)
    11.   (cons n (integers-starting-from (+ n 1))))

    12. (define integers (integers-starting-from 1))
    复制代码


    这里要使用 mzsheme 解释器。


    1. > (define x (pairs integers integers))
    2. > (display (take 10 x))
    3. ((1 1) (2 2) (1 2) (3 3) (1 3) (2 3) (1 4) (4 4) (1 5) (2 4))
    复制代码



[ 本帖最后由 win_hate 于 2009-2-1 13:07 编辑 ]

论坛徽章:
0
40 [报告]
发表于 2009-02-03 19:31 |只看该作者

Exercise 3.69.

Exercise 3.69.  Write a procedure triples that takes three infinite streams, S, T, and U, and produces the stream of triples (S_i,T_j,U_k) such that i < j < k. Use triples to generate the stream of all Pythagorean triples of positive integers, i.e., the triples (i,j,k) such that i < j and i^2 + j^2 = k^2.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP