sicp 第一章习题试做

这是我当时学SICP时候作习题时候的总结。不是很详细,有时间再把代码贴上来。

 

Exercise 1.5 :
问题描述:关于scheme的 substitution model(a way to get started thinking formally about the
evaluation problem)的问题,第一章介绍了两种表达式evaluation的顺序:normal order和applicative order.
normal order: fully expand and the reduce. 即先将符号完全扩展后,然后再将参数代入求值。
applicative order: evaluate the arguments and the apply。即将参数求值后,然后再扩展。
这个习题的程序可以测试解释器采用的problem evaluation order么。我怎么没有看出来捏!
确实可以看出来
applicative order的结果是0
Normal order的结果是无限的循环,revl就象死机了一样。

Exercise 1.6 :
问题描述:用cond 语句 来实现 if 语句在大多数情况下是没有问题的,但是题目的意思是在sqrt-iter函数中使用new-if函
数存在bug,问题出现在什么地方呢?直到现在还没有被发现。

Exercise 1.7 :
问题描述:牛顿法求sqrt的循环收敛条件 goodenough?的实现(define goodenough? (< (abs (- (square guess) x))
0.001) ) 对特别大的值和特别小的值使用都分别有问题存在。
对特别大的值:循环不停止。
对特别小的值:循环过早停止。取x->0 求(sqrt x), goodenough过程将每次的 guess值的平方与x值作比较,当
guess的值的平方自身小于0.001时,循环停止了,这是用sqrt过程求得的x的平方根是不正确的值。比如
(sqrt 0.001)的值是0.0312500011,这个答案是明显不正确的。
问题解决:问题关键在于0.001这个值的选取,应该将其设置为与x相关的值.比如
(define goodenough? (< (abs (- (square guess) x) (/ x 100000000000)))就能够解决部分问题。依旧存在问
题的是误差设定依然存在100000000000这个常数,当X超级大时,误差会变得很大。如果依旧要采用goodenough?目
前的误差方法,有必要对输入值进行进一步区分,分别选取合适的误差。
另外一种方法是通过判断两次循环之间的guess值之差小于guess值的百分数时,然后循环停止。

Exercise 1.9:
问题描述:判断process(与procedure的区别)是recursive的还是iterative的。在理解问题之前需要了解几个问题
1)我们提到procedure是recursive的,表示程序的文法是recursive的。提到process是recursive的,表示程序执
行的过程(process)是recursive的。
2)recursive和iterative的区别。recursive的特征是process存在一串需要未来处理的计算,iterative的特征是
process通过维护特定的几个变量来控制循环的运行和终止。
substitution model:
(+ 4 5)                     (+ 4 5)
(+ 3 5)                     (+ 3 6)
(+ 2 5)                     (+ 2 7)
(+ 1 5)                     (+ 1 8)
(+ 0 5)                     (+ 0 9)
6                            9
7
8
9
在扩展的时候,两种方法都经过了5次扩展,但是在求返回值的时候,方式1确实比方式2多了几个步骤,就是因为
方式1中的一个语句(inc (+ (dec a) b)) 这个语句对迭代子问题的结果进行了处理,而方式2对迭代子问题进行了
直接返回。
从substitution model可以看出,虽然两个方案在文法上都是recursive的,但是方案2是iterative的,而方案1是
recursive的.
tail-recursive,早就听过尾递归的大名,难道就是这种以recursive-procedure形式存在的recursive-process
不过,现在确实知道了递归化循环的真正意思了。

Exercise 1.10:
问题描述:这个习题介绍了一个Ackermann函数。似乎这个函数有些有趣的性质。(A 1 n) = 2^n; (A 0 n)=2*n;
(A 2 n)不好猜呀。期待标准答案揭晓。

Exercise 1.11
问题描述:分别用迭代和循环的方式实现一个功能。
recursive process:
(define (rec-f n)
  (cond ((< n 3) n)
    (else (+ (rec-f (- n 1))
         (* (rec-f (- n 2)) 2)
         (* (rec-f (- n 3)) 3)
         )
          )
    )
  )
iterative process:
(define (iter-f n)
  (cond ((< n 3) n)
      (else (f-iter 0 1 2 (- n 2)))))

(define (f-iter a b c count)
  (cond ((= count 0) c)
    (else (f-iter b c (+ c (* 2 b) (* 3 a)) (- count 1)))))
