SICP 习题 (1.14)解题总结

来源:互联网 发布:游于是乎始得乎 编辑:程序博客网 时间:2024/05/16 01:17

SICP 习题 1.14要求计算出过程count-change的增长阶。count-change是书中1.2.2节讲解的用于计算零钱找换方案的过程。


要解答习题1.14,首先你需要理解count-change的工作方式,要理解count-change的工作方式,最好是自己去实现一遍count-change。


为了避免自己直接抄书中的代码,我决定自己实现一遍用来找换人民币的的“count-change”。事实上,我在看完并理解count-change的代码后,当我去实现人民币版的“count-change”时,我就强制自己不再回去看“count-change”的代码,保证自己有更多的主动思考。

有意思的是,当我实现完了回去看书上的代码,发现两者还是有挺大的区别,虽然算法是一样的,结果也都正确的。


首先我们得有个过程遍历各种零钱,我为了简化程序,只做了“元”的找换,“分”和“角”就略过了。


遍历各种零钱的过程如下,就是遍历“1元”,“2元”,“5元”,“10元”,“20元”,“50元”,“100元”这几种零钱。


这里就和书上有点差别,书上是记录现在还可以使用几种零钱,根据可以使用的零钱种类数量返回其中最大面值的金额。


我是简单粗暴地从最小面值遍历到最大面值。


(define (RMB-Change-Next-Kind change-kind)  (cond ((= change-kind 1) 2)((= change-kind 2) 5)((= change-kind 5) 10)((= change-kind 10) 20)((= change-kind 20) 50)((= change-kind 50) 100)(else 0)))


书中讲到的找换零钱的方法基于以下基本思路:


比如我们需要将100块的人民币找换成零钱,有几种找换方式?可以简单分为两种:有使用1块面额的,和没有使用1块面额的。


对于有使用1块面额的,把这一块钱去掉,剩99块,我们又可以继续看99块有几种找换方式。找到99块的所有找换方式,在加上现在这里去掉的1块钱,就是100块找换方式中有使用1块的所有找换方式。


对于没有使用1块面额的,我们就需要看看100块找成其它面额(不包含1块)的找换方式。


然后将上面两种类型的数量加起来就是100块的所有找换方式。


可以看到,以上的思路对应的是树形递归,每次递归调用分两路,一路调用的找换总额减少,一路调用的找换币种减少。


我的实现方法是下面这样的,和书上略有不同。


