SICP 习题 1.10 讲的是一个叫“Akermann函数”的东西,去百度查可以查到对应的中文翻译,叫“阿克曼函数”。

就像前面的解题总结中提到的,我是一个数学恐惧者,看着稍微复杂一点的什么函数我就怕。所以这道题放了很久都没去动它,不过有担心跳过这道题对后面的学习不利,所以最终还是鼓足勇气尝试做这个题目。

做完了我才发现,其实这道题真的可以跳过,做不做这道题似乎对后面的学习没什么影响。从题目的内容来看,作者应该是希望在习题中引入“树形递归”,让学生在下一节课的学习中有所准备,相当于是预习题。事实上,这个“预习题”太难了,比后面介绍的“斐波那契数”难好多,所以起不到什么“预习”的作用。

所以,如果你也害怕数学的话,可以考虑跳过这道题,就当它从来没有在你生命中出现过。

当然,如果你愿意挑战自己,和我一样尝试一下,你也会发现其实所谓的“阿克曼函数”也没什么太神秘的。

大家都说数学是“大脑的体操”,我没有数学天分,做不了“大脑的体操”,不过我慢慢爬上去,看看“大脑的单杠”啥样子还是可以的嘛。

先看看“阿克曼函数”的Scheme定义:

(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),(A2 4),(A 3 3)展开,从而总结出(A 0 n),(A 1 n)和 (A 2 n)的数学含义,因为我就是这么做出这道题的。

后来写了一半发现不对路,把那么繁琐的展开和归约步骤写下来太麻烦,大家也不会花时间去看,真是浪费时间。

所以就希望通过其它方式和大家解释这个“阿克曼函数”,不过你如果希望自己完成这个练习,像我一样那张纸直接进行展开和归约是可行的,也不会花太长时间。

另外,如果你只是希望了解“阿克曼函数”本身,建议你直接去百度搜索,那里能找到专业的解释,读起来比读程序简单。

如果你是希望了解这里定义的(A x y)过程如何简单地通过递归调用实现“阿克曼函数”,那就让我们来做点事情吧。

第一个可以要做的首先是照猫画猫,将(A x y)过程抄到你的Scheme解释器中,执行一下(A 1 10), (A 2 4), (A 3 3)看看有什么结果,同时可以针对(A 1 n)和 (A 2 n)多做几次试验,比如(A 1 7), (A 1 6), (A 2 2), (A 2 3)之类的,注意,跑(A 2 5)以上会达到递归嵌套限制。

跑完以上过程以后大概会有个认识。(A 0 n)比较简单,就是返回(2*n),这个从过程的代码里也能看出来。(A 1 n)复杂一点点,不过做多了计算机工作,对1024,2048之类的数字还是比较敏感的,大概可以猜出来(A 1 n)返回的是2的n次方,具体为什么会返回2的n次方就需要分析一下才知道。(A 2 n)就比较难猜了,需要看看程序到底怎么跑的才行。