主要注意初始值当n<3时 f(n)=n,当n小于3时,不需要循环,iterative process需要定义两个函数iter-f和
f-iter的作用就在于分开处理n<3和n>3的情形,目前没有发现使用一个函数解决问题的方法。

Exercise 1.12
问题不难,略。

Exercise 1.13
问题描述:对fibonacci函数的闭形式的总结,以后如果接触fibonacci函数再作深入分析。
<<具体数学>>中有使用生成函数来求fibonacci函数闭合形式的方法。

Exercise 1.14
问题描述:linear recursive的process时间复杂度是O(n),空间复杂度是O(1),似乎Tree recursive的process的时间复杂度和
空间复杂度可以用tree来表示,一般来说tree recursive的process需要运行tree的所有节点,所以时间复杂度是Exponentiation
的,空间复杂度与tree的Max depth成比例。
11cent 的count change
                                f(11,5)
                        f(11,4)                     f(11-50,5)
                    f(11,3)              f(11-25,4)
         f(11,2)         f(1,3)=1
       f(11,1)         f(6,2)
f(11,0) f(10,1)  f(6,1)       f(5,2)
       f(10,0)f(9,1)           f(6,0)f(5,1) f(5,1)f(
         f(9,0)  
这个图没有画完,但是我已经了解到了这个过程是很繁琐的,时间复杂度很不理想,改进的办法之一是将一些简单
的结果作为循环结束的条件,比如f(10,1)=1,这种很容易得到答案的结果应该作为循环结束的条件,而不是将
f(10,1)再进一步分解继续求值。
迭代是个好东西!

Exercise 1.15
还未做。

Exercise 1.16
问题描述:使用循环实现exponentiation,时间复杂度为logarithmic.这个问题很有代表性,估计可以作为时间复
杂度为logarithmic的iterative process的模式了。
(define (fast-iter-expt b n)
    (expt-iter 1 b n))

(define (expt-iter a b n)
  (cond ((= n 0) a)
    ((even? n) (expt-iter a (square b) (/ n 2)))
    (else (expt-iter (* a b) b (- n 1))))
    )                                  
invariant quantity: a * b^n

Exercise 1.17
问题描述:使用象1.16的方法来实现加法模拟的乘法运算。

Exercise 1.19
问题描述:因为前面求fibonacci函数的方法时间复杂度分别为O(Power(n)) O(n),本题提供一种O(log(n))的方
法。但是如何实现我还没有分析出来。这个好像很难!留作思考题目!

Exercise 1.20
问题描述:用interpreter应用的substitution model来分析(gcd 206 40)
(define (gcd a b)
  (if ((= b 0)
    a)
  (gcd b (remainder a b))))
applicative order:
(gcd 206 40)
    (gcd 40 6)
        (gcd 6 4)
            (gcd 4 2)
                (gcd 2 0)
Normal order:
(gcd 206 40)
    (gcd 40 (remainder 206 40)
        (gcd (remainder 206 40) (remainder 40 (remainder 206 40)))
            未完...
Normal order的意思是fully expand and Then evaluate,似乎不求值,这个expand的过程就不会停止的!似乎遇
到迭代,这个过程是不会顺利进行的。

Exercise 1.21
问题描述:在1.2中介绍了两种查找divisor的方法,一种是small-divisor,需要花费n^(1/2) steps。 另外一种是
基于fermat little Theorem的有一定概率失败的方法,需要花费(log(n)) steps,这种方法虽然高效,但是有失
败的风险,这一章没有研究各种情况的失败的概率情况,The existence of tests for which one can prove
that the chance of error becomes arbitrarily small has sparked interest in algorithms of this
type,which have come to be known as probabilistic algorithms。Probalilistic algorithms是我研究的另外
一个大的目标。
这道习题很简单,用写好的procedure来验证几个值而已。

Exercise 1.22
问题描述:这个习题很有意义,教我如何测量时间。使用了一个scheme的基础函数(runtime)用来计算运行的时间
和(display x)函数来打印Trace。

Exercise 1.29
问题描述:本节介绍了高阶函数的概念,就是将函数名作为函数的实参。函数名可以被用作函数的参数传递。
本题描述的Simpson法则的数值方法来近似作积分运算。
代码位置: Simpson_rule.scm

Exercise 1.30
问题描述:本题又是一个将recursive的函数转换为iterative的。
recursive process与iterative process在形式上是很相近的,recursive process形式简单直观,iterative
process需要考虑用额外的状态变量来保存中间过程,循环变换的时候还存在一个state transformation的概念。
比如fibonacci 函数的state Transformation值是:
a <- a + b = F(n+1)
b <- a     = F(n) 这是循环不变量
然后传递一个循环变量值n,n在每次循环都会递减
最后要考虑的是循环中止条件:((= n 0) 0) ((= n 1) 1)
iterative process有些使用参数作为实现循环不变量和循环功能的意味,而recursive process用返回值来实现循
环不变量和循环功能的意味。
代码位置: Simpson_rule.scm

Exercise 1.36
问题描述:修改fix-point程序,添加(newline) 和(display message)函数就可以了。似乎fix-point理论在数值方
法中占了很重要的作用。
average damping在fix-point程序的迭代过程中起到了加速收敛的作用,相当于增加了系统的阻尼比,好神奇呀!
代码位置: fix-point.scm

Exercise 1.37
代码位置: fix-point.scm

Exercise 1.38
问题描述:使用cont-frac函数求自然对数值e的近似值
代码位置: fix-point.scm

Exercise 1.39
问题描述:使用cont-frac函数求tanx的近似值
代码位置: fix-point.scm

总结:
第一章书中介绍了问题解决的原则:大事化小。关键在于如何化小,计算机程序必须有秩序,结构良好,迭代和递
归是大事化小的两种方法。递归和迭代件输入分成小规模的的部分,这些小问题的解决为子问题,对所有的的子问
题使用同样的方法解决,然后将解决的子问题合并为解决的整体。
问题求解的方法包括:
(1)输入化小的方法
(2)子问题解决的方法
(3)问题解决合并的方法
Scheme代码的结构:
基本元素是:expression
粘合剂:函数,函数的操作:high-order function、lambda 算子、Return value-function.
high-order function: 函数名可以作为函数的参数传递,也可以看作泛型的基础
lambda算子:lambda可以看作为一个通用的名字,创建一个无名函数
Return value-function: 函数体可以作为函数的返回值,返回一个无名函数
牛顿法的例子可以很好的表明Scheme提供的好的programm gluing元素的。
牛顿法:f(x) = (x - g(x)/(Dg(x)))解g(x)=0。先进行数学上的处理,g(x)=0相当于f(x) = x,这是f(x)的
fixed-pointed点。
这个问题包括几个层次的问题,fixed-point子问题 > f(x)的形式 > D(g(x)) > g(x),这几个层次的问题可以放到一个层次,但是没有利用模块的概念,如果我们将些问题都抽象出来,组合的时候就要用到高阶函数和Return value-function了。组合的办法是将g(x)作为D(g(x))的实际参数,D(g(x))作为fixed-point函数的实际参数。总体说来,高阶函数是Scheme的模块化的关键。函数是模块化的基本函数,高阶函数是模块化的粘合剂。
具体例子的编码可以看Newton-method.scm 里面有两个Newton-method函数,一个模块化好的,另一个就像一锅粥一样。
第一章的最后一段话很经典:
As programmers, we should be alert to opportunities to identify the underlying abstractions in our
programs and to build upon them and generalize them to create more powerful abstractions. This is
not to say that one should always write programs in the most abstract way possible; expert
programmers know how to choose the level of abstraction appropriate to their task. But it is
important to be able to think in terms of these abstractions, so that we can be ready to apply them
in new contexts. The significance of higher-order procedures is that they enable us to represent
these abstractions explicitly as elements in our programming language, so that they can be handled
just like other computational elements.In general, programming languages impose restrictions on the
ways in which computational elements can be manipulated. Elements with the fewest restrictions are
said to have first-class status. Some of the ``rights and privileges'' of first-class elements
are:
They may be named by variables.
They may be passed as arguments to procedures.
They may be returned as the results of procedures.
They may be included in data structures.
Lisp, unlike other common programming languages, awards procedures full first-class status. This
poses challenges for efficient implementation, but the resulting gain in expressive power is
enormous.
总结完毕。

Exercise 1.41
问题描述:高阶函数(high-order function)和(returned value function)作为粘合子程序的方法,似乎他们也能
够达到这样一种目的,程序的粘合与表达式的粘合一样能够表达丰富。这个例子可能想告诉我们一点就是:
returned value function可以将自身作为参数。
例子中函数double的使用很有意思(((double (double double)) inc) 6) double函数可以将其自身传递给其形参。
我将double定义为
(define (double f)
    (lambda (x) (f (f x))))
但是我确实不知道 ((double double) inc) 将会扩展为什么样的过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值