(define (RMB-Change-Recursive amount change-kind change-result)  (if (= amount 0) (format #t "Got one: ~S~%" change-result))  (cond ((= amount 0) 1)((< amount 0) 0)((= change-kind 0) 0)(else (+ (RMB-Change-Recursive amount (RMB-Change-Next-Kind change-kind) change-result) (RMB-Change-Recursive (- amount change-kind) change-kind (cons change-kind change-result))))))


如果对上面的过程不理解,建议回去读书中的1.2.2节,读懂为止。


好的,现在才到我们真正的题目了,第一个是找出11块的找换方式,这种比较容易,可以按以上的方式手工列出,也可以直接执行代码看看结果。


问题的第二部分比较麻烦,就是问,对于现金量的增长,以上过程的空间增长阶和时间增长阶是什么?


如果对增长阶的概念没有理解透,这题很难解。


我们先开始分析一下,如果发现过程中有不理解的,需要回去书上看看增长阶的内容。


首先来看看空间增长阶,当我们需要计算12块的找换方式时,比计算11块的找换方式需要增加多少内存?


当然,空间增长阶不能简单理解为需要增加多少内存,可能会有其它的空间需要,或者人家用纸算,根本不用内存,我们在这里就粗暴地将空间理解为内存吧。


增加多少内存呢?也不需要计算准确数字,只要计算大概的敏感度就好了。比如增长是线性的,多计算1块钱就多需要100K左右的空间。又或者是二次方曲线的增长,计算11块时需要11的二次方的空间,二计算12块时需要12的二次方的空间。这些就是我们要求的增长阶。


对于书中的count-change过程,空间增长阶是多少呢?


对于递归过程的空间增长阶,一般是去计算递归计算过程的最深深度。因为过程调用完以后,其使用的空间是会被释放的,我们需要计算的是被那些一直递归调用自己,目前还没有返回的过程所占用的空间。


同时需要注意每次递归调用的参数有没有累加,累加的形式是什么。


以上过程的递归嵌套最深的就是全部用1块来找换,嵌套深度就是要找换的金钱量,11块就是嵌套了11层,100块就是嵌套100层。而调用过程中参数没有累加,只是不断替换而已。


所以,count-change过程的空间增长阶是Theta[n]。


不过,我这里设计的过程好像有点不一样,为了打印所有可能的找换方式,我定义了一个参数用于保存目前计算过的暂时可行的零钱组合,就是参数change-resule。这个参数占用的空间是如何变化的呢?


可以发现,这个参数中的元素最多的时候就是全部找成1元的时候,这时候change-result中的值是:(1 1 1 ….  1)共有n个。而每次递归调用都有一个列表需要暂时保留,所以,当递归调用到最深层的时候,其实有n个列表,分别是(1)    (1 1)   (1 1 1) …..   (1 1 1 1 …. 1 1),所以这里占用的空间是1到n的累加。


所以我的RMB-Change过程的空间增长阶是Theta[n的累加],因为在求增长阶的时候只取多项式中最高阶的那项,Theta[n]就被忽略了。


以上是空间增长阶,那么步数的增长阶呢?或者说是时间增长阶呢?


书中习题要求的是步数的增长阶,我们一般假设执行计算的每一步都消耗等同的时间,所以时间的增长阶和步数的增长阶应该是一致的。有关这个假设是否正确后面的习题还有详细的讨论,目前暂时认为时间增长阶和步数的增长阶相同。


这个增长阶的求解过程比较复杂,我也到网上参考了好多别人的解法,有许多种思路。我总结了一个我个人比较容易理解的解法,描述如下:


以我这里的例子,找换面额种类有7种,分别是“1元”,“2元”,“5元”,“10元”,“20元”,“50元”,“100元”。

为了方便表达,我们用函数(T n m)来表示以上过程的时间复杂度,其中n是需要找换金额总数量,m是用于找换的零钱的面额的种类数量。


如果把1000元钱进行找换,可以把1000元的找换方式分为 “使用1元的”  和  “不使用1元的” 两种方式,就是:

(T 1000 7) = (T 999 7) + (T 1000 6)


而999元的找换方式又可以分为 “使用1元的”  和  “不使用1元的” 两种方式,就是:

T 999 7) = (T 998 7) + (T 999 6)


如此不断分解,就可以得到1000个分支,每个分支带一个(T n 6)。

也就是所这里的(T  n 7) =  1000 * (T n 6)   =   n * (T n 6)


再来计算(T n 6),在这里也就是( T 1000 6)

就是把1000元找换成“2元”,“5元”,“10元”,“20元”,“50元”,“100元”这6种零钱。


按上面相同的方法有:

(T 1000 6) = (T 998 6) + (T 1000 5)

(T 996 6) = (T 996 6) + (T 1000 5)

(T 994 6) = (T 994 6) + (T 1000 5)

....

按以上方法可以拆出500个分支(就是n/2个分支,因为这次是从2元开始找),每个分支带一个(T 1000 5)。


就有(T 1000 6) = n/2 * ( T 1000 5)


以此类推就有(T 1000 7) = n * ( n/2 * ( n/5 * ( n/10 * ( n/20 * (n/50 * (n/100)))))),其中的除数就是各个面额。


就有(T 1000 7 ) = (n 的 7次方)/10000000


因为我们在求增长阶,所以直接取增长阶为 (n 的 7次方)


虽然除数10000000也算是个很大的数,不过在n大到一定程度时,n 的 7次方)就变成一个超级大的数,这时10000000也就不算什么了。


这同时也符合我们观察到的现象,当n很小时,实际需要的步数远小于n 的 7次方)。



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 监视居住期间再次犯罪怎么办 前妻有贷款跑了怎么办 未成年犯有缓刑罪继续犯罪怎么办 英朗出现检修esc怎么办 照相时奖状拿反了怎么办 wps文档加密后忘记密码怎么办 wps表格文件密码忘记了怎么办 wps表格密码忘记了怎么办 发布的文案缩成一行怎么办 创维电视都是VIP怎么办 挂电视距离太远怎么办 电视离沙发太近怎么办 苹果6更新11.1卡怎么办 苹果升级变卡了怎么办 迅雷被资源举报无法加速怎么办 迅雷的敏感资源无法加速怎么办 迅雷下载敏感资源无法加速怎么办 换手机微信账户怎么办 拉链拉不合没有蜡怎么办 长杆烟斗头松动怎么办? 大王卡没流量了怎么办 晚上电视想看野外生存怎么办 假离婚后真离婚怎么办 苹果手机在车上不能充电怎么办 遥控钥匙打不开车门怎么办 苹果访问限制密码忘记了怎么办 苹果手机忘记访问限制密码怎么办 苹果忘记访问限制密码怎么办 苹果限制访问密码忘了怎么办 东奥会计书盗版怎么办 合同封印少盖一页怎么办 玩单机游戏屏幕输入不支持怎么办 汽车不小心陷进泥潭怎么办 招商银行信用卡激活电话换了怎么办 冲鼻子是耳朵进水了怎么办 不小心点了赞怎么办 小车间太热了怎么办 在车间上班好热怎么办 英雄杀四星李逵不拉仇恨怎么办 手表玻璃里面有雾水怎么办 dw手表玻璃碎了怎么办