怎么来分析(A x y)的运行过程呢?简单一点的方法是在(A x y)过程中加入(format #t )输出,看看到底是怎么调用的。

比如我仿照(A x y)过程写了一个(A-with-info x y)过程,代码如下:

(define (A-with-info x y)
(format #t "Evaluating (A ~S ~S) " x y)
(cond ((= y 0) (format #t "the result is 0~%"))
((= x 0) (format #t "the result is ~S~%" (* 2 y)))
((= y 1) (format #t "the result is 2~%"))
(else (format #t "transforming to (A ~S (A ~S ~S))~%" (- x 1) x (- y 1))))
(cond ((= y 0) 0)
((= x 0) (* 2 y))
((= y 1) 2)
(else (A-with-info (- x 1)
(A-with-info x (- y 1))))))

以上代码几乎完全和(A x y)的代码一样,就是增加了一些format的输出而已,这样可以在代码运行过程中跟踪过程的变换。

比如,调用(A-with-info 1 8)的结果如下,通过以下输出可以比较明了地看清过程的变换。

1 ]=> (A-with-info 1 8)

Evaluating (A 1 8) transforming to (A 0 (A 1 7))

Evaluating (A 1 7) transforming to (A 0 (A 1 6))

Evaluating (A 1 6) transforming to (A 0 (A 1 5))

Evaluating (A 1 5) transforming to (A 0 (A 1 4))

Evaluating (A 1 4) transforming to (A 0 (A 1 3))

Evaluating (A 1 3) transforming to (A 0 (A 1 2))

Evaluating (A 1 2) transforming to (A 0 (A 1 1))

Evaluating (A 1 1) the result is 2

Evaluating (A 0 2) the result is 4

Evaluating (A 0 4) the result is 8

Evaluating (A 0 8) the result is 16

Evaluating (A 0 16) the result is 32

Evaluating (A 0 32) the result is 64

Evaluating (A 0 64) the result is 128

Evaluating (A 0 128) the result is 256

;Value: 256

如果你愿意花时间,可以想一些办法让上面的输出更清晰一些,比如我写的另一个过程(A-with-detail)的输出如下:

1 ]=> (A-with-detail 1 8 "" "")

(A 1 8)

(A 0 (A 1 7))

(A 0 (A 0 (A 1 6)))

(A 0 (A 0 (A 0 (A 1 5))))

(A 0 (A 0 (A 0 (A 0 (A 1 4)))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 1 3))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 2)))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 1))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 [(A 1 1) is 2])))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 2)))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 [(A 0 2) is 4]))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 4))))))

(A 0 (A 0 (A 0 (A 0 (A 0 [(A 0 4) is 8])))))

(A 0 (A 0 (A 0 (A 0 (A 0 8)))))

(A 0 (A 0 (A 0 (A 0 [(A 0 8) is 16]))))

(A 0 (A 0 (A 0 (A 0 16))))

(A 0 (A 0 (A 0 [(A 0 16) is 32])))

(A 0 (A 0 (A 0 32)))

(A 0 (A 0 [(A 0 32) is 64]))

(A 0 (A 0 64))

(A 0 [(A 0 64) is 128])

(A 0 128)

[(A 0 128) is 256]

;Value: 256

这里就可以清晰地看见(A 1 8)的展开和归约过程。

同样,我们可以看看(A 2 4)的变换过程:

1 ]=> (A-with-detail 2 4 "" "")

(A 2 4)

(A 1 (A 2 3))

(A 1 (A 1 (A 2 2)))

(A 1 (A 1 (A 1 (A 2 1))))

(A 1 (A 1 (A 1 [(A 2 1) is 2])))

(A 1 (A 1 (A 1 2)))

(A 1 (A 1 (A 0 (A 1 1))))

(A 1 (A 1 (A 0 [(A 1 1) is 2])))

(A 1 (A 1 (A 0 2)))

(A 1 (A 1 [(A 0 2) is 4]))

(A 1 (A 1 4))

(A 1 (A 0 (A 1 3)))

(A 1 (A 0 (A 0 (A 1 2))))

(A 1 (A 0 (A 0 (A 0 (A 1 1)))))

(A 1 (A 0 (A 0 (A 0 [(A 1 1) is 2]))))

(A 1 (A 0 (A 0 (A 0 2))))

(A 1 (A 0 (A 0 [(A 0 2) is 4])))

(A 1 (A 0 (A 0 4)))

(A 1 (A 0 [(A 0 4) is 8]))

(A 1 (A 0 8))

(A 1 [(A 0 8) is 16])

(A 1 16)

(A 0 (A 1 15))

(A 0 (A 0 (A 1 14)))

(A 0 (A 0 (A 0 (A 1 13))))

