<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>刘未鹏 &#124; Mind Hacks &#187; 数学</title>
	<atom:link href="http://mindhacks.cn/topics/math/feed/" rel="self" type="application/rss+xml" />
	<link>http://mindhacks.cn</link>
	<description>思维改变生活</description>
	<lastBuildDate>Sun, 21 Mar 2010 08:00:41 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>数学之美番外篇：平凡而又神奇的贝叶斯方法</title>
		<link>http://mindhacks.cn/2008/09/21/the-magical-bayesian-method/</link>
		<comments>http://mindhacks.cn/2008/09/21/the-magical-bayesian-method/#comments</comments>
		<pubDate>Sun, 21 Sep 2008 11:34:00 +0000</pubDate>
		<dc:creator>刘未鹏</dc:creator>
				<category><![CDATA[数学]]></category>
		<category><![CDATA[机器学习与人工智能]]></category>
		<category><![CDATA[计算机科学]]></category>

		<guid isPermaLink="false">http://mindhacks.cn/2008/09/21/the-magical-bayesian-method/</guid>
		<description><![CDATA[概率论只不过是把常识用数学公式表达了出来。

——拉普拉斯

记得读本科的时候，最喜欢到城里的计算机书店里面去闲逛，一逛就是好几个小时；有一次，在书店看到一本书，名叫贝叶斯方法。当时数学系的课程还没有学到概率统计。我心想，一个方法能够专门写出一本书来，肯定很牛逼。后来，我发现当初的那个朴素归纳推理成立了——这果然是个牛逼的方法。

——题记

这是一篇关于贝叶斯方法的科普文，我会尽量少用公式，多用平白的语言叙述，多举实际例子。更严格的公式和计算我会在相应的地方注明参考资料。贝叶斯方法被证明是非常 general 且强大的推理框架，文中你会看到很多有趣的应用。]]></description>
			<content:encoded><![CDATA[<p>概率论只不过是把常识用数学公式表达了出来。</p>
<p>——拉普拉斯</p>
<p>记得读本科的时候，最喜欢到城里的计算机书店里面去闲逛，一逛就是好几个小时；有一次，在书店看到一本书，名叫贝叶斯方法。当时数学系的课程还没有学到概率统计。我心想，一个方法能够专门写出一本书来，肯定很牛逼。后来，我发现当初的那个朴素归纳推理成立了——这果然是个牛逼的方法。</p>
<p>——题记</p>
<p><strong>目录</strong></p>
<p>0. 前言    <br />1. 历史     <br />&#160;&#160;&#160; 1.1 一个例子：自然语言的二义性     <br />&#160;&#160;&#160; 1.2 贝叶斯公式     <br />2. 拼写纠正     <br />3. 模型比较与贝叶斯奥卡姆剃刀     <br />&#160;&#160;&#160; 3.1 再访拼写纠正     <br />&#160;&#160;&#160; 3.2 模型比较理论（Model Comparasion）与贝叶斯奥卡姆剃刀（Bayesian Occam’s Razor）     <br />&#160;&#160;&#160; 3.3 最小描述长度原则     <br />&#160;&#160;&#160; 3.4 最优贝叶斯推理     <br />4. 无处不在的贝叶斯     <br />&#160;&#160;&#160; 4.1 中文分词     <br />&#160;&#160;&#160; 4.2 统计机器翻译     <br />&#160;&#160;&#160; 4.3 贝叶斯图像识别，Analysis by Synthesis&#160;&#160;&#160; <br />&#160;&#160;&#160; 4.4 EM 算法与基于模型的聚类     <br />&#160;&#160;&#160; 4.5 最大似然与最小二乘     <br />5. 朴素贝叶斯方法（又名“愚蠢者的贝叶斯（idiot’s bayes）”）     <br />&#160;&#160;&#160; 5.1 垃圾邮件过滤器     <br />&#160;&#160;&#160; 5.2 为什么朴素贝叶斯方法令人诧异地好——一个理论解释     <br />6. 层级贝叶斯模型     <br />&#160;&#160;&#160; 6.1 隐马可夫模型（HMM）     <br />7. 贝叶斯网络</p>
<p><strong>0. </strong><strong>前言</strong></p>
<p>这是一篇关于贝叶斯方法的科普文，我会尽量少用公式，多用平白的语言叙述，多举实际例子。更严格的公式和计算我会在相应的地方注明参考资料。贝叶斯方法被证明是非常 general 且强大的推理框架，文中你会看到很多有趣的应用。 </p>
<p><strong>1. </strong><strong>历史</strong></p>
<p>托马斯·贝叶斯（Thomas Bayes）同学的详细生平在<a href="http://en.wikipedia.org/wiki/Thomas_Bayes">这里</a>。以下摘一段 wikipedia 上的简介：</p>
<blockquote><p>所谓的贝叶斯方法源于他生前为解决一个“逆概”问题写的一篇文章，而这篇文章是在他死后才由他的一位朋友发表出来的。在贝叶斯写这篇文章之前，人们已经能够计算“正向概率”，如“假设袋子里面有N个白球，M个黑球，你伸手进去摸一把，摸出黑球的概率是多大”。而一个自然而然的问题是反过来：“如果我们事先并不知道袋子里面黑白球的比例，而是闭着眼睛摸出一个（或好几个）球，观察这些取出来的球的颜色之后，那么我们可以就此对袋子里面的黑白球的比例作出什么样的推测”。这个问题，就是所谓的逆概问题。</p>
</blockquote>
<p>实际上，贝叶斯当时的论文只是对这个问题的一个直接的求解尝试，并不清楚他当时是不是已经意识到这里面包含着的深刻的思想。然而后来，贝叶斯方法席卷了概率论，并将应用延伸到各个问题领域，所有需要作出概率预测的地方都可以见到贝叶斯方法的影子，特别地，贝叶斯是机器学习的核心方法之一。这背后的深刻原因在于，现实世界本身就是不确定的，人类的观察能力是有局限性的（否则有很大一部分科学就没有必要做了——设想我们能够直接观察到电子的运行，还需要对原子模型争吵不休吗？），我们日常所观察到的只是事物表面上的结果，沿用刚才那个袋子里面取球的比方，我们往往只能知道从里面取出来的球是什么颜色，而并不能直接看到袋子里面实际的情况。这个时候，我们就需要提供一个猜测（hypothesis，更为严格的说法是“假设”，这里用“猜测”更通俗易懂一点），所谓猜测，当然就是不确定的（很可能有好多种乃至无数种猜测都能满足目前的观测），<strong>但也绝对不是两眼一抹黑瞎蒙——具体地说，我们需要做两件事情：1. 算出各种不同猜测的可能性大小。2. 算出最靠谱的猜测是什么。第一个就是计算特定猜测的后验概率，对于连续的猜测空间则是计算猜测的概率密度函数。第二个则是所谓的模型比较，模型比较如果不考虑先验概率的话就是最大似然方法。</strong></p>
<p><strong>1.1 </strong><strong>一个例子：自然语言的二义性</strong></p>
<p>下面举一个自然语言的不确定性的例子。当你看到这句话：</p>
<blockquote><p>The girl saw the boy with a telescope.</p>
</blockquote>
<p>你对这句话的含义有什么猜测？平常人肯定会说：那个女孩拿望远镜看见了那个男孩（即你对这个句子背后的实际语法结构的猜测是：The girl saw-with-a-telescope the boy ）。然而，仔细一想，你会发现这个句子完全可以解释成：那个女孩看见了那个拿着望远镜的男孩（即：The girl saw the-boy-with-a-telescope ）。那为什么平常生活中我们每个人都能够迅速地对这种二义性进行消解呢？这背后到底隐藏着什么样的思维法则？我们留到后面解释。</p>
<p><strong>1.2 </strong><strong>贝叶斯公式</strong></p>
<p>贝叶斯公式是怎么来的？</p>
<p>我们还是使用 wikipedia 上的一个例子：</p>
<blockquote><p>一所学校里面有 60% 的男生，40% 的女生。男生总是穿长裤，女生则一半穿长裤一半穿裙子。有了这些信息之后我们可以容易地计算“随机选取一个学生，他（她）穿长裤的概率和穿裙子的概率是多大”，这个就是前面说的“正向概率”的计算。然而，假设你走在校园中，迎面走来一个穿长裤的学生（很不幸的是你高度近似，你只看得见他（她）穿的是否长裤，而无法确定他（她）的性别），你能够推断出他（她）是男生的概率是多大吗？</p>
</blockquote>
<p>一些认知科学的研究表明（《决策与判断》以及《<a href="http://www.douban.com/subject/3199621/">Rationality for Mortals</a>》第12章：小孩也可以解决贝叶斯问题），我们对形式化的贝叶斯问题不擅长，但对于以频率形式呈现的等价问题却很擅长。在这里，我们不妨把问题重新叙述成：你在校园里面<a href="http://en.wikipedia.org/wiki/Random_walk">随机游走</a>，遇到了 N 个穿长裤的人（仍然假设你无法直接观察到他们的性别），问这 N 个人里面有多少个女生多少个男生。</p>
<p>你说，这还不简单：算出学校里面有多少穿长裤的，然后在这些人里面再算出有多少女生，不就行了？</p>
<p>我们来算一算：假设学校里面人的总数是 U 个。60% 的男生都穿长裤，于是我们得到了 U * P(Boy) * P(Pants|Boy) 个穿长裤的（男生）（其中 P(Boy) 是男生的概率 = 60%，这里可以简单的理解为男生的比例；P(Pants|Boy) 是条件概率，即在 Boy 这个条件下穿长裤的概率是多大，这里是 100% ，因为所有男生都穿长裤）。40% 的女生里面又有一半（50%）是穿长裤的，于是我们又得到了 U * P(Girl) * P(Pants|Girl) 个穿长裤的（女生）。加起来一共是 U * P(Boy) * P(Pants|Boy) + U * P(Girl) * P(Pants|Girl) 个穿长裤的，其中有 U * P(Girl) * P(Pants|Girl) 个女生。两者一比就是你要求的答案。</p>
<p>下面我们把这个答案形式化一下：我们要求的是 P(Girl|Pants) （穿长裤的人里面有多少女生），我们计算的结果是 U * P(Girl) * P(Pants|Girl) / [U * P(Boy) * P(Pants|Boy) + U * P(Girl) * P(Pants|Girl)] 。容易发现这里校园内人的总数是无关的，可以消去。于是得到</p>
<p><strong>P(Girl|Pants) = P(Girl) * P(Pants|Girl) / [P(Boy) * P(Pants|Boy) + P(Girl) * P(Pants|Girl)]</strong></p>
<p>注意，如果把上式收缩起来，分母其实就是 P(Pants) ，分子其实就是 P(Pants, Girl) 。而这个比例很自然地就读作：在穿长裤的人（ P(Pants) ）里面有多少（穿长裤）的女孩（ P(Pants, Girl) ）。</p>
<p>上式中的 Pants 和 Boy/Girl 可以指代一切东西，所以其一般形式就是：</p>
<p><strong>P(B|A) = P(A|B) * P(B) / [P(A|B) * P(B) + P(A|~B) * P(~B) ]</strong></p>
<p>收缩起来就是：</p>
<p><strong>P(B|A) = P(AB) / P(A)</strong></p>
<p>其实这个就等于：</p>
<p><strong>P(B|A) * P(A) = P(AB)</strong></p>
<p>难怪拉普拉斯说<strong>概率论只是把常识用数学公式表达了出来</strong>。</p>
<p>然而，后面我们会逐渐发现，看似这么平凡的贝叶斯公式，背后却隐含着非常深刻的原理。</p>
<p><strong>2. </strong><strong>拼写纠正</strong></p>
<p>经典著作《人工智能：现代方法》的作者之一 Peter Norvig 曾经写过一篇介绍如何写一个拼写检查/纠正器的文章（原文在<a href="http://norvig.com/spell-correct.html">这里</a>，徐宥的翻译版在<a href="http://blog.youxu.info/spell-correct.html">这里</a>，这篇文章很深入浅出，强烈建议读一读），里面用到的就是贝叶斯方法，这里我们不打算复述他写的文章，而是简要地将其核心思想介绍一下。</p>
<p>首先，我们需要询问的是：“<strong>问题是什么？</strong>”</p>
<p>问题是我们看到用户输入了一个不在字典中的单词，我们需要去猜测：“这个家伙到底真正想输入的单词是什么呢？”用刚才我们形式化的语言来叙述就是，我们需要求：</p>
<p><strong>P(</strong><strong>我们猜测他想输入的单词 | 他实际输入的单词)</strong></p>
<p>这个概率。并找出那个使得这个概率最大的猜测单词。显然，我们的猜测未必是唯一的，就像前面举的那个自然语言的歧义性的例子一样；这里，比如用户输入： thew ，那么他到底是想输入 the ，还是想输入 thaw ？到底哪个猜测可能性更大呢？幸运的是我们可以用贝叶斯公式来直接出它们各自的概率，我们不妨将我们的多个猜测记为 h1 h2 .. （ h 代表 hypothesis），它们都属于一个有限且离散的猜测空间 H （单词总共就那么多而已），将用户实际输入的单词记为 D （ D 代表 Data ，即观测数据），于是</p>
<p><strong>P(</strong><strong>我们的猜测1 | 他实际输入的单词)</strong></p>
<p>可以抽象地记为：</p>
<p>P(h1 | D)</p>
<p>类似地，对于我们的猜测2，则是 P(h2 | D)。不妨统一记为：</p>
<p>P(h | D)</p>
<p>运用一次贝叶斯公式，我们得到：</p>
<p><strong>P(h | D) = P(h) * P(D | h) / P(D)</strong></p>
<p>对于不同的具体猜测 h1 h2 h3 .. ，P(D) 都是一样的，所以在比较 P(h1 | D) 和 P(h2 | D) 的时候我们可以忽略这个常数。即我们只需要知道：</p>
<p>P(h | D) ∝ P(h) * P(D | h) （注：那个符号的意思是“正比例于”，不是无穷大，注意符号右端是有一个小缺口的。）</p>
<p>这个式子的抽象含义是：对于给定观测数据，一个猜测是好是坏，取决于“这个猜测本身独立的可能性大小（先验概率，Prior ）”和“这个猜测生成我们观测到的数据的可能性大小”（似然，Likelihood ）的乘积。具体到我们的那个 thew 例子上，含义就是，用户实际是想输入 the 的可能性大小取决于 the 本身在词汇表中被使用的可能性（频繁程度）大小（先验概率）和 想打 the 却打成 thew 的可能性大小（似然）的乘积。</p>
<p>下面的事情就很简单了，对于我们猜测为可能的每个单词计算一下 P(h) * P(D | h) 这个值，然后取最大的，得到的就是最靠谱的猜测。</p>
<p><strong>一点注记</strong>：Norvig 的拼写纠正器里面只提取了编辑距离为 2 以内的所有已知单词。这是为了避免去遍历字典中每个单词计算它们的 P(h) * P(D | h) ，但这种做法为了节省时间带来了一些误差。但话说回来难道我们人类真的回去遍历每个可能的单词来计算他们的后验概率吗？不可能。实际上，根据认知神经科学的观点，我们首先根据错误的单词做一个 bottom-up 的关联提取，提取出有可能是实际单词的那些候选单词，这个提取过程就是所谓的基于内容的提取，可以根据错误单词的一些模式片段提取出有限的一组候选，非常快地缩小的搜索空间（比如我输入 explaination ，单词里面就有充分的信息使得我们的大脑在常数时间内把可能性 narrow down 到 explanation 这个单词上，至于具体是根据哪些线索——如音节——来提取，又是如何在生物神经网络中实现这个提取机制的，目前还是一个没有弄清的领域）。然后，我们对这有限的几个猜测做一个 top-down 的预测，看看到底哪个对于观测数据（即错误单词）的预测效力最好，而如何衡量预测效率则就是用贝叶斯公式里面的那个 P(h) * P(D | h) 了——虽然我们很可能使用了<a href="http://www.douban.com/subject/1599035/">一些启发法来简化计算</a>。后面我们还会提到这样的 bottom-up 的关联提取。</p>
<p><strong>3. </strong><strong>模型比较与奥卡姆剃刀</strong></p>
<p><strong>3.1 </strong><strong>再访拼写纠正</strong></p>
<p>介绍了贝叶斯拼写纠正之后，接下来的一个自然而然的问题就来了：“<strong>为什么？</strong>”为什么要用贝叶斯公式？为什么贝叶斯公式在这里可以用？我们可以很容易地领会为什么贝叶斯公式用在前面介绍的那个男生女生长裤裙子的问题里是正确的。但为什么这里？</p>
<p>为了回答这个问题，一个常见的思路就是想想：<strong>非得这样吗？</strong>因为如果你想到了另一种做法并且证明了它也是靠谱的，那么将它与现在这个一比较，也许就能得出很有价值的信息。那么对于拼写纠错问题你能想到其他方案吗？</p>
<p>不管怎样，一个最常见的替代方案就是，选择离 thew 的<a href="http://en.wikipedia.org/wiki/Edit_distance">编辑距离</a>最近的。然而 the 和 thaw 离 thew 的编辑距离都是 1 。这可咋办捏？你说，不慌，那还是好办。我们就看到底哪个更可能被错打为 thew 就是了。我们注意到字母 e 和字母 w 在键盘上离得很紧，无名指一抽筋就不小心多打出一个 w 来，the 就变成 thew 了。而另一方面 thaw 被错打成 thew 的可能性就相对小一点，因为 e 和 a 离得较远而且使用的指头相差一个指头（一个是中指一个是小指，不像 e 和 w 使用的指头靠在一块——神经科学的证据表明紧邻的身体设施之间容易串位）。OK，很好，因为你现在已经是在用最大似然方法了，或者直白一点，你就是在计算那个使得 P(D | h) 最大的 h 。</p>
<p>而贝叶斯方法计算的是什么？是 P(h) * P(D | h) 。多出来了一个 P(h) 。我们刚才说了，这个多出来的 P(h) 是特定猜测的先验概率。为什么要掺和进一个先验概率？刚才说的那个最大似然不是挺好么？很雄辩地指出了 the 是更靠谱的猜测。有什么问题呢？既然这样，我们就从给最大似然找茬开始吧——我们假设两者的似然程度是一样或非常相近，这样不就难以区分哪个猜测更靠谱了吗？比如用户输入tlp ，那到底是 top 还是 tip ？（这个例子不怎么好，因为 top 和 tip 的词频可能仍然是接近的，但一时想不到好的英文单词的例子，我们不妨就假设 top 比 tip 常见许多吧，这个假设并不影响问题的本质。）这个时候，当最大似然不能作出决定性的判断时，先验概率就可以插手进来给出指示——“既然你无法决定，那么我告诉你，一般来说 top 出现的程度要高许多，所以更可能他想打的是 top ”）。</p>
<p>以上只是最大似然的一个问题，即并不能提供决策的全部信息。</p>
<p>最大似然还有另一个问题：即便一个猜测与数据非常符合，也并不代表这个猜测就是更好的猜测，因为这个猜测本身的可能性也许就非常低。比如 MacKay 在《Information Theory : Inference and Learning Algorithms》里面就举了一个很好的例子：-1 3 7 11 你说是等差数列更有可能呢？还是 -X^3 / 11 + 9/11*X^2 + 23/11 每项把前项作为 X 带入后计算得到的数列？此外曲线拟合也是，平面上 N 个点总是可以用 N-1 阶多项式来完全拟合，当 N 个点近似但不精确共线的时候，用 N-1 阶多项式来拟合能够精确通过每一个点，然而用直线来做拟合/线性回归的时候却会使得某些点不能位于直线上。你说到底哪个好呢？多项式？还是直线？一般地说肯定是越低阶的多项式越靠谱（当然前提是也不能忽视“似然”P(D | h) ，明摆着一个多项式分布您愣是去拿直线拟合也是不靠谱的，这就是为什么要把它们两者乘起来考虑。），原因之一就是低阶多项式更常见，先验概率（ P(h) ）较大（原因之二则隐藏在 P(D | h) 里面），这就是为什么我们要用<a href="http://en.wikipedia.org/wiki/Spline_interpolation">样条</a>来插值，而不是直接搞一个 N-1 阶多项式来通过任意 N 个点的原因。</p>
<p>以上分析当中隐含的哲学是，观测数据总是会有各种各样的误差，比如观测误差（比如你观测的时候一个 MM 经过你一不留神，手一抖就是一个误差出现了），所以如果过分去寻求能够完美解释观测数据的模型，就会落入所谓的数据<a href="http://en.wikipedia.org/wiki/Overfitting">过配（overfitting）</a>的境地，一个过配的模型试图连误差（噪音）都去解释（而实际上噪音又是不需要解释的），显然就过犹不及了。所以 P(D | h) 大不代表你的 h （猜测）就是更好的 h。还要看 P(h) 是怎样的。所谓<a href="http://en.wikipedia.org/wiki/Occam%27s_razor">奥卡姆剃刀</a>精神就是说：如果两个理论具有相似的解释力度，那么优先选择那个更简单的（往往也正是更平凡的，更少繁复的，更常见的）。</p>
<p>过分匹配的另一个原因在于当观测的结果并不是因为误差而显得“不精确”而是因为真实世界中对数据的结果产生贡献的因素太多太多，跟噪音不同，这些偏差是一些另外的因素集体贡献的结果，不是你的模型所能解释的——噪音那是不需要解释——一个现实的模型往往只提取出几个与结果相关度很高，很重要的因素（cause）。这个时候观察数据会倾向于围绕你的有限模型的预测结果呈<a href="http://en.wikipedia.org/wiki/Normal_Distribution">正态分布</a>，于是你实际观察到的结果就是这个正态分布的<a href="http://en.wikipedia.org/wiki/Random_sample">随机取样</a>，这个取样很可能受到其余因素的影响偏离你的模型所预测的中心，这个时候便不能贪心不足地试图通过改变模型来“完美”匹配数据，因为那些使结果偏离你的预测的贡献因素不是你这个有限模型里面含有的因素所能概括的，硬要打肿脸充胖子只能导致不实际的模型，举个教科书例子：身高和体重的实际关系近似于一个二阶多项式的关系，但大家都知道并不是只有身高才会对体重产生影响，物理世界影响体重的因素太多太多了，有人身材高大却瘦得跟稻草，有人却是横长竖不长。但不可否认的是总体上来说，那些特殊情况越是特殊就越是稀少，呈围绕最普遍情况（胖瘦适中）的正态分布，这个分布就保证了我们的身高——体重相关模型能够在大多数情况下做出靠谱的预测。但是——刚才说了，特例是存在的，就算不是特例，人有胖瘦，密度也有大小，所以完美符合身高——体重的某个假想的二阶多项式关系的人是不存在的，我们又不是欧几里德几何世界当中的理想多面体，所以，当我们对人群随机抽取了 N 个样本（数据点）试图对这 N 个数据点拟合出一个多项式的话就得注意，它肯定得是二阶多项式，我们要做的只是去根据数据点计算出多项式各项的参数（一个典型的方法就是最小二乘）；它肯定不是直线（我们又不是稻草），也不是三阶多项式四阶多项式.. 如果硬要完美拟合 N 个点，你可能会整出一个 N-1 阶多项式来——设想身高和体重的关系是 5 阶多项式看看？</p>
<p><strong>3.2 </strong><strong>模型比较理论（Model Comparasion）与贝叶斯奥卡姆剃刀（Bayesian Occam’s Razor）</strong></p>
<p>实际上，模型比较就是去比较哪个模型（猜测）更可能隐藏在观察数据的背后。其基本思想前面已经用拼写纠正的例子来说明了。我们对用户实际想输入的单词的猜测就是模型，用户输错的单词就是观测数据。我们通过：</p>
<p>P(h | D) ∝ P(h) * P(D | h)</p>
<p>来比较哪个模型最为靠谱。前面提到，光靠 P(D | h) （即“似然”）是不够的，有时候还需要引入 P(h) 这个先验概率。奥卡姆剃刀就是说 P(h) 较大的模型有较大的优势，而最大似然则是说最符合观测数据的（即 P(D | h) 最大的）最有优势。整个模型比较就是这两方力量的拉锯。我们不妨再举一个简单的例子来说明这一精神：你随便找枚硬币，掷一下，观察一下结果。好，你观察到的结果要么是“正”，要么是“反”（不，不是少林足球那枚硬币:P ），不妨假设你观察到的是“正”。现在你要去根据这个观测数据推断这枚硬币掷出“正”的概率是多大。根据最大似然估计的精神，我们应该猜测这枚硬币掷出“正”的概率是 1 ，因为这个才是能最大化 P(D | h) 的那个猜测。然而每个人都会大摇其头——很显然，你随机摸出一枚硬币这枚硬币居然没有反面的概率是“不存在的”，我们对一枚随机硬币是否一枚有偏硬币，偏了多少，是有着一个先验的认识的，这个认识就是绝大多数硬币都是基本公平的，偏得越多的硬币越少见（可以用一个 <a href="http://en.wikipedia.org/wiki/Beta_distribution">beta 分布</a>来表达这一先验概率）。将这个先验正态分布 p(θ) （其中 θ 表示硬币掷出正面的比例，小写的 p 代表这是<a href="http://en.wikipedia.org/wiki/Probability_density_function">概率密度函数</a>）结合到我们的问题中，我们便不是去最大化 P(D | h) ，而是去最大化 P(D | θ) * p(θ) ，显然 θ = 1 是不行的，因为 P(θ=1) 为 0 ，导致整个乘积也为 0 。实际上，只要对这个式子求一个导数就可以得到最值点。</p>
<p>以上说的是当我们知道先验概率 P(h) 的时候，光用最大似然是不靠谱的，因为最大似然的猜测可能先验概率非常小。然而，有些时候，我们对于先验概率一无所知，只能假设每种猜测的先验概率是均等的，这个时候就只有用最大似然了。实际上，统计学家和贝叶斯学家有一个有趣的争论，统计学家说：我们让数据自己说话。言下之意就是要摒弃先验概率。而贝叶斯支持者则说：数据会有各种各样的偏差，而一个靠谱的先验概率则可以对这些随机噪音做到健壮。事实证明贝叶斯派胜利了，胜利的关键在于所谓先验概率其实也是经验统计的结果，譬如为什么我们会认为绝大多数硬币是基本公平的？为什么我们认为大多数人的肥胖适中？为什么我们认为肤色是种族相关的，而体重则与种族无关？先验概率里面的“先验”并不是指先于一切经验，而是仅指先于我们“当前”给出的观测数据而已，在硬币的例子中先验指的只是先于我们知道投掷的结果这个经验，而并非“先天”。</p>
<p>然而，话说回来，有时候我们必须得承认，就算是基于以往的经验，我们手头的“先验”概率还是均匀分布，这个时候就必须依赖用最大似然，我们用前面留下的一个自然语言二义性问题来说明这一点：</p>
<blockquote><p>The girl saw the boy with a telescope.</p>
</blockquote>
<p>到底是 The girl saw-with-a-telescope the boy 这一语法结构，还是 The girl saw the-boy-with-a-telescope 呢？两种语法结构的常见程度都差不多（你可能会觉得后一种语法结构的常见程度较低，这是事后偏见，你只需想想 The girl saw the boy with a book 就知道了。当然，实际上从大规模语料统计结果来看后一种语法结构的确稍稍不常见一丁点，但是绝对不足以解释我们对第一种结构的强烈倾向）。那么到底为什么呢？</p>
<p>我们不妨先来看看 MacKay 在书中举的一个漂亮的例子：</p>
<p><a href="http://mindhacks.cn/wp-content/uploads/2009/02/i1.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="i1" border="0" alt="i1" src="http://mindhacks.cn/wp-content/uploads/2009/02/i1-thumb.jpg" width="384" height="371" /></a> </p>
<p>图中有多少个箱子？特别地，那棵书后面是一个箱子？还是两个箱子？还是三个箱子？还是.. 你可能会觉得树后面肯定是一个箱子，但为什么不是两个呢？如下图：</p>
<p><a href="http://mindhacks.cn/wp-content/uploads/2009/02/i2.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="i2" border="0" alt="i2" src="http://mindhacks.cn/wp-content/uploads/2009/02/i2-thumb.jpg" width="262" height="368" /></a> </p>
<p>很简单，你会说：要是真的有两个箱子那才怪了，怎么就那么巧这两个箱子刚刚好颜色相同，高度相同呢？</p>
<p>用概率论的语言来说，你刚才的话就翻译为：猜测 h 不成立，因为 P(D | h) 太小（太巧合）了。我们的直觉是：巧合（小概率）事件不会发生。所以当一个猜测（假设）使得我们的观测结果成为小概率事件的时候，我们就说“才怪呢，哪能那么巧捏？！”</p>
<p>现在我们可以回到那个自然语言二义性的例子，并给出一个完美的解释了：如果语法结构是 The girl saw the-boy-with-a-telecope 的话，怎么那个男孩偏偏手里拿的就是望远镜——一个可以被用来 saw-with 的东东捏？这也忒小概率了吧。他咋就不会拿本书呢？拿什么都好。怎么偏偏就拿了望远镜？所以唯一的解释是，这个“巧合”背后肯定有它的必然性，这个必然性就是，如果我们将语法结构解释为 The girl saw-with-a-telescope the boy 的话，就跟数据完美吻合了——既然那个女孩是用某个东西去看这个男孩的，那么这个东西是一个望远镜就完全可以解释了（不再是小概率事件了）。</p>
<p>自然语言二义性很常见，譬如上文中的一句话：</p>
<blockquote><p>参见《决策与判断》以及《<a href="http://www.douban.com/subject/3199621/">Rationality for Mortals</a>》第12章：小孩也可以解决贝叶斯问题</p>
</blockquote>
<p>就有二义性：到底是参见这两本书的第 12 章，还是仅仅是第二本书的第 12 章呢？如果是这两本书的第 12 章那就是咄咄怪事了，怎么恰好两本书都有第 12 章，都是讲同一个问题，更诡异的是，标题还相同呢？</p>
<p>注意，以上做的是似然估计（即只看 P(D | h) 的大小），不含先验概率。通过这两个例子，尤其是那个树后面的箱子的例子我们可以看到，似然估计里面也蕴含着奥卡姆剃刀：树后面的箱子数目越多，这个模型就越复杂。单个箱子的模型是最简单的。似然估计选择了更简单的模型。</p>
<p>这个就是所谓的<strong>贝叶斯奥卡姆剃刀（Bayesian Occam’s Razor）</strong>，因为这个剃刀工作在贝叶斯公式的似然（P(D | h) ）上，而不是模型本身（ P(h) ）的先验概率上，后者是传统的奥卡姆剃刀。关于贝叶斯奥卡姆剃刀我们再来看一个前面说到的曲线拟合的例子：如果平面上有 N 个点，近似构成一条直线，但绝不精确地位于一条直线上。这时我们既可以用直线来拟合（模型1），也可以用二阶多项式（模型2）拟合，也可以用三阶多项式（模型3），.. ，特别地，用 N-1 阶多项式便能够保证肯定能完美通过 N 个数据点。那么，这些可能的模型之中到底哪个是最靠谱的呢？前面提到，一个衡量的依据是奥卡姆剃刀：越是高阶的多项式越是繁复和不常见。然而，我们其实并不需要依赖于这个先验的奥卡姆剃刀，因为有人可能会争辩说：你怎么就能说越高阶的多项式越不常见呢？我偏偏觉得所有阶多项式都是等可能的。好吧，既然如此那我们不妨就扔掉 P(h) 项，看看 P(D | h) 能告诉我们什么。我们注意到越是高阶的多项式，它的轨迹弯曲程度越是大，到了八九阶简直就是直上直下，于是我们不仅要问：一个比如说八阶多项式在平面上随机生成的一堆 N 个点偏偏恰好近似构成一条直线的概率（即 P(D | h) ）有多大？太小太小了。反之，如果背后的模型是一条直线，那么根据该模型生成一堆近似构成直线的点的概率就大得多了。这就是贝叶斯奥卡姆剃刀。</p>
<p>这里只是提供一个关于贝叶斯奥卡姆剃刀的科普，强调直观解释，更多理论公式请参考 MacKay 的著作 《Information Theory : Inference and Learning Algorithms》第 28 章。</p>
<p><strong>3.3 </strong><strong>最小描述长度原则</strong></p>
<p>贝叶斯模型比较理论与信息论有一个有趣的关联：</p>
<p>P(h | D) ∝ P(h) * P(D | h)</p>
<p>两边求对数，将右式的乘积变成相加：</p>
<p>ln P(h | D) ∝ ln P(h) + ln P(D | h)</p>
<p>显然，最大化 P(h | D) 也就是最大化 ln P(h | D)。而 ln P(h) + ln P(D | h) 则可以解释为模型（或者称“假设”、“猜测”）h 的编码长度加上在该模型下数据 D 的编码长度。使这个和最小的模型就是最佳模型。</p>
<p>而究竟如何定义一个模型的编码长度，以及数据在模型下的编码长度则是一个问题。更多可参考 Mitchell 的 《Machine Learning》的 6.6 节，或 Mackay 的 28.3 节）</p>
<p><strong>3.4 </strong><strong>最优贝叶斯推理</strong></p>
<p>所谓的推理，分为两个过程，第一步是对观测数据建立一个模型。第二步则是使用这个模型来推测未知现象发生的概率。我们前面都是讲的对于观测数据给出最靠谱的那个模型。然而很多时候，虽然某个模型是所有模型里面最靠谱的，但是别的模型也并不是一点机会都没有。譬如第一个模型在观测数据下的概率是 0.5 。第二个模型是 0.4 ，第三个是 0.1 。如果我们只想知道对于观测数据哪个模型最可能，那么只要取第一个就行了，故事到此结束。然而很多时候我们建立模型是为了推测未知的事情的发生概率，这个时候，三个模型对未知的事情发生的概率都会有自己的预测，仅仅因为某一个模型概率稍大一点就只听他一个人的就太不民主了。所谓的最优贝叶斯推理就是将三个模型对于未知数据的预测结论加权平均起来（权值就是模型相应的概率）。显然，这个推理是理论上的制高点，无法再优了，因为它已经把所有可能性都考虑进去了。</p>
<p>只不过实际上我们是基本不会使用这个框架的，因为计算模型可能非常费时间，二来模型空间可能是连续的，即有无穷多个模型（这个时候需要计算模型的概率分布）。结果还是非常费时间。所以这个被看作是一个理论基准。</p>
<p><strong>4. </strong><strong>无处不在的贝叶斯</strong></p>
<p>以下我们再举一些实际例子来说明贝叶斯方法被运用的普遍性，这里主要集中在机器学习方面，因为我不是学经济的，否则还可以找到一堆经济学的例子。</p>
<p><strong>4.1 </strong><strong>中文分词</strong></p>
<p>贝叶斯是机器学习的核心方法之一。比如中文分词领域就用到了贝叶斯。Google 研究员吴军在《数学之美》系列中就有一篇是介绍中文分词的，这里只介绍一下核心的思想，不做赘述，详细请参考吴军的文章（<a href="http://www.googlechinablog.com/2006/04/blog-post_10.html">这里</a>）。</p>
<p>分词问题的描述为：给定一个句子（字串），如：</p>
<p>南京市长江大桥</p>
<p>如何对这个句子进行分词（词串）才是最靠谱的。例如：</p>
<p>1. 南京市/长江大桥</p>
<p>2. 南京/市长/江大桥</p>
<p>这两个分词，到底哪个更靠谱呢？</p>
<p>我们用贝叶斯公式来形式化地描述这个问题，令 X 为字串（句子），Y 为词串（一种特定的分词假设）。我们就是需要寻找使得 P(Y|X) 最大的 Y ，使用一次贝叶斯可得：</p>
<p>P(Y|X) ∝ P(Y)*P(X|Y)</p>
<p>用自然语言来说就是 这种分词方式（词串）的可能性 乘以 这个词串生成我们的句子的可能性。我们进一步容易看到：可以近似地将 P(X|Y) 看作是恒等于 1 的，因为任意假想的一种分词方式之下生成我们的句子总是精准地生成的（只需把分词之间的分界符号扔掉即可）。于是，我们就变成了去最大化 P(Y) ，也就是寻找一种分词使得这个词串（句子）的概率最大化。而如何计算一个词串：</p>
<p>W1, W2, W3, W4 ..</p>
<p>的可能性呢？我们知道，根据<a href="http://en.wikipedia.org/wiki/Joint_probability">联合概率</a>的公式展开：P(W1, W2, W3, W4 ..) = P(W1) * P(W2|W1) * P(W3|W2, W1) * P(W4|W1,W2,W3) * .. 于是我们可以通过一系列的条件概率（右式）的乘积来求整个联合概率。然而不幸的是随着条件数目的增加（P(Wn|Wn-1,Wn-2,..,W1) 的条件有 n-1 个），<a href="http://en.wikipedia.org/wiki/Curse_of_dimensionality">数据稀疏问题</a>也会越来越严重，即便语料库再大也无法统计出一个靠谱的 P(Wn|Wn-1,Wn-2,..,W1) 来。为了缓解这个问题，计算机科学家们一如既往地使用了“天真”假设：我们假设句子中一个词的出现概率只依赖于它前面的有限的 k 个词（k 一般不超过 3，如果只依赖于前面的一个词，就是2元<a href="http://en.wikipedia.org/wiki/N-gram">语言模型</a>（2-gram），同理有 3-gram 、 4-gram 等），这个就是所谓的“有限地平线”假设。虽然这个假设很傻很天真，但结果却表明它的结果往往是很好很强大的，后面要提到的朴素贝叶斯方法使用的假设跟这个精神上是完全一致的，我们会解释为什么像这样一个天真的假设能够得到强大的结果。目前我们只要知道，有了这个假设，刚才那个乘积就可以改写成： P(W1) * P(W2|W1) * P(W3|W2) * P(W4|W3) .. （假设每个词只依赖于它前面的一个词）。而统计 P(W2|W1) 就不再受到数据稀疏问题的困扰了。对于我们上面提到的例子“南京市长江大桥”，如果按照自左到右的贪婪方法分词的话，结果就成了“南京市长/江大桥”。但如果按照贝叶斯分词的话（假设使用 3-gram），由于“南京市长”和“江大桥”在语料库中一起出现的频率为 0 ，这个整句的概率便会被判定为 0 。 从而使得“南京市/长江大桥”这一分词方式胜出。</p>
<p><strong>一点注记</strong>：有人可能会疑惑，难道我们人类也是基于这些天真的假设来进行推理的？不是的。事实上，统计机器学习方法所统计的东西往往处于相当表层（shallow）的层面，在这个层面机器学习只能看到一些非常表面的现象，有一点科学研究的理念的人都知道：越是往表层去，世界就越是繁复多变。从机器学习的角度来说，特征（feature）就越多，成百上千维度都是可能的。特征一多，好了，<a href="http://en.wikipedia.org/wiki/Curse_of_dimensionality">高维诅咒</a>就产生了，数据就稀疏得要命，不够用了。而我们人类的观察水平显然比机器学习的观察水平要更深入一些，为了避免数据稀疏我们不断地发明各种装置（最典型就是显微镜），来帮助我们直接深入到更深层的事物层面去观察更本质的联系，而不是在浅层对表面现象作统计归纳。举一个简单的例子，通过对大规模语料库的统计，机器学习可能会发现这样一个规律：所有的“他”都是不会穿 bra 的，所有的“她”则都是穿的。然而，作为一个男人，却完全无需进行任何统计学习，因为深层的规律就决定了我们根本不会去穿 bra 。至于机器学习能不能完成后者（像人类那样的）这个推理，则是人工智能领域的经典问题。至少在那之前，<a href="http://www.yeeyan.com/articles/view/sylviaangel/9995">声称统计学习方法能够终结科学研究</a>（<a href="http://www.wired.com/science/discoveries/magazine/16-07/pb_theory">原文</a>）的说法<a href="http://scienceblogs.com/goodmath/2008/07/petabyte_scale_dataanalysis_an.php">是纯粹外行人说的话</a>。</p>
<p><strong>4.2 </strong><strong>统计机器翻译</strong></p>
<p>统计机器翻译因为其简单，自动（无需手动添加规则），迅速成为了机器翻译的事实标准。而统计机器翻译的核心算法也是使用的贝叶斯方法。</p>
<p>问题是什么？统计机器翻译的问题可以描述为：给定一个句子 e ，它的可能的外文翻译 f 中哪个是最靠谱的。即我们需要计算：P(f|e) 。一旦出现条件概率贝叶斯总是挺身而出：</p>
<p>P(f|e) ∝ P(f) * P(e|f)</p>
<p>这个式子的右端很容易解释：那些先验概率较高，并且更可能生成句子 e 的外文句子 f 将会胜出。我们只需简单统计（结合上面提到的 N-Gram 语言模型）就可以统计任意一个外文句子 f 的出现概率。然而 P(e|f) 却不是那么好求的，给定一个候选的外文局子 f ，它生成（或对应）句子 e 的概率是多大呢？我们需要定义什么叫 “对应”，这里需要用到一个分词对齐的平行语料库，有兴趣的可以参考 《Foundations of Statistical Natural Language Processing》第 13 章，这里摘选其中的一个例子：假设 e 为：John loves Mary 。我们需要考察的首选 f 是：Jean aime Marie （法文）。我们需要求出 P(e|f) 是多大，为此我们考虑 e 和 f 有多少种对齐的可能性，如：</p>
<p>John (Jean) loves (aime) Marie (Mary) </p>
<p>就是其中的一种（最靠谱的）对齐，为什么要对齐，是因为一旦对齐了之后，就可以容易地计算在这个对齐之下的 P(e|f) 是多大，只需计算：</p>
<p>P(John|Jean) * P(loves|aime) * P(Marie|Mary)</p>
<p>即可。</p>
<p>然后我们遍历所有的对齐方式，并将每种对齐方式之下的翻译概率 ∑ 求和。便可以获得整个的 P(e|f) 是多大。</p>
<p><strong>一点注记</strong>：还是那个问题：难道我们人类真的是用这种方式进行翻译的？highly unlikely 。这种计算复杂性非常高的东西连三位数乘法都搞不定的我们才不会笨到去使用呢。根据认知神经科学的认识，很可能我们是先从句子到语义（一个逐层往上（bottom-up）抽象的 folding 过程），然后从语义根据另一门语言的语法展开为另一门语言（一个逐层往下（top-down）的具体化 unfolding 过程）。如何可计算地实现这个过程，目前仍然是个难题。（我们看到很多地方都有 bottom-up/top-down 这样一个对称的过程，实际上有人猜测这正是生物神经网络原则上的运作方式，对视觉神经系统的研究尤其证明了这一点，Hawkins 在 《On Intelligence》 里面提出了一种 <a href="http://en.wikipedia.org/wiki/Hierarchical_Temporal_Memory">HTM</a> （Hierarchical Temporal Memory）模型正是使用了这个原则。）</p>
<p><strong>4.3 </strong><strong>贝叶斯图像识别，Analysis by Synthesis</strong></p>
<p>贝叶斯方法是一个非常 general 的推理框架。其核心理念可以描述成：Analysis by Synthesis （通过合成来分析）。06 年的认知科学新进展上有一篇 paper 就是讲用贝叶斯推理来解释视觉识别的，一图胜千言，下图就是摘自这篇 paper ：</p>
<p><a href="http://mindhacks.cn/wp-content/uploads/2009/02/i3.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="i3" border="0" alt="i3" src="http://mindhacks.cn/wp-content/uploads/2009/02/i3-thumb.jpg" width="604" height="216" /></a> </p>
<p>首先是视觉系统提取图形的边角特征，然后使用这些特征自底向上地激活高层的抽象概念（比如是 E 还是 F 还是等号），然后使用一个自顶向下的验证来比较到底哪个概念最佳地解释了观察到的图像。</p>
<p><strong>4.4&#160; EM </strong><strong>算法与基于模型的聚类</strong></p>
<p><a href="http://en.wikipedia.org/wiki/Data_clustering">聚类</a>是一种<a href="http://en.wikipedia.org/wiki/Unsupervised_learning">无指导的机器学习</a>问题，问题描述：给你一堆数据点，让你将它们最靠谱地分成一堆一堆的。聚类算法很多，不同的算法适应于不同的问题，这里仅介绍一个基于模型的聚类，该聚类算法对数据点的假设是，这些数据点分别是围绕 K 个核心的 K 个正态分布源所随机生成的，使用 Han JiaWei 的《Data Ming： Concepts and Techniques》中的图：</p>
<p><a href="http://mindhacks.cn/wp-content/uploads/2009/02/i4.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="i4" border="0" alt="i4" src="http://mindhacks.cn/wp-content/uploads/2009/02/i4-thumb.jpg" width="532" height="324" /></a> </p>
<p>图中有两个正态分布核心，生成了大致两堆点。我们的聚类算法就是需要根据给出来的那些点，算出这两个正态分布的核心在什么位置，以及分布的参数是多少。这很明显又是一个贝叶斯问题，但这次不同的是，答案是连续的且有无穷多种可能性，更糟的是，只有当我们知道了哪些点属于同一个正态分布圈的时候才能够对这个分布的参数作出靠谱的预测，现在两堆点混在一块我们又不知道哪些点属于第一个正态分布，哪些属于第二个。反过来，只有当我们对分布的参数作出了靠谱的预测时候，才能知道到底哪些点属于第一个分布，那些点属于第二个分布。这就成了一个先有鸡还是先有蛋的问题了。为了解决这个循环依赖，总有一方要先打破僵局，说，不管了，我先随便整一个值出来，看你怎么变，然后我再根据你的变化调整我的变化，然后如此迭代着不断互相推导，最终收敛到一个解。这就是 EM 算法。</p>
<p>EM 的意思是“Expectation-Maximazation”，在这个聚类问题里面，我们是先随便猜一下这两个正态分布的参数：如核心在什么地方，方差是多少。然后计算出每个数据点更可能属于第一个还是第二个正态分布圈，这个是属于 Expectation 一步。有了每个数据点的归属，我们就可以根据属于第一个分布的数据点来重新评估第一个分布的参数（从蛋再回到鸡），这个是 Maximazation 。如此往复，直到参数基本不再发生变化为止。这个迭代收敛过程中的贝叶斯方法在第二步，根据数据点求分布的参数上面。</p>
<p><strong>4.5 </strong><strong>最大似然与最小二乘</strong></p>
<p><a href="http://mindhacks.cn/wp-content/uploads/2009/02/i5.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="i5" border="0" alt="i5" src="http://mindhacks.cn/wp-content/uploads/2009/02/i5-thumb.png" width="184" height="232" /></a> </p>
<p>学过线性代数的大概都知道经典的最小二乘方法来做线性回归。问题描述是：给定平面上 N 个点，（这里不妨假设我们想用一条直线来拟合这些点——<a href="http://en.wikipedia.org/wiki/Regression_analysis">回归</a>可以看作是<a href="http://en.wikipedia.org/wiki/Curve_fitting">拟合</a>的特例，即允许误差的拟合），找出一条最佳描述了这些点的直线。</p>
<p>一个接踵而来的问题就是，我们如何定义最佳？我们设每个点的坐标为 (Xi, Yi) 。如果直线为 y = f(x) 。那么 (Xi, Yi) 跟直线对这个点的“预测”：(Xi, f(Xi)) 就相差了一个 ΔYi = |Yi – f(Xi)| 。最小二乘就是说寻找直线使得 (ΔY1)^2 + (ΔY2)^2 + .. （即误差的平方和）最小，至于为什么是误差的平方和而不是误差的绝对值和，统计学上也没有什么好的解释。然而贝叶斯方法却能对此提供一个完美的解释。</p>
<p>我们假设直线对于坐标 Xi 给出的预测 f(Xi) 是最靠谱的预测，所有纵坐标偏离 f(Xi) 的那些数据点都含有噪音，是噪音使得它们偏离了完美的一条直线，一个合理的假设就是偏离路线越远的概率越小，具体小多少，可以用一个正态分布曲线来模拟，这个分布曲线以直线对 Xi 给出的预测 f(Xi) 为中心，实际纵坐标为 Yi 的点 (Xi, Yi) 发生的概率就正比于 EXP[-(ΔYi)^2]。（EXP(..) 代表以常数 e 为底的多少次方）。</p>
<p>现在我们回到问题的贝叶斯方面，我们要想最大化的后验概率是：</p>
<p>P(h|D) ∝ P(h) * P(D|h)</p>
<p>又见贝叶斯！这里 h 就是指一条特定的直线，D 就是指这 N 个数据点。我们需要寻找一条直线 h 使得 P(h) * P(D|h) 最大。很显然，P(h) 这个先验概率是均匀的，因为哪条直线也不比另一条更优越。所以我们只需要看 P(D|h) 这一项，这一项是指这条直线生成这些数据点的概率，刚才说过了，生成数据点 (Xi, Yi) 的概率为 EXP[-(ΔYi)^2] 乘以一个常数。而 P(D|h) = P(d1|h) * P(d2|h) * .. 即假设各个数据点是独立生成的，所以可以把每个概率乘起来。于是生成 N 个数据点的概率为 EXP[-(ΔY1)^2] * EXP[-(ΔY2)^2] * EXP[-(ΔY3)^2] * .. = EXP<strong>{-[(ΔY1)^2 + (ΔY2)^2 + (ΔY3)^2 + ..</strong>]} 最大化这个概率就是要最小化 (ΔY1)^2 + (ΔY2)^2 + (ΔY3)^2 + .. 。 熟悉这个式子吗？</p>
<p><strong>5. </strong><strong>朴素贝叶斯方法</strong></p>
<p>朴素贝叶斯方法是一个很特别的方法，所以值得介绍一下。我们用朴素贝叶斯在垃圾邮件过滤中的应用来举例说明。</p>
<p><strong>5.1 </strong><strong>贝叶斯垃圾邮件过滤器</strong></p>
<p>问题是什么？问题是，给定一封邮件，判定它是否属于垃圾邮件。按照先例，我们还是用 D 来表示这封邮件，注意 D 由 N 个单词组成。我们用 h+ 来表示垃圾邮件，h- 表示正常邮件。问题可以形式化地描述为求：</p>
<p>P(h+|D) = P(h+) * P(D|h+) / P(D)</p>
<p>P(h-|D) = P(h-) * P(D|h-) / P(D)</p>
<p>其中 P(h+) 和 P(h-) 这两个先验概率都是很容易求出来的，只需要计算一个邮件库里面垃圾邮件和正常邮件的比例就行了。然而 P(D|h+) 却不容易求，因为 D 里面含有 N 个单词 d1, d2, d3, .. ，所以P(D|h+) = P(d1,d2,..,dn|h+) 。我们又一次遇到了数据稀疏性，为什么这么说呢？P(d1,d2,..,dn|h+) 就是说在垃圾邮件当中出现跟我们目前这封邮件一模一样的一封邮件的概率是多大！开玩笑，每封邮件都是不同的，世界上有无穷多封邮件。瞧，这就是数据稀疏性，因为可以肯定地说，你收集的训练数据库不管里面含了多少封邮件，也不可能找出一封跟目前这封一模一样的。结果呢？我们又该如何来计算 P(d1,d2,..,dn|h+) 呢？</p>
<p>我们将 P(d1,d2,..,dn|h+)&#160; 扩展为： P(d1|h+) * P(d2|d1, h+) * P(d3|d2,d1, h+) * .. 。熟悉这个式子吗？这里我们会使用一个更激进的假设，我们假设 di 与 di-1 是完全条件无关的，于是式子就简化为 P(d1|h+) * P(d2|h+) * P(d3|h+) * .. 。这个就是所谓的<a href="http://en.wikipedia.org/wiki/Conditional_independence">条件独立假设</a>，也正是朴素贝叶斯方法的朴素之处。而计算 P(d1|h+) * P(d2|h+) * P(d3|h+) * .. 就太简单了，只要统计 di 这个单词在垃圾邮件中出现的频率即可。关于贝叶斯垃圾邮件过滤更多的内容可以参考<a href="http://en.wikipedia.org/wiki/Bayesian_spam_filtering">这个条目</a>，注意其中提到的其他资料。</p>
<p><strong>一点注记</strong>：这里，为什么有这个数据稀疏问题，还是因为统计学习方法工作在浅层面，世界上的单词就算不再变多也是非常之多的，单词之间组成的句子也是变化多端，更不用说一篇文章了，文章数目则是无穷的，所以在这个层面作统计，肯定要被数据稀疏性困扰。我们要注意，虽然句子和文章的数目是无限的，然而就拿邮件来说，如果我们只关心邮件中句子的语义（进而更高抽象层面的“意图”（语义，意图如何可计算地定义出来是一个人工智能问题），在这个层面上可能性便大大缩减了，我们关心的抽象层面越高，可能性越小。单词集合和句子的对应是多对一的，句子和语义的对应又是多对一的，语义和意图的对应还是多对一的，这是个层级体系。神经科学的发现也表明大脑的皮层大致有一种层级结构，对应着越来越抽象的各个层面，至于如何具体实现一个可放在计算机内的大脑皮层，仍然是一个未解决问题，以上只是一个原则（principle）上的认识，只有当 computational 的 cortex 模型被建立起来了之后才可能将其放入电脑。</p>
<p><strong>5.2 </strong><strong>为什么朴素贝叶斯方法令人诧异地好——一个理论解释</strong></p>
<p>朴素贝叶斯方法的条件独立假设看上去很傻很天真，为什么结果却很好很强大呢？就拿一个句子来说，我们怎么能鲁莽地声称其中任意一个单词出现的概率只受到它前面的 3 个或 4 个单词的影响呢？别说 3 个，有时候一个单词的概率受到上一句话的影响都是绝对可能的。那么为什么这个假设在实际中的表现却不比决策树差呢？有人对此提出了一个理论解释，并且建立了什么时候朴素贝叶斯的效果能够等价于非朴素贝叶斯的充要条件，这个解释的核心就是：有些独立假设在各个分类之间的分布都是均匀的所以对于似然的相对大小不产生影响；即便不是如此，也有很大的可能性各个独立假设所产生的消极影响或积极影响互相抵消，最终导致结果受到的影响不大。具体的数学公式请参考<a href="http://www.cs.unb.ca/profs/hzhang/publications/FLAIRS04ZhangH.pdf">这篇 paper</a> 。</p>
<p><strong>6. </strong><strong>层级贝叶斯模型</strong></p>
<p><a href="http://mindhacks.cn/wp-content/uploads/2009/02/i6.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="i6" border="0" alt="i6" src="http://mindhacks.cn/wp-content/uploads/2009/02/i6-thumb.png" width="226" height="262" /></a> </p>
<p><a href="http://en.wikipedia.org/wiki/Hierarchical_Bayes_model">层级贝叶斯模型</a>是现代贝叶斯方法的标志性建筑之一。前面讲的贝叶斯，都是在同一个事物层次上的各个因素之间进行统计推理，然而层次贝叶斯模型在哲学上更深入了一层，将这些因素背后的因素（原因的原因，原因的原因，以此类推）囊括进来。一个教科书例子是：如果你手头有 N 枚硬币，它们是同一个工厂铸出来的，你把每一枚硬币掷出一个结果，然后基于这 N 个结果对这 N 个硬币的 θ （出现正面的比例）进行推理。如果根据最大似然，每个硬币的 θ 不是 1 就是 0 （这个前面提到过的），然而我们又知道每个硬币的 p(θ) 是有一个先验概率的，也许是一个 beta 分布。也就是说，每个硬币的实际投掷结果 Xi 服从以 θ 为中心的正态分布，而 θ 又服从另一个以 Ψ 为中心的 beta 分布。层层因果关系就体现出来了。进而 Ψ 还可能依赖于因果链上更上层的因素，以此类推。</p>
<p><strong>6.1 </strong><strong>隐马可夫模型（HMM）</strong></p>
<p><a href="http://mindhacks.cn/wp-content/uploads/2009/02/i7.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="i7" border="0" alt="i7" src="http://mindhacks.cn/wp-content/uploads/2009/02/i7-thumb.png" width="304" height="244" /></a> </p>
<p>吴军在数学之美系列里面介绍的<a href="http://en.wikipedia.org/wiki/Hidden_Markov_model">隐马可夫模型</a>（HMM）就是一个简单的层级贝叶斯模型：</p>
<blockquote><p>那么怎么根据接收到的信息来推测说话者想表达的意思呢？我们可以利用叫做“隐含马尔可夫模型”（Hidden Markov Model）来解决这些问题。以语音识别为例，当我们观测到语音信号 o1,o2,o3 时，我们要根据这组信号推测出发送的句子 s1,s2,s3。显然，我们应该在所有可能的句子中找最有可能性的一个。用数学语言来描述，就是在已知 o1,o2,o3,&#8230;的情况下，求使得条件概率 P (s1,s2,s3,&#8230;|o1,o2,o3&#8230;.) 达到最大值的那个句子 s1,s2,s3,&#8230;</p>
</blockquote>
<p>吴军的文章中这里省掉没说的是，s1, s2, s3, .. 这个句子的生成概率同时又取决于一组参数，这组参数决定了 s1, s2, s3, .. 这个马可夫链的先验生成概率。如果我们将这组参数记为 λ ，我们实际上要求的是：P(S|O, λ) （其中 O 表示 o1,o2,o3,.. ，S表示 s1,s2,s3,..）</p>
<blockquote><p>当然，上面的概率不容易直接求出，于是我们可以间接地计算它。利用贝叶斯公式并且省掉一个常数项，可以把上述公式等价变换成</p>
<p>P(o1,o2,o3,&#8230;|s1,s2,s3&#8230;.) * P(s1,s2,s3,&#8230;)</p>
<p>其中</p>
<p>P(o1,o2,o3,&#8230;|s1,s2,s3&#8230;.) 表示某句话 s1,s2,s3&#8230;被读成 o1,o2,o3,&#8230;的可能性, 而 P(s1,s2,s3,&#8230;) 表示字串 s1,s2,s3,&#8230;本身能够成为一个合乎情理的句子的可能性，所以这个公式的意义是用发送信号为 s1,s2,s3&#8230;这个数列的可能性乘以 s1,s2,s3.. 本身可以一个句子的可能性，得出概率。</p>
</blockquote>
<p>这里，s1,s2,s3&#8230;本身可以一个句子的可能性其实就取决于参数 λ ，也就是语言模型。所以简而言之就是发出的语音信号取决于背后实际想发出的句子，而背后实际想发出的句子本身的独立先验概率又取决于语言模型。</p>
<p><strong>7. </strong><strong>贝叶斯网络</strong></p>
<p>吴军已经对贝叶斯网络作了科普，请直接跳转到<a href="http://googlechinablog.com/2007/01/bayesian-networks.html">这里</a>。更详细的理论参考所有机器学习的书上都有。</p>
<p><strong>参考资料</strong></p>
<p>一堆机器学习，一堆概率统计，一堆 Google ，和一堆 Wikipedia 条目，一堆 paper 。</p>
<p>部分书籍参考<a href="http://blog.csdn.net/pongba/archive/2008/09/11/2915005.aspx">《机器学习与人工智能资源导引》</a>。</p>
<h3  class="related_post_title">你可能也会喜欢以下文章</h3><ul class="related_post"><li><a href="http://mindhacks.cn/2008/09/11/machine-learning-and-ai-resources/" title="机器学习与人工智能学习资源导引">机器学习与人工智能学习资源导引</a> (6)</li><li><a href="http://mindhacks.cn/2008/06/13/why-is-quicksort-so-quick/" title="数学之美番外篇：快排为什么那样快">数学之美番外篇：快排为什么那样快</a> (9)</li><li><a href="http://mindhacks.cn/2006/10/15/cantor-godel-turing-an-eternal-golden-diagonal/" title="康托尔、哥德尔、图灵&mdash;&mdash;永恒的金色对角线(rev#2)">康托尔、哥德尔、图灵&mdash;&mdash;永恒的金色对角线(rev#2)</a> (10)</li><li><a href="http://mindhacks.cn/2007/12/02/probability-theory-in-evolution/" title="数学之美番外篇：进化论中的概率论">数学之美番外篇：进化论中的概率论</a> (4)</li></ul><hr />
<h3>订阅 Mind Hacks</h3>
<a title="用Google Reader订阅" href="http://fusion.google.com/add?feedurl=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_google.gif" style="border:0" alt="订阅到 | Google" /></a> 
<a title="用鲜果订阅" href="http://www.xianguo.com/subscribe.php?url=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_xianguo.gif" style="border:0" alt="订阅到 | 鲜果" /></a>
<br/> 
<a title="用抓虾订阅" href="http://www.zhuaxia.com/add_channel.php?url=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_zhuaxia.gif" style="border:0" alt="订阅到 | 抓虾" /></a> 
<a title="用有道订阅" href="http://reader.youdao.com/b.do?keyfrom=mindhacks&url=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_yodao1.gif" style="border:0" alt="订阅到 | 有道" /></a> 
<hr />
<h3>我是你的信息过滤器</h3>
想了解作者最近在关注什么，欢迎 Follow <a href="https://twitter.com/pongba">pongba@Twitter</a>
<br/>
程序员朋友请到作者发起的 <a href="https://groups.google.com/group/pongba">TopLanguage</a> (<a href="http://mindhacks.cn/about-toplanguage/">about</a>) 社群逛逛，定有收获 :)
<br/>
想了解作者在阅读哪些书，请到 <a href="http://www.douban.com/people/pongba/">pongba@豆瓣</a>，或者直接访问以下四个豆列：<a href="http://www.douban.com/doulist/46003/">[只读经典]思维改变生活</a> | <a href="http://www.douban.com/doulist/127649/">[只读经典]思考的技术与艺术</a> | <a href="http://www.douban.com/doulist/197706/">决策与判断</a> | <a href="http://www.douban.com/doulist/176513/">机器学习与人工智能书籍资源导引</a> 。
<br/>
想了解作者平时在互联网上的阅读，请订阅 <a href="http://delicious.com/pongba/shared-reading">pongba-shared-reading@delicious</a> 。
<hr />
<p><small>
本文由 刘未鹏 发布在 <a href="http://mindhacks.cn">刘未鹏 | Mind Hacks</a>, 2008. | <a href="http://mindhacks.cn/2008/09/21/the-magical-bayesian-method/#commenting">26 条评论</a> | 标签: <a href="http://mindhacks.cn/tags/%e6%95%b0%e5%ad%a6/" rel="tag">数学</a>, <a href="http://mindhacks.cn/tags/%e6%9c%ba%e5%99%a8%e5%ad%a6%e4%b9%a0%e4%b8%8e%e4%ba%ba%e5%b7%a5%e6%99%ba%e8%83%bd/" rel="tag">机器学习与人工智能</a>, <a href="http://mindhacks.cn/tags/%e8%ae%a1%e7%ae%97%e6%9c%ba%e7%a7%91%e5%ad%a6/" rel="tag">计算机科学</a>
<br/>
转载请注明作者，出处，以及<a href="http://mindhacks.cn/2008/09/21/the-magical-bayesian-method/">原始超链接</a>: http://mindhacks.cn/2008/09/21/the-magical-bayesian-method/
</small></p>]]></content:encoded>
			<wfw:commentRss>http://mindhacks.cn/2008/09/21/the-magical-bayesian-method/feed/</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
		<item>
		<title>数学之美番外篇：快排为什么那样快</title>
		<link>http://mindhacks.cn/2008/06/13/why-is-quicksort-so-quick/</link>
		<comments>http://mindhacks.cn/2008/06/13/why-is-quicksort-so-quick/#comments</comments>
		<pubDate>Fri, 13 Jun 2008 11:53:00 +0000</pubDate>
		<dc:creator>刘未鹏</dc:creator>
				<category><![CDATA[数学]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[计算机科学]]></category>

		<guid isPermaLink="false">http://mindhacks.cn/2008/06/13/why-is-quicksort-so-quick/</guid>
		<description><![CDATA[知道这个理论是在TopLanguage上的一次讨论，先是g9转了David MacKay的一篇文章，然后引发了牛人们的一场关于信息论的讨论。Anyway，正如g9很久以前在Blog里面所说的： 

有时无知是福。俺看到一点新鲜的科普也能觉得造化神奇。刚才读Gerald Jay Sussman（SICP作者）的文章，Building Robust Systems – an essay，竟然心如小鹿乱撞，手心湿润，仿佛第一次握住初恋情人温柔的手。

而看到MacKay的这篇文章我也有这种感觉——以前模糊的东西忽然有了深刻的解释，一切顿时变得明白无比。原来看问题的角度或层面能够带来这么大的变化。再一次印证了越是深刻的原理往往越是简单和强大。所以说，土鳖也有土鳖的幸福:P 

这篇文章相当于MacKay原文的白话文版。MacKay在原文中用到了信息论的知识，后者在我看来并不是必须的，尽管计算的时候方便，但与本质无关。所以我用大白话解释了一通。]]></description>
			<content:encoded><![CDATA[<p><strong>目录</strong></p>
<p>0. 前言</p>
<p>1. 猜数字</p>
<p>2. 称球</p>
<p>3. 排序</p>
<p>&#160;&#160;&#160; 3.1 为什么堆排比快排慢</p>
<p>&#160;&#160;&#160; 3.2 为什么快排其实也不是那么快</p>
<p>&#160;&#160;&#160; 3.3 基排又为什么那么快呢</p>
<p>4. 信息论！信息论？</p>
<p>5. 小结</p>
<p><strong>0. </strong><strong>前言</strong></p>
<p>知道这个理论是在<a href="http://groups.google.com/group/pongba">TopLanguage</a>上的一次讨论，先是g9<a href="http://groups.google.com/group/pongba/msg/f95aa12feb4dfd67">转了David MacKay的一篇文章</a>，然后引发了牛人们的<a href="http://groups.google.com/group/pongba/browse_frm/thread/28ac39e0222becf2">一场关于信息论的讨论</a>。Anyway，正如g9很久以前在<a href="http://blog.csdn.net/g9yuayon">Blog</a>里面所<a href="http://blog.csdn.net/g9yuayon/archive/2007/04/22/1574518.aspx">说</a>的： </p>
<blockquote><p>有时无知是福。俺看到一点新鲜的科普也能觉得造化神奇。刚才读Gerald Jay Sussman（<a href="http://mitpress.mit.edu/sicp/">SICP</a>作者）的文章，<a href="http://swiss.csail.mit.edu/classes/symbolic/spring07/readings/robust-systems.pdf">Building Robust Systems – an essay</a>，竟然心如小鹿乱撞，手心湿润，仿佛第一次握住初恋情人温柔的手。</p>
</blockquote>
<p>而看到<a href="http://users.aims.ac.za/~mackay/">MacKay</a>的这篇文章我也有这种感觉——以前模糊的东西忽然有了深刻的解释，一切顿时变得明白无比。原来看问题的角度或层面能够带来这么大的变化。再一次印证了越是深刻的原理往往越是简单和强大。所以说，土鳖也有土鳖的幸福:P </p>
<p>这篇文章相当于MacKay<a href="http://users.aims.ac.za/~mackay/sorting/sorting.html">原文</a>的白话文版。MacKay在原文中用到了信息论的知识，后者在我看来并不是必须的，尽管计算的时候方便，但与本质无关。所以我用大白话解释了一通。 </p>
<p><strong>1. </strong><strong>猜数字 </strong></p>
<p>我们先来玩一个猜数字游戏：我心里默念一个1~64之间的数，你来猜（你只能问答案是“是”或“否”的问题）。为了保证不论在什么情况下都能以尽量少的次数猜中，你应该采取什么策略呢？很显然，二分。先是猜是不是位于1~32之间，排除掉一半可能性，然后对区间继续二分。这种策略能够保证无论数字怎么跟你捉迷藏，都能在log_2{n}次以内猜中。用算法的术语来说就是它的下界是最好的。 </p>
<p>我们再来回顾一下这个游戏所蕴含的本质：为什么这种策略具有最优下界？答案也很简单，这个策略是平衡的。反之如果策略不是平衡的，比如问是不是在1~10之间，那么一旦发现不是在1~10之间的话就会剩下比N/2更多的可能性需要去考察了。 </p>
<p><a href="http://blog.youxu.info/">徐宥</a>在讨论中提到，这种策略的本质可以概括成“让未知世界无机可乘”。它是没有“弱点的”，<strong>答案的任何一个分支都是等概率的</strong>。反之，一旦某个分支蕴含的可能性更多，当情况落到那个分支上的时候你就郁闷了。比如猜数字游戏最糟糕的策略就是一个一个的猜：是1吗？是2吗？&#8230; 因为这种猜法最差的情况下需要64次才能猜对，下界非常糟糕。二分搜索为什么好，就是因为它每次都将可能性排除一半并且<strong>无论如何</strong>都能排除一半（它是最糟情况下表现最好的）。 </p>
<p><strong>2. </strong><strong>称球 </strong></p>
<p>12个小球，其中有一个是坏球。有一架天平。需要你用最少的称次数来确定哪个小球是坏的并且它到底是轻还是重。 </p>
<p>这个问题是一道流传已久的智力题。网络上也有很多讲解，还有泛化到N个球的情况下的严格证明。也有零星的一些地方提到从信息论的角度来看待最优解法。本来我一直认为这道题目除了试错之外没有其它高妙的思路了，只能一个个方法试，并尽量从结果中寻找信息，然后看看哪种方案最少。 </p>
<p>然而，实际上它的确有其它的思路，一个更本质的思路，而且根本用不着信息论这么拗口的知识。 </p>
<p>我们先回顾一下猜数字游戏。为了保证任何情况下以最少次数猜中，我们的策略是每次都排除恰好一半的可能性。类比到称球问题上：坏球可能是12个球中的任意一个，这就是12种可能性；而其中每种可能性下坏球可能轻也可能重。于是“坏球是哪个球，是轻是重”这个问题的答案就有12×2=24种可能性。现在我们用天平来称球，就等同于对这24种可能性发问，由于天平的输出结果有三种“平衡、左倾、右倾”，这就相当于我们的问题有三个答案，即可以将所有的可能性切成三份，根据猜数字游戏的启发，我们应当尽量让这三个分支概率均等，即平均切分所有的可能性为三等份。如此一来的话一次称量就可以将答案的可能性缩减为原来的1/3，三次就能缩减为1/27。而总共才有24种可能性，所以理论上是完全可以3次称出来的。 </p>
<p>如何称的指导原则有了，构造一个称的策略就不是什么太困难的事情了。首先不妨解释一下为什么最直观的称法不是最优的——6、6称：在6、6称的时候，天平平衡的可能性是0。刚才说了，最优策略应该使得天平三种状态的概率均等，这样才能三等分答案的所有可能性。 </p>
<p>为了更清楚的看待这个问题，我们不妨假设有6个球，来考虑一下3、3称和2、2称的区别： </p>
<p>在未称之前，一共有12种可能性：1轻、1重、2轻、2重、&#8230;、6轻、6重。现在将1、2、3号放在左边，4、5、6放在右边3、3称了之后，不失一般性假设天平左倾，那么小球的可能性就变成了原来的一半（6种）：1重、2重、3重、4轻、5轻、6轻。即这种称法能排除一半可能性。 </p>
<p>现在再来看2、2称法，即1、2放左边，3、4放右边，剩下的5、6不称，放一边。假设结果是天平平衡，那么可能性剩下——4种：5重、5轻、6重、6轻。假设天平左倾，可能性也剩下4种：1重、2重、3轻、4轻。右倾和左倾的情况类似。总之，这种称法，不管天平结果如何，情况都被我们缩小到了原来的三分之一！我们充分利用了“天平的结果状态可能有三种”这个条件来三等分所有可能性，而不是二等分。 </p>
<p>说到这里，剩下的事情就实在很简单了：第二步称法，只要记着这样一个指导思想——你选择的称法必须使得当天平平衡的时候答案剩下的可能性和天平左倾（右倾）的时候答案剩下的可能性一样多。实际上，这等同于你得选择一种称法，使得天平输出三种结果的概率是均等的，因为天平输出某个结果的概率就等同于所有支持这个结果（左倾、右倾、平衡）的答案可能性的和，并且答案的每个可能性都是等概率的。 </p>
<p>MacKay在他的书《Information Theory: Inference and Learning Algorithms》（<a href="http://users.aims.ac.za/~mackay/itila/book.html">作者开放免费电子书</a>）里面4.1节专门讲了这个称球问题，还画了一张不错的图，我就照抄了： </p>
<p><a href="http://mindhacks.cn/wp-content/uploads/2009/02/23131201.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="2313120" border="0" alt="2313120" src="http://mindhacks.cn/wp-content/uploads/2009/02/2313120-thumb1.jpg" width="476" height="480" /></a> </p>
<p>图中“1+”是指“1号小球为重”这一可能性。一开始一共有24种可能性。4、4称了之后不管哪种情况（分支），剩下来的可能性总是4种。这是一个完美的三分。然后对每个分支构造第二次称法，这里你只要稍加演算就可以发现，分支1上的第二次称法，即“1、2、6对3、4、5”这种称法，天平输出三种结果的可能性是均等的（严格来说是几乎均等）。这就是为什么这个称法能够在最坏的情况下也能表现最好的原因，没有哪个分支是它的弱点，它必然能将情况缩小到原来的1/3。 </p>
<p><strong>3. </strong><strong>排序 </strong></p>
<p>用前面的看问题视角，排序的本质可以这样来表述：一组未排序的N个数字，它们一共有N!种重排，其中只有一种排列是满足题意的（譬如从大到小排列）。换句话说，排序问题的可能性一共有N!种。任何基于比较的排序的基本操作单元都是“比较a和b”，这就相当于猜数字游戏里面的一个问句，显然这个问句的答案只能是“是”或“否”，一个只有两种输出的问题最多只能将可能性空间切成两半，根据上面的思路，最佳切法就是切成1/2和1/2。也就是说，我们希望在比较了a和b的大小关系之后，如果发现a&lt;b的话剩下的排列可能性就变成N!/2，如果发现a&gt;b也是剩下N!/2种可能性。由于假设每种排列的概率是均等的，所以这也就意味着支持a&lt;b的排列一共有N!/2个，支持a&gt;b的也是N!/2个，换言之，a&lt;b的概率等于a&gt;b的概率。 </p>
<p>我们希望每次在比较a和b的时候，a&lt;b和a&gt;b的概率是均等的，这样我们就能保证无论如何都能将可能性缩小为原来的一半了！最优下界。 </p>
<p>一个直接的推论是，如果每次都像上面这样的完美比较，那么N个元素的N!种可能排列只需要log_2{N!}就排查玩了，而log_2{N!}近似于NlogN。这正是快排的复杂度。 </p>
<p><strong>3.1 </strong><strong>为什么堆排比快排慢 </strong></p>
<p>回顾一下堆排的过程： </p>
<p>1. 建立最大堆（堆顶的元素大于其两个儿子，两个儿子又分别大于它们各自下属的两个儿子&#8230; 以此类推） </p>
<p>2. 将堆顶的元素和最后一个元素对调（相当于将堆顶元素（最大值）拿走，然后将堆底的那个元素补上它的空缺），然后让那最后一个元素从顶上往下滑到恰当的位置（重新使堆最大化）。 </p>
<p>3. 重复第2步。 </p>
<p>这里的关键问题就在于第2步，堆底的元素肯定很小，将它拿到堆顶和原本属于最大元素的两个子节点比较，它比它们大的可能性是微乎其微的。实际上它肯定小于其中的一个儿子。而大于另一个儿子的可能性非常小。于是，这一次比较的结果就是概率不均等的，根据前面的分析，概率不均等的比较是不明智的，因为它并不能保证在糟糕情况下也能将问题的可能性削减到原本的1/2。可以想像一种极端情况，如果a肯定小于b，那么比较a和b就会什么信息也得不到——原本剩下多少可能性还是剩下多少可能性。 </p>
<p>在堆排里面有大量这种近乎无效的比较，因为被拿到堆顶的那个元素几乎肯定是很小的，而靠近堆顶的元素又几乎肯定是很大的，将一个很小的数和一个很大的数比较，结果几乎肯定是“小于”的，这就意味着问题的可能性只被排除掉了很小一部分。 </p>
<p>这就是为什么堆排比较慢（堆排虽然和快排一样复杂度都是O(NlogN)但堆排复杂度的常系数更大）。 </p>
<p>MacKay也提供了一个修改版的堆排：每次不是将堆底的元素拿到上面去，而是直接比较堆顶（最大）元素的两个儿子，即选出次大的元素。由于这两个儿子之间的大小关系是很不确定的，两者都很大，说不好哪个更大哪个更小，所以这次比较的两个结果就是概率均等的了。具体参考<a href="http://users.aims.ac.za/~mackay/sorting/sorting.html">这里</a>。 </p>
<p><strong>3.2 </strong><strong>为什么快排其实也不是那么快 </strong></p>
<p>我们考虑快排的过程：随机选择一个元素做“轴元素”，将所有大于轴元素的移到左边，其余移到右边。根据这个过程，快排的第一次比较就是将一个元素和轴元素比较，这个时候显而易见的是，“大于”和“小于”的可能性各占一半。这是一次漂亮的比较。 </p>
<p>然而，快排的第二次比较就不那么高明了：我们不妨令轴元素为pivot，第一次比较结果是a1&lt;pivot，那么可以证明第二次比较a2也小于pivot的可能性是2/3！这容易证明：如果a2&gt;pivot的话，那么a1，a2，pivot这三个元素之间的关系就完全确定了——a1&lt;pivot&lt;a2，剩下来的元素排列的可能性我们不妨记为P（不需要具体算出来）。而如果a2&lt;pivot呢？那么a1和a2的关系就仍然是不确定的，也就是说，这个分支里面含有两种情况：a1&lt;a2&lt;pivot，以及a2&lt;a1&lt;pivot。对于其中任一种情况，剩下的元素排列的可能性都是P，于是这个分支里面剩下的排列可能性就是2P。所以当a2&lt;pivot的时候，还剩下2/3的可能性需要排查。 </p>
<p>再进一步，如果第二步比较果真发现a2&lt;pivot的话，第三步比较就更不妙了，模仿上面的推理，a3&lt;pivot的概率将会是3/4！ </p>
<p>这就是快排也不那么快的原因，因为它也没有做到每次比较都能将剩下的可能性砍掉一半。 </p>
<p><strong>3.3 </strong><strong>鸡排为什么又那么快呢？ </strong></p>
<p>传统的解释是：<a href="http://en.wikipedia.org/wiki/Radix_sort">基排</a>不是基于比较的，所以不具有后者的局限性。话是没错，但其实还可以将它和基于比较的排序做一个类比。 </p>
<p>基排的过程也许是源于我们理顺一副牌的过程：如果你有N（N&lt;=13）张牌，乱序，如何理顺呢？我们假象桌上有十三个位置，然后我们将手里的牌一张一张放出去，如果是3，就放在位置3上，如果是J，就放在位置11上，放完了之后从位置1到位置13收集所有的牌（没有牌的位置上不收集任何牌）。 </p>
<p>我们可以这样来理解基排高效的本质原因：假设前i张牌都已经放到了它们对应的位置上，第i+1张牌放出去的时候，实际上就相当于“一下子”就确立了它和前i张牌的大小关系，用O(1)的操作就将这张牌正确地插入到了前i张牌中的正确位置上，这个效果就相当于插入排序的第i轮原本需要比较O(i)次的，现在只需要O(1)了。 </p>
<p>但是，为什么基排能够达到这个效果呢？上面只是解释了过程，解释了过程不代表解释了本质。 </p>
<p>当i张牌放到位之后，放置第i+1张牌的时候有多少种可能性？大约i+1种，因为前i张牌将13个位置分割成了i+1个区间——第i+1张牌可以落在任意一个区间。所以放置第i+1张牌就好比是询问这样一个问题：“这张牌落在哪个区间呢？”而这个问题的答案有i+1种可能性？所以它就将剩下来的可能性均分成了i+1份（换句话说，砍掉了i/i+1的可能性！）。再看看基于比较的排序吧：由于每次比较只有两种结果，所以最多只能将剩下的可能性砍掉一半。 </p>
<p>这就是为什么基排要快得多。而所有基于比较的排序都逃脱不了NlogN的宿命。 </p>
<p><strong>4. </strong><strong>信息论！信息论？ </strong></p>
<p>本来呢，MacKay写那篇文章是想用信息论来解释为什么堆排慢，以及为什么快排也慢的。MacKay在他的文章中的解释是，只有提出每种答案的概率都均等的问题，才能获得最大信息量。然而，仔细一想，其实这里信息论并不是因，而是果。这里不需要用信息论就完全能够解释，而且更明白。信息论只是对这个解释的一个形式化。当然，信息论在其它地方还是有应用的。但这里其实用不着信息论这么重量级的东西（也许具体计算一些数据的时候是需要的），而是只需要一种看问题的本质视角：将排序问题看成和猜数字一样，是通过问问题来缩小/排除（narrow down）结果的可能性区间，这样一来，就会发现，“最好的问题”就是那些能够均分所有可能性的问题，因为那样的话不管问题的答案如何，都能排除掉k-1/k（k为问题的答案有多少种输出——猜数字里面是2，称球里面是3）种可能性，而不均衡的问题总会有一个或一些答案分支排除掉的可能性要小于k-1/k。于是策略的下界就被拖累了。 </p>
<p><strong>5. </strong><strong>小结 </strong></p>
<p>这的确是“小结”，因为两点： </p>
<p>1. 这个问题可以有信息论的理论解释，而信息论则是一个相当大的领域了。</p>
<p>2. 文中提到的这种看问题的视角除了用于排序、称球，还能够运用到哪些问题上（比如搜索）。</p>
<p><strong>Update(06/13/2008)</strong> : <a href="http://blog.youxu.info/">徐宥</a>在讨论中<a href="http://groups.google.com/group/pongba/msg/07493e329ed920ff">继续提到</a>：</p>
<blockquote><p>另外，这几天我重新把TAOCP 第三卷(第二版)翻出来看了看 Knuth 怎么说这个问题的, 发现真是牛大了： </p>
<p>先说性能： </p>
<p>pp148, section 5.2.3 说： </p>
<p>When N = 1000, the approximate average runiing time on MIX are      <br />160000u for heapsort       <br />130000u for shellsort       <br />80000u&#160; for quicksort </p>
<p>这里,&#160; Knuth 同学发现一般情况下 heapsort 表现很不好. 于是，在下文他就说，习题18 (pp156, 难度21) </p>
<p>(R.W.Floyd) During the selection phase of heapsort, the key K tends to      <br />be quite small, so that nearly all the comparisons in step H6 find       <br />K&lt;K_j. Show how to modify the algorithm so that K is not compared with       <br />K_j in the main loop of the computation, thereby nearly cutting the       <br />average number of comparisons in half. </p>
<p>答案里面的方法和DMK的方法是一样的。(我觉得DMK是看了这个论文或者TAoCP的) 这里说 by half，就正好和快排差不多了。 </p>
<p>再说信息论分析： </p>
<p>在5.3.1 (pp181) 高爷爷就说, “排序问题可以看成是一个树上的鸟儿排排站的问题. (还特地画了一棵树), 下一段就说, 其实这个也      <br />有等价说法, 就是信息论, 我们从称球问题说起&#8230;” </p>
<p>然后后面一直讲信息论和最小比较排序&#8230;</p>
</blockquote>
<p>高爷爷真不愧是姓高的，囧rz..</p>
<h3  class="related_post_title">你可能也会喜欢以下文章</h3><ul class="related_post"><li><a href="http://mindhacks.cn/2008/09/21/the-magical-bayesian-method/" title="数学之美番外篇：平凡而又神奇的贝叶斯方法">数学之美番外篇：平凡而又神奇的贝叶斯方法</a> (26)</li><li><a href="http://mindhacks.cn/2007/12/02/probability-theory-in-evolution/" title="数学之美番外篇：进化论中的概率论">数学之美番外篇：进化论中的概率论</a> (4)</li><li><a href="http://mindhacks.cn/2006/10/15/cantor-godel-turing-an-eternal-golden-diagonal/" title="康托尔、哥德尔、图灵&mdash;&mdash;永恒的金色对角线(rev#2)">康托尔、哥德尔、图灵&mdash;&mdash;永恒的金色对角线(rev#2)</a> (10)</li><li><a href="http://mindhacks.cn/2008/09/11/machine-learning-and-ai-resources/" title="机器学习与人工智能学习资源导引">机器学习与人工智能学习资源导引</a> (6)</li><li><a href="http://mindhacks.cn/2008/07/07/the-importance-of-knowing-why/" title="知其所以然（以算法学习为例）">知其所以然（以算法学习为例）</a> (4)</li><li><a href="http://mindhacks.cn/2008/04/18/learning-from-polya/" title="跟波利亚学解题(rev#3)">跟波利亚学解题(rev#3)</a> (7)</li></ul><hr />
<h3>订阅 Mind Hacks</h3>
<a title="用Google Reader订阅" href="http://fusion.google.com/add?feedurl=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_google.gif" style="border:0" alt="订阅到 | Google" /></a> 
<a title="用鲜果订阅" href="http://www.xianguo.com/subscribe.php?url=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_xianguo.gif" style="border:0" alt="订阅到 | 鲜果" /></a>
<br/> 
<a title="用抓虾订阅" href="http://www.zhuaxia.com/add_channel.php?url=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_zhuaxia.gif" style="border:0" alt="订阅到 | 抓虾" /></a> 
<a title="用有道订阅" href="http://reader.youdao.com/b.do?keyfrom=mindhacks&url=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_yodao1.gif" style="border:0" alt="订阅到 | 有道" /></a> 
<hr />
<h3>我是你的信息过滤器</h3>
想了解作者最近在关注什么，欢迎 Follow <a href="https://twitter.com/pongba">pongba@Twitter</a>
<br/>
程序员朋友请到作者发起的 <a href="https://groups.google.com/group/pongba">TopLanguage</a> (<a href="http://mindhacks.cn/about-toplanguage/">about</a>) 社群逛逛，定有收获 :)
<br/>
想了解作者在阅读哪些书，请到 <a href="http://www.douban.com/people/pongba/">pongba@豆瓣</a>，或者直接访问以下四个豆列：<a href="http://www.douban.com/doulist/46003/">[只读经典]思维改变生活</a> | <a href="http://www.douban.com/doulist/127649/">[只读经典]思考的技术与艺术</a> | <a href="http://www.douban.com/doulist/197706/">决策与判断</a> | <a href="http://www.douban.com/doulist/176513/">机器学习与人工智能书籍资源导引</a> 。
<br/>
想了解作者平时在互联网上的阅读，请订阅 <a href="http://delicious.com/pongba/shared-reading">pongba-shared-reading@delicious</a> 。
<hr />
<p><small>
本文由 刘未鹏 发布在 <a href="http://mindhacks.cn">刘未鹏 | Mind Hacks</a>, 2008. | <a href="http://mindhacks.cn/2008/06/13/why-is-quicksort-so-quick/#commenting">9 条评论</a> | 标签: <a href="http://mindhacks.cn/tags/%e6%95%b0%e5%ad%a6/" rel="tag">数学</a>, <a href="http://mindhacks.cn/tags/%e7%ae%97%e6%b3%95/" rel="tag">算法</a>, <a href="http://mindhacks.cn/tags/%e8%ae%a1%e7%ae%97%e6%9c%ba%e7%a7%91%e5%ad%a6/" rel="tag">计算机科学</a>
<br/>
转载请注明作者，出处，以及<a href="http://mindhacks.cn/2008/06/13/why-is-quicksort-so-quick/">原始超链接</a>: http://mindhacks.cn/2008/06/13/why-is-quicksort-so-quick/
</small></p>]]></content:encoded>
			<wfw:commentRss>http://mindhacks.cn/2008/06/13/why-is-quicksort-so-quick/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>数学之美番外篇：进化论中的概率论</title>
		<link>http://mindhacks.cn/2007/12/02/probability-theory-in-evolution/</link>
		<comments>http://mindhacks.cn/2007/12/02/probability-theory-in-evolution/#comments</comments>
		<pubDate>Sun, 02 Dec 2007 10:55:00 +0000</pubDate>
		<dc:creator>刘未鹏</dc:creator>
				<category><![CDATA[数学]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[概率论]]></category>

		<guid isPermaLink="false">http://mindhacks.cn/2007/12/02/probability-theory-in-evolution/</guid>
		<description><![CDATA[偶然性在进化中确实存在（例如，偶然性的突变可以产生新的特征），但是进化并不依赖偶然性来产生新的器官、蛋白质或其他实体。截然相反的是，自然选择，作为进化中已知的最主要机制，却会明确保留“需要的”（能适应的）特性，消除“不需要的”（无法适应的）特性。只要选择的影响力存在，自然选择就能把进化向一个方向推进，在出乎意料的短时间内产生复杂的结构。举个例子，现有由13个字母构成的序列“TOBEORNOTTOBE”，假设有几百万只猴子，每只猴子每秒钟挑一条短语，需要78,800年才能从26^13种可能中选出这样的排列。不过，Glendale College的Richard Hardison在20世纪80年代写过一个程序，它能够在随机产生序列的同时，保证那些已经出现在正确位置上的字母不会变化（这样做倒有点《汉姆雷特》 的味道。译注：这个句子看了大半天才明白，嘿嘿）。这个程序平均只需要336次迭代就能生成上文提到的短语，时间少于90秒。更神奇的是，把莎士比亚的整个剧本重新生成一遍也只需要四天半时间。]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.xiaolai.net/">李笑来</a>老师<a href="http://www.xiaolai.net/?p=565">在blog上转了</a>一篇宏文，“<a href="http://www.sciam.com/article.cfm?articleID=000D4FEC-7D5B-1D07-8E49809EC588EEDF">15 Answers to Creationist Nonsense</a>”；然后<a href="http://www.luanxiang.org/">余晟</a>同学（顺便推荐<a href="http://www.luanxiang.org/">余晟</a>同学译的<a href="http://www.douban.com/subject/2154713/">《精通正则表达式》（第3版）</a>）把它给<a href="http://www.luanxiang.org/blog/?p=348">译了出来</a>。漂亮的文章加上漂亮的翻译，当然是要拜读的:-)</p>
<p>进化论从其诞生以来受到的非难不计其数。这里提到的这篇便收集了广为神创论者提出以及广为大众误解的一些观点。其中有一点尤其引起了我的兴趣，如下：</p>
<blockquote><p><b>8. </b><b>严格说起来，我们很难相信复杂如蛋白质的物质能偶然出现，更不用说人或是活细胞了。</b><b></b></p>
<p>偶然性在进化中确实存在（例如，偶然性的突变可以产生新的特征），但是进化并不依赖偶然性来产生新的器官、蛋白质或其他实体。截然相反的是，自然选择，作为进化中已知的最主要机制，却会明确保留“需要的”（能适应的）特性，消除“不需要的”（无法适应的）特性。只要选择的影响力存在，自然选择就能把进化向一个方向推进，在出乎意料的短时间内产生复杂的结构。举个例子，现有由13个字母构成的序列“TOBEORNOTTOBE”，假设有几百万只猴子，每只猴子每秒钟挑一条短语，需要78,800年才能从26^13种可能中选出这样的排列。不过，Glendale College的Richard Hardison在20世纪80年代写过一个程序，它能够在随机产生序列的同时，保证那些已经出现在正确位置上的字母不会变化（这样做倒有点《汉姆雷特》 的味道。译注：这个句子看了大半天才明白，嘿嘿）。这个程序平均只需要336次迭代就能生成上文提到的短语，时间少于90秒。更神奇的是，把莎士比亚的整个剧本重新生成一遍也只需要四天半时间。</p>
</blockquote>
<p>关于这个随机枚举特定的13字母的单词的问题有点意思。如果是纯粹随机枚举的话，由于长度为13的单词一共有26^13个不同的（假设所有组合都是有效单词的话），其中只有一个跟目标单词一样，也就是说平均（数学期望）枚举26^13次才能枚举出目标串来。</p>
<p>我们不妨把人类的DNA链当成一个长长的单词。突变是产生随机枚举的动力。那么根据上面的分析，要枚举出我们现在用的DNA，需要的迭代次数将是跟DNA链上的“字母数”（碱基对）成指数关系的。枚举一个13个字母的单词就需要26^13次方了，上亿碱基对，需要多少次迭代？更不要说人类的一代更迭就平均要耗上十来二十年了。</p>
<p>从这个角度来看，作为生命只有短短几十年的我们，似乎的确很难理解像眼睛这么复杂精妙的结构是如何从随机的突变中产生出来的。而这也恰恰是神创论者最经常用来非难进化论的论点之一。那么，这个貌似有力的论点到底正确与否呢？其实，在达尔文同学的<a href="http://www.douban.com/subject/1969983/">《物种起源》</a>中就已经进行了一定程度的驳斥。达尔文同学列举了一系列的比我们人类眼睛简单的眼睛结构，从复杂逐渐到简单，其中最简单的“眼睛”只由一些聚集在一起的感光细胞构成；并且，达尔文同学还雄辩了为什么眼睛的复杂性并非是不可归约（irreducible）的。</p>
<p>然而，达尔文同学毕竟不是专业的理工科出生（在大学里面是学神学的），虽然其实践精神是每个科学家的楷模，虽然在《物种起源》中他运用了一系列的证据和推理，某种程度上论证了随机突变加上自然的选择之手，的确能够进化出如眼睛这么复杂的结构。然而他并没有从数学上加以证明，为什么定向选择能够导致在短得多的时间内产生复杂结构，以及这个时间与纯随机枚举相比到底短到什么程度？</p>
<p>我们仍以那个13个字母的单词为例TOBEORNOTTOBE。如果是纯随机枚举的话，平均需要26^13次方才能枚举出一个来。那么，自然中的进化过程也是这样的吗？并非如此。虽然每一个基因位都可能发生变异，然而自然选择之手会将那些“好”的部分留下来，差的部分剔掉（因为突变出来的好基因比差基因更有生存繁殖优势，于是渐渐就会在种群中通过遗传扩散开来）。反映在我们枚举单词的过程中就是，一旦我们枚举出了某个或某些特定位上的字母，那么这个字母就确定下来，不再变动，只需继续枚举剩下来的字母。这样，直觉上需要枚举的次数就会大大减少。而实际上也正是如此，引文中提到的Richard同学写的一个程序便说明了这一点：本来需要78,800年，现在只需90秒。差异何等巨大！</p>
<p>现在，我们关心的问题是，按照后一种枚举方法，能从数学上精确计算出来，要枚举出这个目标单词需要的迭代次数的数学期望吗？（即，平均枚举多少次，才能枚举出它呢？）</p>
<p>Richard同学写的那个程序显示需要平均336次；然而，问题是他写的是一个程序，而不是用数学来计算到底需要多少次。336次是数学期望吗？不是。它是一个实践值。</p>
<p>实际上，我也写了一个这样的程序，然而结果显示却是大约82次左右。那么到底谁的正确呢？</p>
<p>要检验这个结论，更重要的问题是，应该有一个数学方法能够计算出按照这种方法，可（数学）期望通过迭代多少次就迭代出目标串。</p>
<p>为了从数学上解决这个问题。我们需要用到一点基本的概率论知识：</p>
<p>如果一个随机变量X的值为x<sub>i</sub>的几率是P<sub>i</sub>，那么X的数学期望Ex就等于∑x<sub>i</sub>P<sub>i</sub>。举个例子，假设小明的考试成绩为90分的概率为30%，为80分的概率为70%，那么小明的成绩的数学期望便是90×30%+80×70%=83（虽然83其实是个不可能的成绩）。</p>
<p>回到我们要求解的问题：我们想知道需要枚举多少次才能枚举出目标单词。要求解这个问题，我们可以反过来思考：<b>平均每枚举一次能正确枚举出目标单词上的几个字母</b>（这里“正确”的意思当然是要满足“在相同位置上的字母也相同”，比如目标单词是TUBE的话，一旦枚举出POTE，我们就说正确枚举出了最后一个字母E，而T由于位置不对应，因此就不能算是正确的了）。</p>
<p>很显然，平均一次能正确枚举出的字母数目是一个随机变量，不妨令为X。该随机变量依赖于在这次枚举中，目标单词上的每一个位置上的字母是否被正确枚举出来了，于是我们设目标单词第i位上的字母被枚举的结果（即是否枚举中——只有“中”或“不中”两种结果，因而是一个二元随机变量）为随机变量X<sub>i</sub>；X<sub>i</sub>只有“中”或“不中”两种可能，我们将“中”的值量化为1，“不中”为0。由于每一位上枚举中的概率都是独立同分布的，因此对于任意一个X<sub>i</sub>来说，为0（“不中”）的概率皆为25/26；为1（“中”）的概率皆为1/26。这很容易理解，因为字母表中一共有26个字母，随机选择一个，跟目标字母相同的概率自然是1/26，不同的概率则是25/26。</p>
<p>有了X<sub>i</sub>，我们进一步发现，X其实是X<sub>i</sub>的函数：X = ∑X<sub>i</sub>。这个式子这样理解：如果在位置i处枚举中了，那么X<sub>i</sub>便是1，这样就给总共枚举中的位数X贡献了1；否则X<sub>i</sub>则为0，即没有贡献。</p>
<p>现在，我们回过头审视我们想要求得的东西：我们想求得是<b>枚举一次能正确枚举出目标单词上的字母数目的数学期望</b>。也就是X的数学期望EX。由于X = ∑X<sub>i</sub>。于是EX = E(∑X<sub>i</sub>) = ∑EX<sub>i</sub>。而EX<sub>i­</sub>对每个X<sub>i</sub>是相同的（独立同分布嘛），都是0×25/26 + 1×1/26 = 1/26。因此EX = n×EX<sub>i</sub>（其中n是目标单词的长度——本例中是13）= 13×1/26 = 1/2。</p>
<p>综上，我们得出结论：随机枚举一次可（数学）期望枚举中目标单词上的1/2个字母。</p>
<p>1/2个字母？是不是开玩笑？哪有“半个字母”的说法？实际上，因为是数学期望，而数学期望的值很可能并非所有可能值中的任一个，而是它们的概率加权平均，所以半个字母的说法在数学期望上是说得通的；更关键的是，这个期望值给我们提供了一个极其重要的信息，那就是<b>要想枚举中其中的一位，我们（数学期望上）需要枚举</b><b>2</b><b>次才行（因为每次枚举中</b><b>1/2</b><b>位）</b>。</p>
<p>一旦枚举中了其中的一位，那么后面的随机枚举过程便不需要考虑这一位，只需要考虑剩下的了。换句话说，目标单词中的字母便被剔掉了一个，只剩12个字母。而在12个字母的单词中，要想再枚举中一位，需要多少次迭代呢？重用上面的推导过程，EX仍然还是等于n×EX<sub>i</sub>。EX<sub>i</sub>没变，而n变成了12。即迭代一次平均命中12/26个字母，那么要完全命中其中一位字母，便需要26/12（即2.17次）。</p>
<p>如此类推，每次减掉其中的一个字母需要特定次数的枚举，一直到减至只剩最后一个字母，需要26次。把所有这些枚举次数的期望值加起来，便是总共需要枚举的次数了。即26/13 + 26/12 + 26/11 + … + 26/1 ~= 82.68次。</p>
<p><b>另一种思路</b><b></b></p>
<p>我们回顾一下上一个解法过程中的核心问题：<b>要命中其中的一个字母，（数学期望上）需要枚举多少次</b>，我们令这个次数为随机变量X。我们回顾一下数学期望的本质定义：每个可能的值的概率加权平均。于是，要求得X的数学期望EX，我们只需知道X所有可能的取值以及对应于各个取值的概率。</p>
<p>那么，要命中其中一个字母，究竟需要枚举多少次呢？可能是一次就中了，也可能需要两次，也可能需要三次…你会发现，有可能需要任意次。只不过随着所需次数的增加，概率也越来越小。实际上，这是一个无穷级数求和问题；所幸，你马上就会看到，这个求和其实很简单。</p>
<p>所谓一次就命中的意思是，只随机枚举一次，就会命中目标单词中的一个且仅一个字母。这个随机事件由三个部分组成，首先是其余n-1个字母不中，然后是剩下的1个字母中了，再然后是那个命中的字母有n种可能的位置。因此，其概率是[(25/26)^(n-1)]×1/26×n。同理，需要两次才能命中其中一个字母的概率是(25/26)^n×1/26×n×[(25/26)^(n-1)]…以此类推。</p>
<p>而命中其中一个字母所需枚举次数X的数学期望是：</p>
<p>1×一次就中的概率 + 2×二次才中的概率 + 3×三次才中的概率 + …</p>
<p>也就是：</p>
<p>1/26×n×(25/26)^(n-1) [1 + 2×(25/26)^n + 3×(25/26)^2n + 4×(25/26)^3n … ]</p>
<p>左边方括号内的无穷级数求和的形式为1+2q+3q^2+4q^3+…，结果为1/(1-q)^2（利用类似等比级数求和的技巧——错位相减），所以上式求和简化后的结果为：</p>
<p>[n×25^(n-1)×26^n]/[(26^n-25^n)^2]</p>
<p>即，当目标单词长为n时，平均需要[n×25^(n-1)×26^n]/[(26^n-25^n)^2]次枚举才能命中其中一个字母；而一旦命中一个字母之后，该字母就会被从单词当中剔掉，并继续枚举第二个字母，此时n减少了1，因而上式的值也发生了变化。</p>
<p>简言之，长度为N的单词，需要∑[n×25^(n-1)×26^n]/[(26^n-25^n)^2]次（其中n从1变化到N）迭代便能够完全枚举出来。</p>
<p>现在我们回到原来的问题：一个长为13的单词，TOBEORNOTTOBE，究竟需要枚举多少次才能够完全枚举出来呢？按照以上的式子，得出的结果是82.39。而采用前面的不精确近似，计算的结果82.68。跟我自己写的一个枚举程序运行一万次平均之后的结果刚好相符，后者也是在82左右徘徊。</p>
<p>不过，以上两种做法其实都建立在一个假设之上，即我们是一个一个地枚举出目标单词中的字母的。不是两个，也不是三个。然而实际当中可能一次就枚举出多个乃至全部的字母。因此，其实以上两种做法计算出的都是一个不精确的值，这也是为什么它们的结果相近但不一样的原因（真正的结果只有一个）。然而，如果想给出精确表达式或计算方法就非常复杂了，或者说至少我没有想到更简单的表达方法，如果你有不妨告诉我:-)</p>
<p><b>小结</b><b></b></p>
<p>本文介绍了隐藏在自然选择中的概率论，并说明了为什么自然选择能够在相对（与纯随机枚举相比）极短的时间内塑造出复杂的有机体；简而言之，选择之手总是不断地将生物的基因向某个方向推进，一旦基因中变异出有益的片段，该片段就会被选择保留下来并逐渐在种群中蔓延开来，反映到文中讨论的枚举单词的例子中就是，一旦某个位上的字母被枚举出来，便会被保留住，不再受到后续变异的影响（除非是更好的变异）（通过一个数学示例，我们看到，原本需要26^13次迭代才能产生的目标序列，只需82次居然就进化出来了，其间的差距是无法估计的；事实上，计算机算法上就有使用进化思想来实现算法的，也就是所谓的<a href="http://www.douban.com/subject/1232071/">进化算法</a>）；再则，加以种群中数量巨大的个体（每个个体都是一个单独的枚举器），我们就不难理解为什么自然选择能够进化出复杂如眼睛的结构了。</p>
<p><b><i>Update 2007-12-3</i></b>：<a href="http://blog.youxu.info/">徐宥</a>同学<a href="http://blog.csdn.net/pongba/archive/2007/12/02/1912466.aspx#747121">指出</a>，<a href="http://surge.ods.org/listarc/20020621.HTM">在Richard原来写的那个程序中</a>，是采用的“挨个枚举”办法。即先枚举出第一个字母（期望需要26次），然后枚举第二个，如此直到把所有字母枚举完。如此需要的时间为13×26=338；符合文中给出的值。如此说来，之所以我得出的结果跟Richard的不一样，是因为<b>采用的枚举策略不一样</b>，我将“一次枚举”定义为“枚举整个单词”，而非“枚举其中某个字母”。不过我感觉Richard的那个枚举策略显然不符合自然选择的工作方式，自然状况下，每一个基因位（“字母”）都可能发生变异（独立分布，不过变异概率未必一样），而对变异基因的择优筛选则发生在“遗传”这一个环节（严格来说是发生在遗传过程中的“差异繁殖率”上）。</p>
<p>不过，总而言之，尽管计算机模拟的选择算法不同，总的思想是一样的，即一旦加入了“选择之手”，就能够极大地加快进化的速度。</p>
<p><b>下期预告？</b><b></b></p>
<p>本来打算写写Google Pagerank算法的，但那玩意往细了写太麻烦，总找不出动力，所以索性先写篇短的:-) Google Pagerank算法是数学与工程的完美结合，其中蕴含了数学的纯粹和工程的务实，实在值得欣赏，所以，强烈推荐下面这篇：</p>
<p><a href="http://www.ams.org/featurecolumn/archive/pagerank.html">How Google Finds Your Needle in the Web&#8217;s Haystack</a>，那么这篇有多火呢？del.icio.us上save的人竟有1,774人！可见一斑。</p>
<h3  class="related_post_title">你可能也会喜欢以下文章</h3><ul class="related_post"><li><a href="http://mindhacks.cn/2008/06/13/why-is-quicksort-so-quick/" title="数学之美番外篇：快排为什么那样快">数学之美番外篇：快排为什么那样快</a> (9)</li><li><a href="http://mindhacks.cn/2008/09/21/the-magical-bayesian-method/" title="数学之美番外篇：平凡而又神奇的贝叶斯方法">数学之美番外篇：平凡而又神奇的贝叶斯方法</a> (26)</li><li><a href="http://mindhacks.cn/2008/07/07/the-importance-of-knowing-why/" title="知其所以然（以算法学习为例）">知其所以然（以算法学习为例）</a> (4)</li><li><a href="http://mindhacks.cn/2008/04/18/learning-from-polya/" title="跟波利亚学解题(rev#3)">跟波利亚学解题(rev#3)</a> (7)</li><li><a href="http://mindhacks.cn/2006/10/15/cantor-godel-turing-an-eternal-golden-diagonal/" title="康托尔、哥德尔、图灵&mdash;&mdash;永恒的金色对角线(rev#2)">康托尔、哥德尔、图灵&mdash;&mdash;永恒的金色对角线(rev#2)</a> (10)</li></ul><hr />
<h3>订阅 Mind Hacks</h3>
<a title="用Google Reader订阅" href="http://fusion.google.com/add?feedurl=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_google.gif" style="border:0" alt="订阅到 | Google" /></a> 
<a title="用鲜果订阅" href="http://www.xianguo.com/subscribe.php?url=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_xianguo.gif" style="border:0" alt="订阅到 | 鲜果" /></a>
<br/> 
<a title="用抓虾订阅" href="http://www.zhuaxia.com/add_channel.php?url=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_zhuaxia.gif" style="border:0" alt="订阅到 | 抓虾" /></a> 
<a title="用有道订阅" href="http://reader.youdao.com/b.do?keyfrom=mindhacks&url=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_yodao1.gif" style="border:0" alt="订阅到 | 有道" /></a> 
<hr />
<h3>我是你的信息过滤器</h3>
想了解作者最近在关注什么，欢迎 Follow <a href="https://twitter.com/pongba">pongba@Twitter</a>
<br/>
程序员朋友请到作者发起的 <a href="https://groups.google.com/group/pongba">TopLanguage</a> (<a href="http://mindhacks.cn/about-toplanguage/">about</a>) 社群逛逛，定有收获 :)
<br/>
想了解作者在阅读哪些书，请到 <a href="http://www.douban.com/people/pongba/">pongba@豆瓣</a>，或者直接访问以下四个豆列：<a href="http://www.douban.com/doulist/46003/">[只读经典]思维改变生活</a> | <a href="http://www.douban.com/doulist/127649/">[只读经典]思考的技术与艺术</a> | <a href="http://www.douban.com/doulist/197706/">决策与判断</a> | <a href="http://www.douban.com/doulist/176513/">机器学习与人工智能书籍资源导引</a> 。
<br/>
想了解作者平时在互联网上的阅读，请订阅 <a href="http://delicious.com/pongba/shared-reading">pongba-shared-reading@delicious</a> 。
<hr />
<p><small>
本文由 刘未鹏 发布在 <a href="http://mindhacks.cn">刘未鹏 | Mind Hacks</a>, 2007. | <a href="http://mindhacks.cn/2007/12/02/probability-theory-in-evolution/#commenting">4 条评论</a> | 标签: <a href="http://mindhacks.cn/tags/%e6%95%b0%e5%ad%a6/" rel="tag">数学</a>, <a href="http://mindhacks.cn/tags/%e6%a6%82%e7%8e%87%e8%ae%ba/" rel="tag">概率论</a>, <a href="http://mindhacks.cn/tags/%e7%ae%97%e6%b3%95/" rel="tag">算法</a>
<br/>
转载请注明作者，出处，以及<a href="http://mindhacks.cn/2007/12/02/probability-theory-in-evolution/">原始超链接</a>: http://mindhacks.cn/2007/12/02/probability-theory-in-evolution/
</small></p>]]></content:encoded>
			<wfw:commentRss>http://mindhacks.cn/2007/12/02/probability-theory-in-evolution/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>康托尔、哥德尔、图灵&#8212;&#8212;永恒的金色对角线(rev#2)</title>
		<link>http://mindhacks.cn/2006/10/15/cantor-godel-turing-an-eternal-golden-diagonal/</link>
		<comments>http://mindhacks.cn/2006/10/15/cantor-godel-turing-an-eternal-golden-diagonal/#comments</comments>
		<pubDate>Sun, 15 Oct 2006 11:16:00 +0000</pubDate>
		<dc:creator>刘未鹏</dc:creator>
				<category><![CDATA[数学]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[计算机科学]]></category>

		<guid isPermaLink="false">http://mindhacks.cn/2006/10/15/cantor-godel-turing-an-eternal-golden-diagonal/</guid>
		<description><![CDATA[哥德尔的不完备性定理震撼了20世纪数学界的天空，其数学意义颠覆了希尔伯特的形式化数学的宏伟计划，其哲学意义直到21世纪的今天仍然不断被延伸到各个自然学科，深刻影响着人们的思维。图灵为了解决希尔伯特著名的第十问题而提出有效计算模型，进而作出了可计算理论和现代计算机的奠基性工作，著名的停机问题给出了机械计算模型的能力极限，其深刻的意义和漂亮的证明使它成为可计算理论中的标志性定理之一。丘齐，跟图灵同时代的天才，则从另一个抽象角度提出了lambda算子的思想，与图灵机抽象的倾向于硬件性不同，丘齐的lambda算子理论是从数学的角度进行抽象，不关心运算的机械过程而只关心运算的抽象性质，只用最简洁的几条公理便建立起了与图灵机完全等价的计算模型，其体现出来的数学抽象美开出了函数式编程语言这朵奇葩，Lisp、Scheme、Haskell… 这些以抽象性和简洁美为特点的语言至今仍然活跃在计算机科学界，虽然由于其本质上源于lambda算子理论的抽象方式不符合人的思维习惯从而注定无法成为主流的编程语言[2]，然而这仍然无法妨碍它们成为编程理论乃至计算机学科的最佳教本。而诞生于函数式编程语言的神奇的Y combinator至今仍然让人们陷入深沉的震撼和反思当中…]]></description>
			<content:encoded><![CDATA[<p><i>我看到了它，却不敢相信它</i><sup>[1]</sup><i>。</i><i></i></p>
<p><i>——</i><i>康托尔</i><i></i></p>
<p><i>计算机是数学家一次失败思考的产物。</i><i></i></p>
<p><i>——</i><i>无名氏</i><i></i></p>
<p><a href="http://en.wikipedia.org/wiki/Kurt_Godel">哥德尔</a>的<a href="http://www.answers.com/topic/g-del-s-incompleteness-theorems">不完备性定理</a>震撼了20世纪数学界的天空，其数学意义颠覆了<a href="http://en.wikipedia.org/wiki/David_Hilbert">希尔伯特</a>的形式化数学的宏伟计划，其哲学意义直到21世纪的今天仍然不断被延伸到各个自然学科，深刻影响着人们的思维。<a href="http://www.alanturing.net/">图灵</a>为了解决希尔伯特著名的<a href="http://en.wikipedia.org/wiki/Hilbert's_tenth_problem">第十问题</a>而提出有效计算模型，进而作出了<a href="http://en.wikipedia.org/wiki/Computability_theory_(computation)">可计算理论</a>和现代计算机的奠基性工作，著名的停机问题给出了机械计算模型的能力极限，其深刻的意义和漂亮的证明使它成为可计算理论中的标志性定理之一。<a href="http://en.wikipedia.org/wiki/Alonzo_Church">丘齐</a>，跟图灵同时代的天才，则从另一个抽象角度提出了<a href="http://en.wikipedia.org/wiki/Lambda_calculus">lambda算子</a>的思想，与<a href="http://en.wikipedia.org/wiki/Turing_machine">图灵机</a>抽象的倾向于硬件性不同，丘齐的lambda算子理论是从数学的角度进行抽象，不关心运算的机械过程而只关心运算的抽象性质，只用最简洁的几条公理便建立起了与图灵机<a href="http://en.wikipedia.org/wiki/Turing_machine#Models_equivalent_to_the_Turing_machine_model">完全等价</a>的计算模型，其体现出来的数学抽象美开出了<a href="http://en.wikipedia.org/wiki/Functional_programming">函数式编程语言</a>这朵奇葩，<a href="http://en.wikipedia.org/wiki/Lisp_programming_language">Lisp</a>、<a href="http://en.wikipedia.org/wiki/Scheme_(programming_language)">Scheme</a>、<a href="http://www.haskell.org/">Haskell</a>… 这些以抽象性和简洁美为特点的语言至今仍然活跃在计算机科学界，虽然由于其本质上源于lambda算子理论的抽象方式不符合人的思维习惯从而注定无法成为主流的编程语言<sup>[2]</sup>，然而这仍然无法妨碍它们成为编程理论乃至计算机学科的最佳教本。而诞生于函数式编程语言的神奇的<a href="http://en.wikipedia.org/wiki/Y_combinator">Y combinator</a>至今仍然让人们陷入深沉的震撼和反思当中…</p>
<p>然而，这一切的一切，看似不很相关却又有点相关，认真思考其关系却又有点一头雾水的背后，其实隐隐藏着一条线，这条线把它们从本质上串到了一起，而顺着时光的河流逆流而上，我们将会看到，这条线的尽头，不是别人，正是只手拨开被不严密性问题困扰的19世纪数学界阴沉天空的天才数学家<a href="http://en.wikipedia.org/wiki/Georg_Cantor">康托尔</a>，康托尔创造性地将一一对应和对角线方法运用到无穷集合理论的建立当中，这个被希尔伯特称为“谁也无法将我们从康托尔为我们创造的乐园中驱逐出去”、被罗素称为“19世纪最伟大的智者之一”的人，他在<a href="http://www.amazon.com/Contributions-Founding-Theory-Transfinite-Numbers/dp/0875481574/ref=sr_1_4/103-3576835-0834231?ie=UTF8&amp;s=books&amp;qid=1177237889&amp;sr=8-4">集合论方面的工作</a>终于驱散了不严密性问题带来的阴霾，仿佛一道金色的阳光刺破乌云，19世纪的数学终于看到了真正严格化的曙光，数学终于得以站在了前所未有的坚固的基础之上；集合论至今仍是数学里最基础和最重要的理论之一。而康托尔当初在研究无穷集合时最具天才的方法之一——对角线方法——则带来了极其深远的影响，其纯粹而直指事物本质的思想如洪钟大吕般响彻数学和哲学的每一个角落<sup>[3]</sup>。随着本文的展开，你将会看到，刚才提到的一切，歌德尔的不完备性定理，图灵的停机问题，lambda算子理论中神奇的Y combinator、乃至著名的罗素悖论、理发师悖论等等，其实都源自这个简洁、纯粹而同时又是最优美的数学方法，反过来说，从康托尔的对角线方法出发，我们可以轻而易举地推导出哥德尔的不完备性定理，而由后者又可以轻易导出停机问题和Y combinator，实际上，我们将会看到，后两者也可以直接由康托尔的对角线方法导出。尤其是Y combinator，这个形式上绕来绕去，本质上捉摸不透，看上去神秘莫测的算子，其实只是一个非常自然而然的推论，如果从哥德尔的不完备性定理出发，它甚至比停机问题还要来得直接简单。总之，你将会看到这些看似深奥的理论是如何由一个至为简单而又至为深刻的数学方法得出的，你将会看到最纯粹的数学美。</p>
<p><b>图灵的停机问题</b><b>(The Halting Problem)</b></p>
<p><i>了解停机问题的可以直接跳过这一节，到下一节“Y Combinator”，了解后者的再跳到下一节“哥德尔的不完备性定理”</i></p>
<p>我们还是从图灵著名的停机问题说起，一来它相对来说是我们要说的几个定理当中最简单的，二来它也最贴近程序员。实际上，我以前曾写过<a href="http://blog.csdn.net/pongba/archive/2006/03/11/621723.aspx">一篇关于图灵机的文章</a>，有兴趣的读者可以从那篇开始，那篇主要是从理论上阐述，所以这里我们打算避开抽象的理论，换一种符合程序员思维习惯的直观方式来加以解释。</p>
<p><b>停机问题</b><b></b></p>
<p><i>不存在这样一个程序（算法），它能够计算任何程序（算法）在给定输入上是否会结束（停机）。</i><i></i></p>
<p>那么，如何来证明这个停机问题呢？反证。假设我们某一天真做出了这么一个极度聪明的万能算法（就叫God_algo吧），你只要给它一段程序（二进制描述），再给它这段程序的输入，它就能告诉你这段程序在这个输入上会不会结束（停机），我们来编写一下我们的这个算法吧：</p>
<p>bool God_algo(char* program, char* input)</p>
<p>{</p>
<p>if(<i>&lt;program</i>&gt; <i>halts on </i>&lt;<i>input</i>&gt;)</p>
<p>return true;</p>
<p>return false;</p>
<p>}</p>
<p>这里我们假设if的判断语句里面是你天才思考的结晶，它能够像上帝一样洞察一切程序的宿命。现在，我们从这个God_algo出发导出一个新的算法：</p>
<p>bool Satan_algo(char* program)</p>
<p>{</p>
<p>if( <i>God_algo</i>(program, program) ){</p>
<p>while(1); // loop forever!</p>
<p>return false; // can never get here!</p>
<p>}</p>
<p>else</p>
<p>return true;</p>
<p>}</p>
<p>正如它的名字所暗示的那样，这个算法便是一切邪恶的根源了。当我们把这个算法运用到它自身身上时，会发生什么呢？</p>
<p>Satan_algo(Satan_algo);</p>
<p>我们来分析一下这行简单的调用：</p>
<p>显然，Satan_algo(Satan_algo)这个调用要么能够运行结束返回（停机），要么不能返回（loop forever）。</p>
<p><i>如果它能够结束</i>，那么Santa_algo算法里面的那个if判断就会成立（因为God_algo(Santa_algo,Santa_algo)将会返回true），从而程序便进入那个包含一个无穷循环while(1);的if分支，于是这个Satan_algo(Satan_algo)调用<i>便永远不会返回（结束）了</i>。</p>
<p>而<i>如果</i><i>Satan_algo(Satan_algo)</i><i>不能结束（停机）呢</i>，则if判断就会失败，从而选择另一个if分支并返回true，即<i>Satan_algo(Satan_algo)</i><i>又</i><i>能够返回（停机）</i>。</p>
<p>总之，我们有：</p>
<p><i>Satan_algo(Satan_algo)</i><i>能够停机</i><i> </i><i>=&gt; </i><i>它不能停机</i><i></i></p>
<p><i>Satan_algo(Satan_algo)</i><i>不能停机</i><i> </i><i>=&gt; </i><i>它能够停机</i><i></i></p>
<p>所以它停也不是，不停也不是。左右矛盾。</p>
<p>于是，我们的假设，即God_algo算法的存在性，便不成立了。正如<a href="http://en.wikipedia.org/wiki/Joseph_Louis_Lagrange">拉格朗日</a>所说：“陛下，我们不需要（上帝）这个假设”<sup>[4]</sup>。</p>
<p>这个证明相信每个程序员都能够容易的看懂。然而，这个看似不可捉摸的技巧背后其实隐藏着深刻的数学原理（甚至是哲学原理）。在没有认识到这一数学原理之前，至少我当时是对于图灵如何想出这一绝妙证明感到无法理解。但后面，在介绍完了与图灵的停机问题“同构”的Y combinator之后，我们会深入哥德尔的不完备性定理，在理解了哥德尔不完备性定理之后，我们从这一同样绝妙的定理出发，就会突然发现，离停机问题和神奇的Y combinator只是咫尺之遥而已。当然，最后我们会回溯到一切的尽头，康托尔那里，看看停机问题、Y combinator、以及不完备性定理是如何自然而然地由康托尔的对角线方法推导出来的，我们将会看到这些看似神奇的构造性证明的背后，其实是一个简洁优美的数学方法在起作用。</p>
<p><b>Y Combinator</b></p>
<p><i>了解</i><i>Y combinator</i><i>的请直接跳过这一节，到下一节</i><i>“</i><i>哥德尔的不完备性定理</i><i>”</i><i>。</i><i></i></p>
<p>让我们暂且搁下但记住绕人的图灵停机问题，走进函数式编程语言的世界，走进由跟图灵机理论等价的lambda算子发展出来的另一个平行的语言世界。让我们来看一看被人们一代一代吟唱着的神奇的Y Combinator…</p>
<p>关于Y Combinator的文章可谓数不胜数，这个由师从希尔伯特的著名逻辑学家<a href="http://en.wikipedia.org/wiki/Haskell_Curry">Haskell B.Curry</a>（Haskell语言就是以他命名的，而函数式编程语言里面的Curry手法也是以他命名）“发明”出来的组合算子（Haskell是研究<a href="http://en.wikipedia.org/wiki/Combinatory_logic">组合逻辑(combinatory logic)</a>的）仿佛有种神奇的魔力，它能够算出给定lambda表达式（函数）的<a href="http://en.wikipedia.org/wiki/Fixed_point">不动点</a>。从而使得递归成为可能。事实上，我们待会就会看到，Y Combinator在神奇的表面之下，其实隐藏着深刻的意义，其背后体现的意义，曾经开出过历史上最灿烂的数学之花，所以MIT的计算机科学系将它做成系徽也就不足为奇了<sup>[5]</sup>。</p>
<p>当然，要了解这个神奇的算子，我们需要一点点lambda算子理论的基础知识，不过别担心，lambda算子理论是我目前见过的最简洁的公理系统，这个系统仅仅由三条非常简单的公理构成，而这三条公理里面我们又只需要关注前两条。</p>
<p><i>以下小节</i><i>——lambda calculus——</i><i>纯粹是为了没有接触过</i><i>lambda</i><i>算子理论的读者准备的，并不属于本文重点讨论的东西，然而要讨论</i><i>Y combinator</i><i>就必须先了解一下</i><i>lambda</i><i>（当然，以编程语言来了解也行，但是你会看到，丘齐最初提出的</i><i>lambda</i><i>算子理论才是最最简洁和漂亮的，学起来也最省事。）所以我单独准备了一个小节来介绍它。如果你已经知道，可以跳过这一小节。不知道的读者也可以跳过这一小节去</i><i>wikipedia</i><i>上面看，这里的介绍使用了</i><i>wikipedia</i><i>上的方式</i><i></i></p>
<p><b>lambda calculus</b></p>
<p>先来看一下lambda表达式的基本语法(BNF)：</p>
<p>&lt;expr&gt; ::= &lt;identifier&gt;</p>
<p>&lt;expr&gt; ::= <i>lambda</i> &lt;identifier-list&gt;. &lt;expr&gt;</p>
<p>&lt;expr&gt; ::= (&lt;expr&gt; &lt;expr&gt;)</p>
<p>前两条语法用于生成lambda表达式（lambda函数），如：</p>
<p><i>lambda</i> x y. x + y</p>
<p><i>haskell</i><i>里面为了简洁起见用</i><i>“\”</i><i>来代替希腊字母</i><i>lambda</i><i>，它们形状比较相似。故而上面的定义也可以写成：</i><i></i></p>
<p><i>\ x y. x + y</i></p>
<p>这是一个匿名的加法函数，它接受两个参数，返回两值相加的结果。当然，这里我们为了方便起见赋予了lambda函数直观的计算意义，而实际上lambda calculus里面一切都只不过是文本替换，有点像C语言的宏。并且这里的“+”我们假设已经是一个具有原子语义的运算符<sup>[6]</sup>，此外，为了方便我们使用了中缀表达（按照lambda calculus系统的语法实际上应该写成“(+ x y)”才对——参考第三条语法）。</p>
<p>那么，函数定义出来了，怎么使用呢？最后一条规则就是用来调用一个lambda函数的：</p>
<p>((<i>lambda</i> x y. x + y) 2 3)</p>
<p>以上这一行就是把刚才定义的加法函数运用到2和3上（这个调用语法形式跟<a href="http://en.wikipedia.org/wiki/Imperative_programming">命令式语言(imperative language)</a>惯用的调用形式有点区别，后者是“f(x, y)”，而这里是“(f x y)”，不过好在顺序没变:) ）。为了表达简洁一点，我们可以给(<i>lambda</i> x y. x + y)起一个名字，像这样：</p>
<p>let Add = (<i>lambda</i> x y. x + y)</p>
<p>这样我们便可以使用Add来表示该lambda函数了：</p>
<p>(Add 2 3)</p>
<p>不过还是为了方便起见，后面调用的时候一般用“Add(2, 3)”，即我们熟悉的形式。</p>
<p>有了语法规则之后，我们便可以看一看这个语言系统的两条简单至极的公理了：</p>
<p><i>Alpha</i><i>转换公理</i>：例如，“lambda x y. x + y”转换为“lambda a b. a + b”。换句话说，函数的参数起什么名字没有关系，可以随意替换，只要函数体里面对参数的使用的地方也同时注意相应替换掉就是了。</p>
<p><i>Beta</i><i>转换公理</i>：例如，“(lambda x y. x + y) 2 3”转换为“2 + 3”。这个就更简单了，也就是说，当把一个lambda函数用到参数身上时，只需用实际的参数来替换掉其函数体中的相应变量即可。</p>
<p>就这些。是不是感觉有点太简单了？但事实就是如此，lambda算子系统从根本上其实就这些东西，然而你却能够从这几个简单的规则中推演出神奇无比的Y combinator来。我们这就开始！</p>
<p><b>递归的迷思</b><b></b></p>
<p>敏锐的你可能会发现，就以上这两条公理，我们的lambda语言中无法表示递归函数，为什么呢？假设我们要计算经典的阶乘，递归描述肯定像这样：</p>
<p>f(n):</p>
<p>if n == 0 return 1</p>
<p>return n*f(n-1)</p>
<p>当然，上面这个程序是假定n为正整数。这个程序显示了一个特点，f在定义的过程中用到了它自身。那么如何在lambda算子系统中表达这一函数呢？理所当然的想法如下：</p>
<p><i>lambda</i> n. If_Else n==0 1 n*&lt;<i>self</i>&gt;(n-1)</p>
<p>当然，上面的程序假定了If_Else是一个已经定义好的三元操作符（你可以想象C的“?:”操作符，后面跟的三个参数分别是判断条件、成功后求值的表达式、失败后求值的表达式。那么很显然，这个定义里面有一个地方没法解决，那就是&lt;<i>self</i>&gt;那个地方我们应该填入什么呢？很显然，熟悉C这类命令式语言的人都知道应该填入这个函数本身的名字，然而lambda算子系统里面的lambda表达式（或称函数）是没有名字的。</p>
<p>怎么办？难道就没有办法实现递归了？或者说，丘齐做出的这个lambda算子系统里面根本没法实现递归从而在计算能力上面有重大的缺陷？显然不是。马上你就会看到Y combinator是如何把一个看上去非递归的lambda表达式像变魔术那样变成一个递归版本的。在成功之前我们再失败一次，注意下面的尝试：</p>
<p>let F = <i>lambda</i> n. IF_Else n==0 1 n*F(n-1)</p>
<p>看上去不错，是吗？可惜还是不行。因为let F只是起到一个<a href="http://en.wikipedia.org/wiki/Syntactic_sugar">语法糖</a>的作用，在它所代表的lambda表达式还没有完全定义出来之前你是不可以使用F这个名字的。更何况实际上丘齐当初的lambda算子系统里面也并没有这个语法元素，这只是刚才为了简化代码而引入的语法糖。当然，了解这个let语句还是有意义的，后面还会用到。</p>
<p><b>一次成功的尝试</b><b></b></p>
<p>在上面几次失败的尝试之后，我们是不是就一筹莫展了呢？别忘了软件工程里面的一条黄金定律：“任何问题都可以通过增加一个间接层来解决”。不妨把它沿用到我们面临的递归问题上：没错，我们的确没办法在一个lambda函数的定义里面直接（按名字）来调用其自身。但是，可不可以间接调用呢？</p>
<p>我们回顾一下刚才不成功的定义：</p>
<p><i>lambda</i> n. If_Else n==0 1 n*&lt;<i>self</i>&gt;(n-1)</p>
<p>现在&lt;self&gt;处不是缺少“这个函数自身”嘛，既然不能直接填入“这个函数自身”，我们可以增加一个参数，也就是说，把&lt;self&gt;参数化：</p>
<p><i>lambda</i> <b>self</b> n. If_Else n==0 1 n*<b>self</b>(n-1)</p>
<p>上面这个lambda算子总是合法定义了吧。现在，我们调用这个函数的时候，只要加传一个参数self，这个参数不是别人，正是这个函数自身。还是为了简单起见，我们用let语句来给上面这个函数起个别名：</p>
<p>let P = <i>lambda</i> self n. If_Else n==0 1 n*self(n-1)</p>
<p>我们这样调用，比如说我们要计算3的阶乘：</p>
<p><b>P</b>(<b>P</b>, 3)</p>
<p>也就是说，把P自己作为P的第一个参数（注意，调用的时候P已经定义完毕了，所以我们当然可以使用它的名字了）。这样一来，P里面的self处不就等于是P本身了吗？自身调用自身，递归！</p>
<p>可惜这只是个美好的设想，还差一点点。我们分析一下P(P, 3)这个调用。利用前面讲的Beta转换规则，这个函数调用展开其实就是（你可以完全把P当成一个宏来进行展开！）：</p>
<p>IF_Else n==0 1 n*<b>P</b>(n-1)</p>
<p>看出问题了吗？这里的<b>P</b>(n-1)虽然调用到了P，然而只给出了一个参数；而从P的定义来看，它是需要两个参数的（分别为<b>self</b>和n）！也就是说，为了让<b>P</b>(n-1)变成良好的调用，我们得加一个参数才行，所以我们得稍微修改一下P的定义：</p>
<p>let P = <i>lambda</i> <b>self</b> n. If_Else n==0 1 n*<b>self</b>(<b>self</b>, n-1)</p>
<p>请注意，我们在P的函数体内调用self的时候增加了一个参数。现在当我们调用P(P, 3)的时候，展开就变成了：</p>
<p>IF_Else 3==0 1 3*<b>P</b>(<b>P</b>, 3-1)</p>
<p>而<b>P</b>(<b>P</b>, 3-1)是对P合法的递归调用。这次我们真的成功了！</p>
<p><b>不动点原理</b><b></b></p>
<p>然而，看看我们的P的定义，是不是很丑陋？“n*<b>self</b>(<b>self</b>, n-1)”？什么玩意？为什么要多出一个多余的self？<a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a>！怎么办呢？我们想起我们一开始定义的那个失败的P，虽然行不通，但最初的努力往往是大脑最先想到的最直观的做法，我们来回顾一下：</p>
<p>let P = <i>lambda</i> self n. If_Else n==0 1 n*<b>self</b>(n-1)</p>
<p>这个P的函数体就非常清晰，没有冗余成分，虽然参数列表里面多出一个self，但我们其实根本不用管它，看函数体就行了，self这个名字已经可以说明一切了对不对？但很可惜这个函数不能用。我们再来回想一下为什么不能用呢？因为当你调用P(P, n)的时候，里面的self(n-1)会展开为P(n-1)而P是需要两个参数的。唉，要是这里的self是一个“真正”的，只需要一个参数的递归阶乘函数，那该多好啊。为什么不呢？干脆我们假设出一个“真正”的递归阶乘函数：</p>
<p>power(n):</p>
<p>if(n==0) return 1;</p>
<p>return n*power(n-1);</p>
<p>但是，前面不是说过了，这个理想的版本无法在lambda算子系统中定义出来吗（由于lambda函数都是没名字的，无法自己内部调用自己）？不急，我们并不需要它被定义出来，我们只需要在头脑中“假设”它以“某种”方式被定义出来了，现在我们把这个真正完美的power传给P，这样：</p>
<p>P(<b>power</b>, 3) </p>
<p>注意它跟P(P, 3)的不同，P(P, 3)我们传递的是一个有缺陷的P为参数。而P(power, 3)我们则是传递的一个真正的递归函数power。我们试着展开P(power, 3):</p>
<p>IF_Else 3==0 1 3*<b>power</b>(3-1)</p>
<p>发生了什么？？power(3-1)将会计算出2的阶乘（别忘了，power是我们设想的完美递归函数），所以这个式子将会忠实地计算出3的阶乘！</p>
<p>回想一下我们是怎么完成这项任务的：我们设想了一个以某种方式构造出来的完美的能够内部自己调用自己的递归阶乘函数power，我们发现把这个power传给P的话，P(power, n)的展开式就是真正的递归计算n阶乘的代码了。</p>
<p>你可能要说：废话！都有了power了我们还要费那事把它传给P来个P(power, n)干嘛？直接power(n)不就得了？! 别急，之所以设想出这个power只是为了引入不动点的概念，而不动点的概念将会带领我们发现Y combinator。</p>
<p>什么是不动点？一点都不神秘。让我们考虑刚才的power与P之间的关系。一个是真正可递归的函数，一个呢，则是以一个额外的self参数来试图实现递归的伪递归函数，我们已经看到了把power交给P为参数发生了什么，对吧？不，似乎还没有，我们只是看到了，“把power加上一个n一起交给P为参数”能够实现真正的递归。现在我们想考虑power跟P之间的关系，直接把power交给P如何？</p>
<p>P(power)</p>
<p>这是什么？这叫函数的<a href="http://en.wikipedia.org/wiki/Partial_evaluation"><i>部分求值</i>(<i>partial evaluation</i>)</a>。换句话说，第一个参数是给出来了，但第二个参数还悬在那里，等待给出。那么，光给一个参数得到的是什么呢？是“还剩一个参数待给的一个新的函数”。其实也很简单，只要按照Beta转换规则做就是了，把P的函数体里面的self出现处皆替换为power就可以了。我们得到：</p>
<p>IF_Else n==0 1 n*power(n-1)</p>
<p>当然，这个式子里面还有一个变量没有绑定，那就是n，所以这个式子还不能求值，你需要给它一个n才能具体求值，对吧。这么说，这可不就是一个以n为参数的函数么？实际上就是的。在lambda算子系统里面，如果给一个lambda函数的参数不足，则得到的就是一个新的lambda函数，这个新的lambda函数所接受的参数也就是你尚未给出的那些参数。换句话来说，调用一个lambda函数可以分若干步来进行，每次只给出一部分参数，而只有等所有参数都给齐了，函数的求值结果才能出来，否则你得到的就是一个“中间函数”。</p>
<p>那么，这跟不动点定理有什么关系？关系大了，刚才不是说了，P(power)返回的是一个新的“中间函数”嘛？这个“中间函数”的函数体我们刚才已经看到了，就是简单地展开P(power)而已，回顾一遍：</p>
<p>IF_Else n==0 1 n*power(n-1)</p>
<p>我们已经知道，这是个函数，参数n待定。因此我们不妨给它加上一个“lambda n”的帽子，这样好看一点：</p>
<p><i>lambda</i> n. IF_Else n==0 1 n*power(n-1)</p>
<p>这是什么呢？这可不就是power本身的定义？（当然，如果我们能够定义power的话）。不信我们看看power如果能够定义出来像什么样子：</p>
<p>let power = <i>lambda</i> n. IF_Else n==0 1 n*power(n-1)</p>
<p>一模一样！也就是说，P(power)展开后跟power是一样的。即：</p>
<p><b>P(power) = power</b></p>
<p>以上就是所谓的<i>不动点</i>。即对于函数P来说power是这样一个“点”：当把P用到power身上的时候，得到的结果仍然还是power，也就是说，power这个“点”在P的作用下是“不动”的。</p>
<p>可惜的是，这一切居然都是建立在一个不存在的power的基础上的，又有什么用呢？可别过早提“不存在”这个词，你觉得一样东西不存在或许只是你没有找到使它存在的正确方法。我们已经看到power是跟P有着密切联系的。密切到什么程度呢？对于伪递归的P，存在一个power，满足P(power)=power。注意，这里所说的“伪递归”的P，是指这样的形式：</p>
<p>let P = <i>lambda</i> self n. If_Else n==0 1 n*<b>self</b>(n-1) // 注意，不是self(self,n-1)</p>
<p>一般化的描述就是，对任一伪递归F（回想一下伪递归的F如何得到——是我们为了解决lambda函数不能引用自身的问题，于是给理想的f加一个self参数从而得到的），必存在一个理想f（F就是从这个理想f演变而来的），满足F(f) = f。</p>
<p>那么，现在的问题就归结为如何针对F找到它的f了。根据F和f之间的密切联系（F就比f多出一个self参数而已），我们可以从F得出f吗？假设我们可以（又是假设），也就是说假设我们找到了一根魔棒，把它朝任意一个伪递归的F一挥，眼前一花，它就变成了真正的f了。这根魔棒如果存在的话，它具有什么性质？我们假设这个神奇的函数叫做Y，把Y用到任何伪递归的函数F上就能够得到真正的f，也就是说：</p>
<p>Y(F) = f</p>
<p>结合上面的F(f) = f，我们得到：</p>
<p>Y(F) = f = F(f) = F(Y(F))</p>
<p>也就是说，Y具有性质：</p>
<p><b>Y(F) = F(Y(F))</b></p>
<p>性质倒是找出来了，怎么构造出这个Y却又成了难题。一个办法就是使用抽象法，这是从工程学的思想的角度，也就是通过不断迭代、重构，最终找到问题的解。然而对于这里的Y combinator，接近问题解的过程却显得复杂而费力，甚至过程中的有些点上的思维跳跃有点如羚羊挂角无迹可寻。然而，在这整个Y combinator介绍完了之后我们将会介绍著名的哥德尔不完备性定理，然后我们就会发现，通过哥德尔不完备性定理证明中的一个核心构造式，只需一步自然的推导就能得出我们的Y combinator。而且，最美妙的是，还可以再往下归约，把一切都归约到康托尔当初提出的对角线方法，到那时我们就会发现原来同样如羚羊挂角般的哥德尔的证明其实是对角线方法的一个自然推论。数学竟是如此奇妙，我们由简单得无法再简单的lambda calculus系统的两条公理居然能够导出如此复杂如此令人目眩神迷的Y Combinator，而这些复杂性其实也只是荡漾在定理海洋中的涟漪，拨开复杂性的迷雾我们重又发现它们居然寓于极度的简洁之中。这就是数学之美。</p>
<p>让我们先来看一看Y combinator的费力而复杂的工程学构造法，我会尽量让这个过程显得自然而流畅<sup>[7]</sup>：</p>
<p>我们再次回顾一下那个伪递归的求阶乘函数：</p>
<p>let P = <i>lambda</i> self n. If_Else n==0 1 n*<b>self</b>(n-1)</p>
<p>我们的目标是找出P的不动点power，根据不动点的性质，只要把power传给P，即P(power)，便能够得到真正的递归函数了。</p>
<p>现在，关键的地方到了，由于：</p>
<p>power = P(power) // 不动点原理</p>
<p>这就意味着，power作为一个函数（lambda calculus里面一切都是函数），它是自己调用了自己的。那么，我们如何实现这样一个能够自己调用自己的power呢？回顾我们当初成功的一次尝试，要实现递归，我们是通过增加一个间接层来进行的：</p>
<p>let power_gen = <i>lambda</i> self. P(<b>self</b>(<b>self</b>))</p>
<p>还记得<b>self</b>(<b>self</b>)这个形式吗？我们在成功实现出求阶乘递归函数的时候不就是这么做的？那么对于现在这个power_gen，怎么递归调用？</p>
<p>power_gen(power_gen)</p>
<p>不明白的话可以回顾一下前面我们调用P(P, n)的地方。这里power_gen(power_gen)展开后得到的是什么呢？我们根据刚才power_gen的定义展开看一看，原来是：</p>
<p><b>P</b>(power_gen(power_gen))</p>
<p>看到了吗？也就是说：</p>
<p>power_gen(power_gen) =&gt; <b>P</b>(power_gen(power_gen))</p>
<p><b></b></p>
<p>现在，我们把power_gen(power_gen)当成整体看，不妨令为power，就看得更清楚了：</p>
<p>power =&gt; <b>P</b>(power)</p>
<p>这不正是我们要的答案么？</p>
<p>OK，我们<i>总结一下</i>：对于给定的P，只要构造出一个相应的power_gen如下：</p>
<p>let power_gen = <i>lambda</i> self. P(<b>self</b>(<b>self</b>))</p>
<p>我们就会发现，power_gen(power_gen)这个调用展开后正是P(power_gen(power_gen))。也就是说，我们的power_gen(power_gen)就是我们苦苦寻找的不动点了！</p>
<p><b>铸造</b><b>Y Combinator</b></p>
<p>现在我们终于可以铸造我们的Y Combinator了，Y Combinator只要生成一个形如power_gen的lambda函数然后把它应用到自身，就大功告成：</p>
<p>let <b>Y</b> = <i>lambda</i> F.</p>
<p>let <b><i>f_gen</i></b> = <i>lambda</i> self. F(<b>self</b>(<b>self</b>))</p>
<p>return <b>f_gen</b>(<b>f_gen</b>)</p>
<p>稍微解释一下，Y是一个lambda函数，它接受一个伪递归F，在内部生成一个f_gen（还记得我们刚才看到的power_gen吧），然后把f_gen应用到它自身（记得power_gen(power_gen)吧），得到的这个f_gen(f_gen)也就是F的不动点了（因为f_gen(f_gen) = F(f_gen(f_gen))），而根据不动点的性质，F的不动点也就是那个对应于F的真正的递归函数！</p>
<p>如果你还觉得不相信，我们稍微展开一下看看，还是拿阶乘函数说事，首先我们定义阶乘函数的伪递归版本：</p>
<p>let Pwr = <i>lambda</i> self n. If_Else n==0 1 n*self(n-1)</p>
<p>让我们把这个Pwr交给<b>Y</b>，看会发生什么（根据刚才Y的定义展开吧）：</p>
<p>Y(Pwr) =&gt;</p>
<p>let f_gen = <i>lambda</i> self. <b>Pwr</b>(self(self))</p>
<p>return f_gen(f_gen)</p>
<p>Y(Pwr)的求值结果就是里面返回的那个f_gen(f_gen)，我们再根据f_gen的定义展开f_gen(f_gen)，得到：</p>
<p>Pwr(f_gen(f_gen))</p>
<p>也就是说：</p>
<p>Y(Pwr) =&gt; f_gen(f_gen) =&gt; Pwr(f_gen(f_gen))</p>
<p>我们来看看得到的这个Pwr(f_gen(f_gen))到底是不是真有递归的魔力。我们展开它（注意，因为Pwr需要两个参数，而我们这里只给出了一个，所以Pwr(f_gen(f_gen))得到的是一个单参（即n）的函数）：</p>
<p>Pwr(<b>f_gen</b>(<b>f_gen</b>)) =&gt; If_Else n==0 1 n*<b>f_gen</b>(<b>f_gen</b>) (n-1)</p>
<p>而里面的那个<b>f_gen</b>(<b>f_gen</b>)，根据f_gen的定义，又会展开为Pwr(f_gen(f_gen))，所以：</p>
<p><b>Pwr(f_gen(f_gen))</b> =&gt; If_Else n==0 1 n* <b>Pwr(f_gen(f_gen))</b> (n-1)</p>
<p>看到加粗的部分了吗？因为<b>Pwr(f_gen(f_gen))</b>是一个接受n为参数的函数，所以不妨把它令成f（f的参数是n），这样上面的式子就是：</p>
<p><b>f</b> =&gt; If_Else n==0 1 n*<b>f</b>(n-1)</p>
<p>完美的阶乘函数！</p>
<p><b>哥德尔的不完备性定理</b><b></b></p>
<p><i>了解哥德尔不完备性定理的可以跳到下一节，</i><i>“</i><i>大道至简</i><i>——</i><i>康托尔的天才</i><i>”</i></p>
<p>然而，漫长的Y Combinator征途仍然并非本文的最终目的，对于Y combinator的构造和解释，只是给不了解lambda calculus或Y combinator的读者看的。关键是马上你会看到Y combinator可以由哥德尔不完备性定理证明的一个核心构造式一眼瞧出来！</p>
<p>让我们的思绪回到1931年，那个数学界风起云涌的年代，一个名不经传的20出头的学生，在他的博士论文中证明了一个惊天动地的结论。</p>
<p>在那个年代，希尔伯特的数学天才就像太阳的光芒一般夺目，在关于数学严格化的大纷争中希尔伯特带领的形式主义派系技压群雄，得到许多当时有名望的数学家的支持。希尔伯特希望借助于形式化的手段，抽掉数学证明中的意义，把数学证明抽象成一堆无意义的符号转换，就连我们人类赖以自豪的逻辑推导，也不过只是一堆堆符号转换而已（想起lambda calculus系统了吧：）)。这样一来，一个我们日常所谓的，带有直观意义和解释的数学系统就变成了一个纯粹由无意义符号表达的、公理加上推导规则所构成的形式系统，而数学证明呢，只不过是在这个系统内玩的一个文字游戏。令人惊讶的是，这样一种做法，真的是可行的！数学的意义，似乎竟然真的可以被抽掉！另一方面，一个形式系统具有非常好的性质，平时人们证明一个定理所动用的推导，变成了纯粹机械的符号变换。希尔伯特希望能够证明，在任一个无矛盾的形式系统中所能表达的所有陈述都要么能够证明要么能够证伪。这看起来是个非常直观的结论，因为一个结论要么是真要么是假，而它在它所处的领域/系统中当然应该能够证明或证伪了（只要我们能够揭示出该系统中足够多的真理）。</p>
<p>然而，哥德尔的证明无情的击碎了这一企图，哥德尔的证明揭示出，任何足够强到蕴含了皮亚诺算术系统（PA）的一致（即无矛盾）的系统都是不完备的，所谓不完备也就是说在系统内存在一个为真但无法在系统内推导出的命题。这在当时的数学界揭起了轩然大波，其证明不仅具有数学意义，而且蕴含了深刻的哲学意义。从那时起这一不完备性定理就被引申到自然科学乃至人文科学的各个角落…至今还没有任何一个数学定理居然能够产生这么广泛而深远的影响。</p>
<p>哥德尔的证明非常的长，达到了200多页纸，但其中很大的成分是用在了一些辅助性的工作上面，比如占据超过1/3纸张的是关于一个形式系统如何映射到自然数，也就是说，如何把一个形式系统中的所有公式都表示为自然数，并可以从一自然数反过来得出相应的公式。这其实就是编码，在我们现在看来是很显然的，因为一个程序就可以被编码成二进制数，反过来也可以解码。但是在当时这是一个全新的思想，也是最关键的辅助性工作之一，另一方面，这正是“程序即数据”的最初想法。</p>
<p>现在我们知道，要证明哥德尔的不完备性定理，只需在假定的形式系统T内表达出一个为真但无法在T内推导出（证明）的命题。于是哥德尔构造了这样一个命题，用自然语言表达就是：命题P说的是“<i>P</i><i>不可在系统</i><i>T</i><i>内证明</i>”（这里的系统T当然就是我们的命题P所处的形式系统了），也就是说“<i>我不可以被证明</i>”，跟著名的说谎者悖论非常相似，只是把“说谎”改成了“不可以被证明”。我们注意到，一旦这个命题能够在T内表达出来，我们就可以得出“P为真但无法在T内推导出来”的结论，从而证明T的不完备性。为什么呢？我们假设T可以证明出P，而因为P说的就是P不可在系统T内证明，于是我们又得到T无法证明出P，矛盾产生，说明我们的假设“T可以证明P”是错误的，根据排中律，我们得到T不可以证明P，而由于P说的正是“T不可证明P”，所以P就成了一个正确的命题，同时无法由T内证明！</p>
<p>如果你足够敏锐，你会发现上面这番推理本身不就是证明吗？其证明的结果不就是P是正确的？然而实际上这番证明是位于T系统之外的，它用到了一个关于T系统的假设“T是一致（无矛盾）的”，这个假设并非T系统里面的内容，所以我们刚才其实是在T系统<i>之外</i>推导出了P是正确的，这跟P不能在T<i>之</i><i>内</i>推导出来并不矛盾。所以别担心，一切都正常。</p>
<p>那么，剩下来最关键的问题就是如何用形式语言在T内表达出这个P，上面的理论虽然漂亮，但若是P根本没法在T内表达出来，我们又如何能证明“T内存在这个为真但无法被证明的P”呢？那一切还不是白搭？</p>
<p>于是，就有了哥德尔证明里面最核心的构造，哥德尔构造了这样一个公式：</p>
<p><b>N(n) is unprovable in T</b></p>
<p>这个公式由两部分构成，n是这个公式的自由变量，它是一个自然数，一旦给定，那么这个公式就变成一个明确的命题。而N则是从n解码出的货真价实的（即我们常见的符号形式的）公式（记得哥德尔的证明第一部分就是把公式编码吗？）。”is unprovable in T”则是一个谓词，这里我们没有用形式语言而是用自然语言表达出来的，但哥德尔证明了它是可以用形式语言表达出来的，大致思路就是：一个形式系统中的符号数目是有限的，它们构成这个形式系统的符号表。于是，我们可以依次枚举出所有长度为1的串，长度为2的串，长度为3的串… 此外根据形式系统给出的语法规则，我们可以检查每个串是否是良构的公式（well formed formula，简称wff，其实也就是说，是否符合语法规则，前面我们在介绍lambda calculus的时候看到了，一个形式系统是需要语法规则的，比如逻辑语言形式化之后我们就会看到P-&gt;Q是一个wff，而-&gt;PQ则不是），因而我们就可以枚举出所有的wff来。最关键的是，我们观察到形式系统中的证明也不过就是由一个个的wff构成的序列（想想推导的过程，不就是一个公式接一个公式嘛）。而wff构成的序列本身同样也是由符号表内的符号构成的串。所以我们只需枚举所有的串，对每一个串检查它是否是一个由wff构成的序列（证明），如果是，则记录下这个wff序列（证明）的最后一个wff，也就是它的结论。这样我们便枚举出了所有的可由T推导出的定理。然后为了表达出”X is unprovable in T”，本质上我们只需说“不存在这样一个自然数S，它所解码出来的wff序列以X为终结”！这也就是说，我们表达出了“is unprovable in T”这个谓词。</p>
<p>我们用UnPr(X)来表达“X is unprovable in T”，于是哥德尔的公式变成了：</p>
<p>UnPr( N(n) )</p>
<p>现在，到了最关键的部分，首先我们把这个公式简记为G(n)——别忘了G内有一个自由变量n，所以G现在还不是一个命题，而只是一个公式，所以谈不上真假：</p>
<p>G(n): UnPr( N(n) )</p>
<p>又由于G也是个wff，所以它也有自己的编码g，当然g是一个自然数，现在我们把g作为G的参数，也就是说，把G里面的自由变量n替换为g，我们于是得到一个真正的命题：</p>
<p>G(g): UnPr( G(g) )</p>
<p>用自然语言来说，这个命题G(g)说的就是“<i>我是不可在</i><i>T</i><i>内证明的</i>”。看，我们在形式系统T内表达出了“我是不可在T内证明的”这个命题。而我们一开始已经讲过了如何用这个命题来推断出G(g)为真但无法在T内证明，于是这就证明了哥德尔的不完备性定理<sup>[8]</sup>。</p>
<p>哥德尔的不完备性定理被称为20世纪数学最重大的发现（不知道有没有“之一”:) ）现在我们知道为真但无法在系统内证明的命题不仅仅是这个诡异的“哥德尔命题”，还有很多真正有意义的明确命题，其中最著名的就是<a href="http://en.wikipedia.org/wiki/Continuum_hypothesis">连续统假设</a>，此外哥德巴赫猜想也有可能是个没法在数论系统中证明的真命题。</p>
<p><b>从哥德尔公式到</b><b>Y Combinator</b></p>
<p>哥德尔的不完备性定理证明了数学是一个未完结的学科，永远有需要我们以人的头脑从系统之外去用我们独有的直觉发现的东西。罗杰·彭罗斯在<a href="http://www.amazon.com/Emperors-New-Mind-Roger-Penrose/dp/0140145346">《The Emperor&#8217;s New Mind》</a>中用它来证明人工智能的不可实现。当然，这个结论是很受质疑的。但哥德尔的不完备性定理的确还有很多很多的有趣推论，数学的和哲学上的。哥德尔的不完备性定理最深刻的地方就是它揭示了自指（或称自引用，递归调用自身等等）结构的普遍存在性，我们再来看一看哥德尔命题的绝妙构造：</p>
<p>G(n): UnPr( N(n) )</p>
<p>我们注意到，这里的UnPr其实是一个形式化的谓词，它不一定要说“X在T内可证明”，我们可以把它泛化为一个一般化的谓词，P：</p>
<p><b>G(n): P( N(n) )</b></p>
<p>也就是说，对于任意一个单参的谓词P，都存在上面这个哥德尔公式。然后我们算出这个哥德尔公式的自然数编码g，然后把它扔给G，就得到：</p>
<p>G(g): P( G(g) )</p>
<p>是不是很熟悉这个结构？我们的Y Combinator的构造不就是这样一个形式？我们把G和P都看成一元函数，G(g)可不正是P这个函数的不动点么！于是，<b>我们从哥德尔的证明里面直接看到了</b><b>Y Combinator</b>！</p>
<p>至于如何从哥德尔的证明联系到停机问题，就留给你去解决吧:) 因为更重要的还在后面，我们看到，哥德尔的证明虽然巧妙至极，然而其背后的思维过程仍然飘逸而不可捉摸，至少我当时看到G(n)的时候，“乃大惊”“不知所从出”，他怎么想到的？难道是某一个瞬间“灵光一现”？一般我是不信这一说的，已经有越来越多的科学研究表明一瞬间的“灵感”往往是潜意识乃至表层意识长期思考的结果。哥德尔天才的证明也不例外，我们马上就会看到，在这个神秘的构造背后，其实隐藏着某种更深的东西，这就是康托尔在19世纪80年代研究无穷集合和超限数时引入的对角线方法。这个方法仿佛有种神奇的力量，能够揭示出某种自指的结构来，而同时，这又是一个极度简单的手法，通过它我们能够得到数学里面一些非常奇妙的性质。无论是哥德尔的不完备性定理还是再后来丘齐建立的lambda calculus，抑或我们非常熟悉的图灵机理论里的停机问题，其实都只是这个手法简单推演的结果！</p>
<p><b>大道至简</b><b>——</b><b>康托尔的天才</b><b></b></p>
<p>“大道至简”这个名词或许更多出现在文学和哲学里面，一般用在一些模模糊糊玄玄乎乎的哲学观点上。然而，用在这里，数学上，这个名词才终于适得其所。大道至简，看上去最复杂的理论其实建立在一个最简单最纯粹的道理之上。</p>
<p>康托尔在无穷集合和超限数方面的工作主要集中在两篇突破性的论文上，这也是我所见过的最纯粹最美妙的数学论文，现代的数学理论充斥了太多复杂的符号和概念，很多时候让人看不到最本质的东西，当然，不否认这些东西很多也是有用的，然而，要领悟真正的数学美，像集合论和数论这种纯粹的东西，真的非常适合。不过这里就不过多谈论数学的细节了，只说康托尔引入对角线方法的动机和什么是对角线方法。</p>
<p><b>神奇的一一对应</b></p>
<p>康托尔在研究无穷集合的时候，富有洞察性地看到了对于无穷集合的大小问题，我们不能再使用直观的“所含元素的个数”来描述，于是他创造性地将一一对应引入进来，两个无穷集合“大小”一样当且仅当它们的元素之间能够构成一一对应。这是一个非常直观的概念，一一对应嘛，当然个数相等了，是不是呢？然而这同时就是它不直观的地方了。对于无穷集合，我们日常的所谓“个数”的概念不管用了，因为无穷集合里面的元素个数本就是无穷多个。不信我们来看一个小小的例子。我们说自然数集合能够跟偶数集合构成一一对应，从而<i>自然数集合跟偶数集合里面元素</i><i>“</i><i>个数</i><i>”</i><i>是一样多的</i>。怎么可能？偶数集合是自然数集合的真子集，所有偶数都是自然数，但自然数里面还包含奇数呢，说起来应该是二倍的关系不是？不是！我们只要这样来构造一一对应：</p>
<p>1 2 3 4 …</p>
<p>2 4 6 8 …</p>
<p>用函数来描述就是 f(n) = 2n。检验一下是不是一一对应的？不可思议对吗？还有更不可思议的，<i>自然数集是跟有理数集一一对应的</i>！对应函数的构造就留给你解决吧，提示，按如下方式来挨个数所有的有理数：</p>
<p>1/1 1/2 2/1 1/3 2/2 3/1 1/4 2/3 3/2 4/1 …</p>
<p>用这种一一对应的手法还可以得到很多惊人的结论，如<i>一条直线上所有的点跟一个平面上所有的点构成一一对应</i>（也就是说<i>复数集合跟实数集合构成一一对应</i>）。以致于连康托尔自己都不敢相信自己的眼睛了，这也就是为什么他在给戴得金的信中会说“我看到了它，却不敢相信它”的原因。</p>
<p>然而，除了一一对应之外，还有没有不能构成一一对应的两个无穷集合呢？有。<i>实数集合就比自然数集合要</i><i>“</i><i>大</i><i>”</i>，它们之间实际上无法构成一一对应。这就是康托尔的对角线方法要解决的问题。</p>
<p><b>实数集和自然数集无法构成一一对应？！</b><b></b></p>
<p>我们只需将实数的小数位展开，并且我们假设实数集能够与自然数集一一对应，也就是说假设实数集<a href="http://en.wikipedia.org/wiki/Countable">可列</a>，所以我们把它们与自然数一一对应列出，如下：</p>
<p>1 a<sub>10</sub>.a<sub>11</sub>a<sub>12</sub>a<sub>13</sub>…</p>
<p>2 a<sub>20</sub>.a<sub>21</sub>a<sub>22</sub>a<sub>23</sub>…</p>
<p>3 a<sub>30</sub>.a<sub>31</sub>a<sub>32</sub>a<sub>33</sub>…</p>
<p>4 …</p>
<p>5 …</p>
<p>（注：aij里面的ij是下标）</p>
<p>现在，我们构造一个新的实数，它的第i位小数不等于aii。也就是说，它跟上面列出的每一个实数都至少有一个对应的小数位不等，也就是说它不等于我们上面列出的所有实数，这跟我们上面假设已经列出了所有实数的说法相矛盾。所以实数集只能是不可列的，即不可与自然数集一一对应！这是对角线方法的最简单应用。</p>
<p><b>对角线方法</b><b>——</b><b>停机问题的深刻含义</b><b></b></p>
<p>对角线方法有很多非常奇妙的结论。其中之一就是文章一开始提到的停机问题。我想绝大多数人刚接触停机问题的时候都有一个问题，图灵怎么能够想到这么诡异的证明，怎么能构造出那个诡异的“说停机又不停机，说不停机又停机”的悖论机器。马上我们就会看到，这其实只是对角线方法的一个直接结论。</p>
<p>还是从反证开始，我们假设存在这样一个图灵机，他能够判断任何程序在任何输入上是否停机。由于所有图灵机构成的集合是一个可列集（也就是说，我们可以逐一列出所有的图灵机，严格证明见我以前的一篇文章《<a href="http://blog.csdn.net/pongba/archive/2006/03/11/621723.aspx">图灵机杂思</a>》），所以我们可以很自然地列出下表，它表示每个图灵机分别在每一个可能的输入（1,2,3,…）下的输出，N表示无法停机，其余数值则表示停机后的输出：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160; 1&#160; 2&#160; 3&#160; 4 …</p>
<p>M1&#160; N&#160; 1&#160; N&#160; N …</p>
<p>M2&#160; 2&#160; 0&#160; N&#160; 0 …</p>
<p>M3&#160; 0&#160; 1&#160; 2&#160; 0 …</p>
<p>M4&#160; N&#160; 0&#160; 5&#160; N …</p>
<p>…</p>
<p>M1，M2，M3 … 是逐一列出的图灵机，并且，注意，由于程序即数据，每个图灵机都有唯一编码，所以我们规定在枚举图灵机的时候Mi其实就代表编码为i的图灵机，当然这里很多图灵机将会是根本没用的玩意，但这不要紧。此外，最上面的一行1 2 3 4 … 是输入数据，如，矩阵的第一行代表M1分别在1，2，3，…上面的输出，不停机的话就是N。</p>
<p>我们刚才假设存在这样一个图灵机H，它能够判断任何程序在任何输入上能否停机，换句话说，H(i,j)（i是Mi的编码）能够给出“Mi(j)”是N（不停）呢还是给出一个具体的结果（停）。</p>
<p>我们现在来运用康托尔的对角线方法，我们构造一个新的图灵机P，P在1上的输出行为跟M1(1)“不一样”，在2上的输出行为跟M2(2)“不一样”，…总之P在输入i上的输出跟Mi(i)不一样。只需利用一下我们万能的H，这个图灵机P就不难构造出来，如下：</p>
<p>P(i):</p>
<p>if( <b>H</b>(i, i) == 1 ) then // Mi(i) halts</p>
<p>&#160; return 1 + Mi(i)</p>
<p>else // if H(i, i) == 0 (Mi(i) doesn’t halt)</p>
<p>&#160; return 0</p>
<p>也就是说，如果Mi(i)停机，那么P(i)的输出就是Mi(i)+1，如果Mi(i)不停机的话，P(i)就停机且输出0。这就保证了P(i)的输出行为跟Mi(i)反正不一样。现在，我们注意到P本身是一个图灵机，而我们上面已经列出了所有的图灵机，所以必然存在一个k，使得Mk = P。而两个图灵机相等当且仅当它们对于所有的输入都相等，也就是说对于任取的n，有Mk(n) = P(n)，现在令n=k，得到Mk(k)=P(k)，根据上面给出的P的定义，这实际上就是：</p>
<p>Mk(k) = P(k) = </p>
<p>&#160; 1+Mk(k) if Mk(k) halts</p>
<p>&#160; 0 if Mk(k) doesn’t halt</p>
<p>看到这个式子里蕴含的矛盾了吗？如果Mk(k)停机，那么Mk(k)=1+Mk(k)；如果Mk(k)不停机，则Mk(k)=0（给出结果0即意味着Mk(k)停机）；不管哪种情况都是矛盾。于是我们得出，不存在那样的H。</p>
<p>这个对角线方法实际上说明了，无论多聪明的H，总存在一个图灵机的停机行为是它无法判断的。这跟哥德尔定理“无论多‘完备’的形式化公理系统，都存在一个‘哥德尔命题’是无法在系统内推导出来的”从本质上其实是一模一样的。只不过我们一般把图灵的停机问题称为“可判定问题”，而把数学的称为“可证明问题”。</p>
<p>等等！如果我们把那个无法判定是否停机的图灵机作为算法的特例纳入到我们的H当中呢？我们把得到的新的判定算法记为H<sub>1</sub>。然而，可惜的是，在H<sub>1</sub>下，我们又可以相应地以同样的手法从H<sub>1</sub>构造出一个无法被它（H<sub>1</sub>）判定的图灵机来。你再加，我再构造，无论你加多少个特例进去，我都可以由同样的方式构造出来一个你无法够到的图灵机，以彼之矛，攻彼之盾。其实这也是哥德尔定理最深刻的结论之一，哥德尔定理其实就说明了无论你给出多少个公理，即无论你建立多么完备的公理体系，这个系统里面都有由你的那些公理出发所推导不到的地方，这些黑暗的角落，就是人类直觉之光才能照射到的地方！</p>
<p>本节我们从对角线方法证明了图灵的停机问题，我们看到，对角线方法能够揭示出某种自指结构，从而构造出一个“悖论图灵机”。实际上，对角线方法是一种有深远影响的方法，哥德尔的证明其实也是这个方法的一则应用。证明与上面的停机问题证明如出一辙，只不过把Mi换成了一个形式系统内的公式fi，具体的证明就留给聪明的你吧:)我们现在来简单的看一下这个奇妙方法的几个不那么明显的推论。</p>
<p><b>罗素悖论</b><b></b></p>
<p>学过逻辑的人大约肯定是知道著名的罗素悖论的，罗素悖论用数学的形式来描述就是：</p>
<p>R = {X:X不属于X};</p>
<p>这个悖论最初是从康托尔的无穷集合论里面引申出来的。当初康托尔在思考无穷集合的时候发现可以称“一切集合的集合”，这样一个集合由于它本身也是一个集合，所以它就属于它自身。也就是说，我们现在可以称世界上存在一类属于自己的集合，除此之外当然就是不属于自己的集合了。而我们把所有不属于自己的集合收集起来做成一个集合R，这就是上面这个著名的罗素悖论了。</p>
<p>我们来看R是否属于R，如果R属于R，根据R的定义，R就不应该属于R。而如果R不属于R，则再次根据R的定义，R就应该属于R。</p>
<p>这个悖论促使了集合论的公理化。后来策梅罗公理化的集合论里面就不允许X属于X（不过可惜的是，尽管如此还是没法证明这样的集合论不可能产生出新的悖论。而且永远没法证明——这就是哥德尔第二不完备性定理的结论——一个包含了PA的形式化公理系统永远无法在内部证明其自身的一致（无矛盾）性。从而希尔伯特想从元数学推出所有数学系统的一致性的企图也就失败了，因为元数学的一致性又得由元元数学来证明，后者的一致性又得由元元元数学来证明…）。</p>
<p>这里我们只关心罗素是如何想出这个绝妙的悖论的。还是对角线方法！我们罗列出所有的集合，S1,S2,S3 …</p>
<p>&#160;&#160;&#160;&#160;&#160; S1&#160; S2&#160; S3 …</p>
<p>S1&#160; 0&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160; 1 …</p>
<p>S2&#160; 1&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160; 0 …</p>
<p>S3&#160; 0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; 0 …</p>
<p>… …</p>
<p>右侧纵向列出所有集合，顶行横向列出所有集合。0/1矩阵的(i,j)处的元素表示Si是否包含Sj，记为Si(j)。现在我们只需构造一个新的0/1序列L，它的第i位与矩阵的(i,i)处的值恰恰相反：L(i) = 1-Si(i)。我们看到，这个新的序列其实对应了一个集合，不妨也记为L，L(i)表示L是否包含Si。根据L的定义，如果矩阵的(i,i)处值为0（也就是说，如果Si不包含Si），那么L这个集合就包含Si,否则就不包含。我们注意到这个新的集合L肯定等于某个Sk（因为我们已经列出了所有的集合），L = Sk。既然L与Sk是同一集合，那么它们肯定包含同样的元素，从而对于任意n，有L(n) = Sk(n)。于是通过令n=k，得到L(k) = Sk(k)，而根据L的定义，L(k) = 1- Sk(k)。这就有Sk(k) = 1-Sk(k)，矛盾。</p>
<p>通过抽象简化以上过程，我们看到，我们构造的L其实是“包含了所有不包含它自身的集合的集合”，用数学的描述正是罗素悖论！</p>
<p>敏锐的你可能会注意到所有集合的数目是不可数的从而根本不能S1,S2…的一一列举出来。没错，但通过假设它们可以列举出来，我们发现了一个与可列性无关的悖论。所以这里的对角线方法其实可以说是一种启发式方法。</p>
<p>同样的手法也可以用到证明P(A)（A的所有子集构成的集合，也叫幂集）无法跟A构成一一对应上面。证明就留给聪明的你了:)</p>
<p><b>希尔伯特第十问题结出的硕果</b><b></b></p>
<p>希尔伯特是在1900年巴黎数学家大会上提出著名的希尔伯特第十问题的，简言之就是<i>是否存在一个算法，能够计算任意</i><i><a href="http://en.wikipedia.org/wiki/Diophantine_equation">丢番图方程</a></i><i>是否有整根</i>。要解决这个问题，就得先严格定义“算法”这一概念。为此图灵和丘齐分别提出了图灵机和lambda calculus这两个概念，它们从不同的角度抽象出了“有效（机械）计算”的概念，著名的<a href="http://en.wikipedia.org/wiki/Church-Turing_thesis">图灵——丘齐命题</a>就是说<i>所有可以有效计算出来的问题都可以由图灵机计算出来</i>。实际上我们已经看到，丘齐的lambda calculus其实就是数学推理系统的一个形式化。而图灵机则是把这个数学概念物理化了。而也正因为图灵机的概念隐含了实际的物理实现，所以冯·诺依曼才据此提出了奠定现代计算机体系结构的<a href="http://en.wikipedia.org/wiki/Von_Neumann_architecture">冯·诺依曼体系结构</a>，其遵循的，正是图灵机的概念。而“程序即数据”的理念，这个发端于数学家哥德尔的不完备性定理的证明之中的理念，则早就在黑暗中预示了可编程机器的必然问世。</p>
<p><b>对角线方法</b><b>——</b><b>回顾</b><b></b></p>
<p>我们看到了对角线方法是如何简洁而深刻地揭示出自指或递归结构的。我们看到了著名的不完备性定理、停机问题、Y Combinator、罗素悖论等等等等如何通过这一简洁优美的方法推导出来。这一诞生于康托尔的天才的手法如同一条金色的丝线，把位于不同年代的伟大发现串联了起来，并且将一直延续下去…</p>
<p>P.S</p>
<p>1. lambda calculus里面的“停机问题”</p>
<p>实际上lambda calculus里面也是有“停机问题”的等价版本的。其描述就是：不存在一个算法能够判定任意两个lambda函数是否等价。所谓等价当然是对于所有的n,有f(n)=g(n)了。这个问题的证明更加能够体现对角线方法的运用。仍然留给你吧。</p>
<p>2. <a href="http://blog.csdn.net/g9yuayon">负喧琐话</a>(<a href="http://blog.csdn.net/g9yuayon">http://blog.csdn.net/g9yuayon</a>)是个非常不错的blog:)。g9的文字轻松幽默，而且有很多名人八卦可以养眼，真的灰常…灰常…不错哦。此外g9老兄还是个理论功底非常扎实的牛。所以，anyway，看了他的blog就知道啦！最初这篇文章的动机也正是看了上面的一篇<a href="http://blog.csdn.net/g9yuayon/archive/2006/09/24/1271319.aspx">关于Y Combinator的铸造过程的介绍</a>，于是想揭示一些更深的东西，于是便有了本文。</p>
<p>3. 文章起名《康托尔、哥德尔、图灵——永恒的金色对角线》其实是为了纪念看过的一本好书GEB，即《Godel、Escher、Bach-An Eternal Golden Braid》中文译名《哥德尔、埃舍尔、巴赫——集异璧之大成》——商务印书馆出版。对于一本定价50元居然能够在douban上卖到100元的二手旧书，我想无需多说。另，幸福的是，电子版可以找到:)</p>
<p>4. 其实很久前想写的是一篇《从哥德尔到图灵》，但那篇写到1/3不到就搁下了，一是由于事务，二是总觉得少点什么。呵呵，如今把康托尔扯进来，也算是完成当时扔掉的那一篇吧。</p>
<p>5. 这恐怕算是写得最曲折的一篇文章了。不仅自己被这些问题搞得有点晕头转向（还好总算走出来），更因为要把这些东西自然而然的串起来，也颇费周章。很多时候是利用吃饭睡觉前或走路的时间思考本质的问题以及如何表达等等，然后到纸上一气呵成。不过同时也锻炼了不拿纸笔思考数学的能力，呵呵。</p>
<p>6. 关于图灵的停机问题、Y Combinator、哥德尔的不完备性定理以及其它种种与康托尔的对角线之间的本质联系，几乎查不到完整系统的深入介绍，一些书甚至如《The Emperor’s New Mind》也只是介绍了与图灵停机问题之间的联系（已经非常的难得了），google和baidu的结果也是基本没有头绪。很多地方都是一带而过让人干着急。所以看到很多地方介绍这些定理和构造的时候都是弄得人晕头转向的，绝大部分人在面对如Y Combinator、不完备性定理、停机问题的时候都把注意力放在力图理解它是怎么运作的上面了，却使人看不到其本质上从何而来，于是人们便对这些东东大为惊叹。这使我感到很不痛快，如隔靴搔痒般。这也是写这篇文章的主要动机之一。</p>
<p><strong>Reference</strong></p>
<p>[1] 《数学——确定性的丧失》</p>
<p>[2] 也有观点认为函数式编程语言之所以没有广泛流行起来是因为一些实际的商业因素。</p>
<p>[3] Douglas R.Hofstadter的著作《Godel, Escher, Bach: An Eternal Golden Braid》（《哥德尔、艾舍尔、巴赫——集异璧之大成》）就是围绕这一思想写出的一本奇书。非常建议一读。</p>
<p>[4] 《数学——确定性的丧失》</p>
<p>[5] 虽然我觉得那个系徽做得太复杂，要表达这一简洁优美的思想其实还能有更好的方式。</p>
<p>[6] 关于如何在lambda calculus系统里实现“+”操作符以及自然数等等，可参见<a href="http://blog.csdn.net/g9yuayon/archive/2006/05/29/759778.aspx">这里</a>，<a href="http://blog.csdn.net/g9yuayon/archive/2006/06/12/790953.aspx">这里</a>，和<a href="http://blog.csdn.net/g9yuayon/archive/2006/08/14/1062514.aspx">这里</a>。</p>
<p>[7] g9的blog（负暄琐话）<a href="http://blog.csdn.net/g9yuayon/">http://blog.csdn.net/g9yuayon/</a> 上有一系列介绍lambda calculus的文章（当然，还有其它好文章:)），非常不错，强烈推荐。最近的两篇就是介绍Y combinator的。其中有一篇以javaScript语言描述了迭代式逐步抽象出Y Combinator的过程。</p>
<p>[8] 实际上这只是第一不完备性定理，它还有一个推论，被称为第二不完备性定理，说的是任一个系统T内无法证明这个系统本身的一致性。这个定理的证明核心思想如下：我们前面证明第一不完备性定理的时候用的推断其实就表明 Con/T -&gt; G(g) （自然语言描述就是，由系统T的无矛盾，可以推出G(g)成立），而这个“Con/T -&gt; G(g)”本身又是可以在T内表达且证明出来的（具体怎么表达就不再多说了）——只需要用排中律即可。于是我们立即得到，T里面无法推出Con/T，因为一旦推出Con/T就立即推出G(g)从而推出UnPr(G(g))，这就矛盾了。所以，Con/T无法在T内推出（证明）。</p>
<h3  class="related_post_title">你可能也会喜欢以下文章</h3><ul class="related_post"><li><a href="http://mindhacks.cn/2008/09/21/the-magical-bayesian-method/" title="数学之美番外篇：平凡而又神奇的贝叶斯方法">数学之美番外篇：平凡而又神奇的贝叶斯方法</a> (26)</li><li><a href="http://mindhacks.cn/2008/06/13/why-is-quicksort-so-quick/" title="数学之美番外篇：快排为什么那样快">数学之美番外篇：快排为什么那样快</a> (9)</li><li><a href="http://mindhacks.cn/2008/09/11/machine-learning-and-ai-resources/" title="机器学习与人工智能学习资源导引">机器学习与人工智能学习资源导引</a> (6)</li><li><a href="http://mindhacks.cn/2007/12/02/probability-theory-in-evolution/" title="数学之美番外篇：进化论中的概率论">数学之美番外篇：进化论中的概率论</a> (4)</li></ul><hr />
<h3>订阅 Mind Hacks</h3>
<a title="用Google Reader订阅" href="http://fusion.google.com/add?feedurl=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_google.gif" style="border:0" alt="订阅到 | Google" /></a> 
<a title="用鲜果订阅" href="http://www.xianguo.com/subscribe.php?url=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_xianguo.gif" style="border:0" alt="订阅到 | 鲜果" /></a>
<br/> 
<a title="用抓虾订阅" href="http://www.zhuaxia.com/add_channel.php?url=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_zhuaxia.gif" style="border:0" alt="订阅到 | 抓虾" /></a> 
<a title="用有道订阅" href="http://reader.youdao.com/b.do?keyfrom=mindhacks&url=http://mindhacks.cn/feed/"><img src="http://mindhacks.cn/wp-content/uploads/2009/02/feed_yodao1.gif" style="border:0" alt="订阅到 | 有道" /></a> 
<hr />
<h3>我是你的信息过滤器</h3>
想了解作者最近在关注什么，欢迎 Follow <a href="https://twitter.com/pongba">pongba@Twitter</a>
<br/>
程序员朋友请到作者发起的 <a href="https://groups.google.com/group/pongba">TopLanguage</a> (<a href="http://mindhacks.cn/about-toplanguage/">about</a>) 社群逛逛，定有收获 :)
<br/>
想了解作者在阅读哪些书，请到 <a href="http://www.douban.com/people/pongba/">pongba@豆瓣</a>，或者直接访问以下四个豆列：<a href="http://www.douban.com/doulist/46003/">[只读经典]思维改变生活</a> | <a href="http://www.douban.com/doulist/127649/">[只读经典]思考的技术与艺术</a> | <a href="http://www.douban.com/doulist/197706/">决策与判断</a> | <a href="http://www.douban.com/doulist/176513/">机器学习与人工智能书籍资源导引</a> 。
<br/>
想了解作者平时在互联网上的阅读，请订阅 <a href="http://delicious.com/pongba/shared-reading">pongba-shared-reading@delicious</a> 。
<hr />
<p><small>
本文由 刘未鹏 发布在 <a href="http://mindhacks.cn">刘未鹏 | Mind Hacks</a>, 2006. | <a href="http://mindhacks.cn/2006/10/15/cantor-godel-turing-an-eternal-golden-diagonal/#commenting">10 条评论</a> | 标签: <a href="http://mindhacks.cn/tags/%e6%95%b0%e5%ad%a6/" rel="tag">数学</a>, <a href="http://mindhacks.cn/tags/%e8%ae%a1%e7%ae%97%e6%9c%ba%e7%a7%91%e5%ad%a6/" rel="tag">计算机科学</a>
<br/>
转载请注明作者，出处，以及<a href="http://mindhacks.cn/2006/10/15/cantor-godel-turing-an-eternal-golden-diagonal/">原始超链接</a>: http://mindhacks.cn/2006/10/15/cantor-godel-turing-an-eternal-golden-diagonal/
</small></p>]]></content:encoded>
			<wfw:commentRss>http://mindhacks.cn/2006/10/15/cantor-godel-turing-an-eternal-golden-diagonal/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
	</channel>
</rss>