(A 0 (A 0 (A 0 (A 0 (A 1 12)))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 1 11))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 10)))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 9))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 8)))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 7))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 6)))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 5))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 4)))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 3))))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 2)))))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 1))))))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 [(A 1 1) is 2])))))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 2)))))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 [(A 0 2) is 4]))))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 4))))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 [(A 0 4) is 8])))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 8)))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 [(A 0 8) is 16]))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 16))))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 [(A 0 16) is 32])))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 32)))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 [(A 0 32) is 64]))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 64))))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 [(A 0 64) is 128])))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 128)))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 [(A 0 128) is 256]))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 256))))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 [(A 0 256) is 512])))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 512)))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 [(A 0 512) is 1024]))))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 1024))))))

(A 0 (A 0 (A 0 (A 0 (A 0 [(A 0 1024) is 2048])))))

(A 0 (A 0 (A 0 (A 0 (A 0 2048)))))

(A 0 (A 0 (A 0 (A 0 [(A 0 2048) is 4096]))))

(A 0 (A 0 (A 0 (A 0 4096))))

(A 0 (A 0 (A 0 [(A 0 4096) is 8192])))

(A 0 (A 0 (A 0 8192)))

(A 0 (A 0 [(A 0 8192) is 16384]))

(A 0 (A 0 16384))

(A 0 [(A 0 16384) is 32768])

(A 0 32768)

[(A 0 32768) is 65536]

;Value: 65536

最后就是有关(A 2 n)的数学含义,仔细看看上面的变换过程大概可以想明白,就是2的右上角有n个不断变小的2,就是取2 的2次方,赋予A,然后取2的A次方,赋予B,再取2的B次方,赋予C,一直下去,做n次。从上面的分析看,这个“阿克曼函数”有迭代实现喔。是否还记得我们之前讨论过的“迭代计算过程”和“递归计算过程”?书中的“阿克曼函数”的实现使用的是“递归计算过程”,而这个函数显然有“迭代计算过程”的实现方法。有关这个我们在这里就不详细讨论了,另找时间再讲这个东西。

如果看完上面的内容不明白的话最好自己做完成以上步骤,应该会有一些认识。如果还是不明白就去看看网上有关“阿克曼函数”的具体解释,看了还是不明白的话就放弃吧,“数学不是个买卖,想买就能买”。

对于已经明白过来的同学们,可以想想(A 3 n)的数学含义是什么,有点花脑筋哟!想明白就再想想(A 4 n), (A 5 n),想想(A m n)函数中m 和n分别起到什么作用,(A m n)的广泛含义是什么?

问完这些问题,我似乎看到了很多好学的同学们抓破脑袋毫无头绪的样子,于是我开心地笑了,愉快地关上了我的MacBook,深藏功与名。

SICP 习题 (1.10)解题总结的更多相关文章

  1. SICP 习题 (1.14)解题总结

    SICP 习题 1.14要求计算出过程count-change的增长阶.count-change是书中1.2.2节讲解的用于计算零钱找换方案的过程. 要解答习题1.14,首先你需要理解count-ch ...

  2. SICP 习题 (1.13) 解题总结

    SICP习题1.13要求证明Fib(n)是最接近φn/√5 的整数,其中φ=(1+√5)/2 .题目还有一个提示,提示解题者利用归纳法和斐波那契数的定义证明Fib(n)=(φn - ψn) / √5 ...

  3. SICP 习题 (1.7) 解题总结

    SICP 习题 1.7 是对正文1.1.7节中的牛顿法求平方根的改进,改进部分是good-enough?过程. 原来的good-enough?是判断x和guess平方的差值是否小于0.001,这个过程 ...

  4. SICP 习题 (1.8) 解题总结

    SICP 习题1.8需要我们做的是按照牛顿法求平方根的方法做一个求立方根的过程. 所以说书中讲牛顿法求平方根的内容还是要好好理解,不然后面这几道题做起来就比较困难. 反过来,如果理解了牛顿法求平方根的 ...

  5. SICP 习题 (1.9) 解题总结

    SICP 习题 1.9 开始针对“迭代计算过程”和“递归计算过程”,有关迭代计算过程和递归计算过程的内容在书中的1.2.1节有详细讨论,要完成习题1.9,必须完全吃透1.2.1节的内容,不然的话,即使 ...

  6. SICP 习题1.10

    题目要求 解题方法 递归计算 没什么好说的,单纯的套用数学公示 (define (f n) (if (< n 3) n (+ (f (- n 1)) (* 2 (f (- n 2))) (* 3 ...

  7. SICP 习题 (2.10)解题总结: 区间除法中除于零的问题

    SICP 习题 2.10 要求我们处理区间除法运算中除于零的问题. 题中讲到一个专业程序猿Ben Bitdiddle看了Alyssa的工作后提出了除于零的问题,大家留意一下这个叫Ben的人,后面会不断 ...

  8. SICP 习题 (1.35)解题总结

    SICP 习题 1.35要求我们证明黄金切割率φ 是变换函数 x => 1+ 1/x 的不动点,然后利用这一事实通过过程fixed-point 计算出φ的值. 首先是有关函数的不动点,这个概念须 ...

  9. SICP 习题 (1.41)解题总结

    SICP 习题1.41 看似和周边的题目没有关系,突然叫我们去定义一个叫double的过程,事实上这道题的核心还是高阶函数. 题目要求我们定义一个过程double,它以一个过程作为參数,这个作为參数的 ...

随机推荐

  1. android模块

    网络模块 1.URL --------openStream() return InputStream --------openConnection() return URLConnection 2.U ...

  2. ASP.NET自定义控件加载资源WebResource问题

    最近项目用日期控件,想把My97的资源文件跟TextBox封装成一个DatePicker控件,其实很简单的意见事情,但是还是用了一天多的时间,主要的问题就是解决资源文件加载的问题.通过一天多的努力,得 ...

  3. [11-2] adaboost理解

    以二分类问题为例({-1,+1}) adaboost步骤: 1.初始化u1=(1/N,1/N,-,1/N) 2.找到h,使最小化,记该h为g:计算作为该g的权重 3.更新ui: 4.重复2,3得到T个 ...

  4. [置顶] 自娱自乐6之Linux gadget驱动5(自编gadget驱动,包涵与之通讯的主机usb驱动,已调试通过)

    这个代码调试,你首先要保证你的udc驱动没用问题,这个有些矛盾,应为我本来要用gadget驱动来调试udc驱动,结果反过来了. 这是在zero基础改的,大概的改动 1. 去掉loop. 2. sink ...

  5. UVA 10003 Cutting Sticks 切木棍 dp

    题意:把一根木棍按给定的n个点切下去,每次切的花费为切的那段木棍的长度,求最小花费. 这题出在dp入门这边,但是我看完题后有强烈的既是感,这不是以前做过的石子合并的题目变形吗? 题目其实就是把n+1根 ...

  6. Android之TextView的样式类Span的使用具体解释

    Android中的TextView是个显示文字的的UI类,在现实中的需求中,文字有各式各样的样式.TextView本身没有属性去设置实现,我们能够通过Android提供的 SpannableStrin ...

  7. C# 学习笔记 C#基础

    今天第一天开通博客.恰好在学习C#,所以就准备把学到的知识要点记录下来. 基础类型 类型定义了值得蓝图.值是一个被变量或者常量所指定的存储位置,变量是指可以被改变的,而常量则相反,其值不可以便改变, ...

  8. 《think in python》学习-2

    高能提示:本文大量编程术语与释义,一些释义如有偏差恕不讨论. 变量,表达式 数据类型: print 4 #打印整数 int print 4.1#打印浮点数 float print "hell ...

  9. KVC和KVO

    OC中的一个比较有特色的知识点:KVC和KVO 一.KVC操作OC中的KVC操作就和Java中使用反射机制去访问类的private权限的变量,很暴力的,这样做就会破坏类的封装性,本来类中的的priva ...

  10. 《OS X Mountain Lion》 读书杂记

    OS X是一个类UNIX操作系统,由底层的Darwin和上层的OS X应用程序框架(Cocoa, Carbon, Quartz等)及Aqua用户界面组成.其中Darwin是一个开源.完整的POSIX- ...