如果你一直读到了这里,就会对现代逻辑的基本思想有一个不错的理解。不过,也仅限于入门。现代逻辑的内容远胜于此,包含极为深刻而优美的结果。当然,我们不可能在这样一本书中全面评述这些结果。但本章和下一章,我们至少对它们提供一瞥。我们将看看一些关于形式推理哪些可以做哪些不能做的结果,以及这些结果的哲学意味。警告:这两章可能比前面的章节更难一点。我已竭力简单化处理,但我们涉及的是一些复杂的数学问题。说完这些,让我们继续本章的主题。
莱布尼茨——我们在第 6 章和第 9 章碰到的那个莱布尼茨——有一个梦想,一个终结争论之梦。每当我们有一个富有争议的论断,我们就可以用一种被称为普遍文字(characteristica universalis)的恰当语言把它写出来。然后,为了判定该论断的真假,我们演算(calculemus)就行。这种语言使得有一个计算过程(calculus ratiocinator)可以被用于判定该论断是否为真。
尽管莱布尼茨对于实现该计划确实建议了一些步骤,但它一度只是一个梦想。莱布尼茨时代的数学还没有达到可以处理其设想的程度。
我们时代的数学达到了。我们在前面的章节中看到的符号语言可以表达(至少一大类)真假未知的论断。剩下的问题就是,是否存在一个合适的计算过程。
答案(不幸)是,不存在——即使对范围十分有限的数学论断也是如此。该结果由英国数学家阿兰·图灵(1912-1954)于 1936 年证明。图灵是现代计算机科学的奠基者之一。当然,在图灵时代,没有任何为现在多数人所熟知的现代计算机这样的东西。但这些机器的理论远在计算机出现之前就已被图灵和其他人发明,剩下那些人只是去寻找如何在实践中实现这些想法——尽管图灵本人在实际构建计算机器方面(例如在破解二战中德国海军电报密码的Enigma项目中),也取得过令人瞩目的进展。正如所料,图灵在计算上的兴趣与莱布尼茨之梦的联系并非巧合。
什么是计算机?最简单的情况下,计算机就是某种接收一个或多个输入,执行某个过程——数学家称之为算法(algorithm),该名称来自波斯数学家 Al Khwārizmī(780-850)——然后(如果运气好的话)给你一个输出的装置。
现代计算机的输入和输出有不同种类:数、文本、图片、声音。但对机器而言,这些都是数,这是它所能作用的全部对象。计算机的输入装置将输入转换成一串算法能作用的数。输出装置则将这一过程倒转过来。
不过,计算机存储数的形式并非我们在小学算术中熟知的那些。计算机的存储单元只能是两个状态中的某一个:开或关。因此,计算机只有两个基本字节的信息可以使用。人们可以把它们想成 1 和 0。任何数都可以用这两个数字表达。这是由二进制算术完成的。(即,如果你只有两个指头你会如何计数。)在标准(十进制)算术中,数实际上是一种表达 10 的幂之和的方式。比如,4302 就是:
($$10^0$$——实际上任何数的 0 次幂——就是 1)。类似的,二进制数也是一些幂之和,只是这次是 2 的幂。于是,1011 就是:
下表给出了前几个十进制数与二进制数之间的转换。
十进制 | 二进制 |
---|---|
0 | 0 |
1 | 1 |
2 | 10 |
3 | 11 |
4 | 100 |
5 | 101 |
所以我们可以将计算(算法)看作某种作用在这种二进制数上的东西。
输入和输出就讲这么多,但什么是计算?计算由一组在标准计算机程序中可找到的那类规则给定。这些程序由很多不同的语言写成,其精确细节在这里并不重要。一个相当无趣的程序可能长下面这个样子:
- 若
$$x=0$$ 则输出$$x$$ ;否则跳至第 2 行 - 令
$$x:=x-1$$ - 跳至第 1 行
左边的数是行号。输入是某个数
目前都没有问题。接下来——这是现代计算机真正聪明之处——当计算进行时,计算机不必等待人输入每一行程序。程序本身就存储在计算机中。当然,是存储成一个数。计算机没法存储任何其他东西。(事实上,我们可以把计算机在任何时刻的整个状态想成一个巨大的 0、1 字符串——一个庞大的二进制数!)我们可以把存储在计算机中代表程序的数看作程序的"编码"。若
现在,有时一个给定输入的程序会产生一个输出;但有时它会永远运行下去。比如,考虑下面这个程序:
- 令
$$x:=x+1$$ - 若
$$x=0$$ 则输出$$0$$ ;否则跳至第 1 行
该程序将某个输入加 1,然后测试它是否为 0,如果是,则输出
良好构造的程序会被设计成这绝不会发生。程序员通过分析程序,保证它永远不会进入这种无限循环。但这总是能做到吗?是否有算法可以对任何程序和输入,判定该程序和输入下的计算是否会终止?
答案是:没有。这就是图灵所证明的。这是一个相对简单,但却十分精巧的证明。它使用了归谬法(reductio ad absurdum),先假定和我们希望证明的结论相反的情况成立,然后表明这会导致某种不可接受的结果。
假定有一个这样满足要求的算法,称之为
现在考虑下面这个算法。让我们称之为
- 运行算法
$$A$$ (输入$$x$$ 和$$x$$ )。该算法终止并给出 1 或 0 的结果。 - 若结果是 0,则输出 1
- 若结果是 1,则运行
$$L$$ (输入$$x$$ )。
该程序输入
我是用一种颇为"高层次"的术语来描述程序
现在,为了完成证明:$$T$$ 自身也有一个编码,称之为
图灵证明的精巧之处是某种自指。(我们在第5章碰到过自指。)它让某个假定的程序应用于它自身的编码。这有时被称作对角化(diagonalization),一种由伟大的德国数学家康托(Georg Cantor, 1845-1918)在研究无穷时发明的方法。考察下表你就会明白它为何叫对角化。
0 | 1 | 2 | 3 | 4 | ||
---|---|---|---|---|---|---|
0 | ||||||
1 | ||||||
2 | ||||||
3 | ||||||
4 | ||||||
最左列是程序的编码。最上面一行是输入。表格中的项
这个结果称为停机定理(Halting Theorem)。它表明,没有算法可以判定任何给定的程序在给定的输入下是否停机(当然,我们尽可在特殊情形做到这一点)。现在回到莱布尼茨之梦。我们看到的是,有一些数学问题,比如这个停机问题,并没有算法判定其真假。莱布尼茨之梦无法实现。
我在结束前面的章节时会指出,为何其中的看法可能存在争议。让我以同样的方式结束本章。在标准数论假设下,图灵的证明是无可辩驳的。这是足够好的数学。但在我给的上述证明中,有一个到现在我还未作评论的假设。该假设就是,任何我们能看作算法的东西都能写成计算机程序。如果并非如此,则图灵的证明只是表明了没有计算机程序能判定任何计算是否停止。但或许存在某种其他的算法——一种或许能在莱布尼茨的计划中有效使用的算法。
每个算法都能写成计算机程序,这一论断被称为丘奇-图灵论题(Church-Turing Thesis),以图灵和美国数学家Alonzo Church(1903-1995)命名。它本身不能进行数学证明,因为证明只能作用于被精确定义的概念,而尽管什么是计算机能做的_可以_被精确的数学语言定义,算法却只是一个非形式的直观概念。粗略而言,算法是可以无需猜测和创造性的逐步完成的过程——而这或多或少是模糊概念。
长期以来,丘奇-图灵论题都被数学家所接受。曾有一段试图反驳它的历史。这些反驳都试图构造某种直观上可以被看作算法,但却不能在计算机上编程的东西。这些尝试都失败了。因此,丘奇-图灵论题就被普遍接受了。
然而,现在也有研究领域研究除了那种运用于台式计算机中的计算的计算方法。这些计算有时被称作超计算(hypercomputation)。一个例子是:有些涉及的方法使用模拟量,而不是数字量。(模拟量是连续的,如长度;而数字量则是离散的,如二进制数。)另一个例子是:有些涉及的方法诉诸于广义相对论中的时空性质,其中时间会"加速"。不过,现在谈这些研究的最终结果如何还为时尚早。
本章要点
- 算法可以被编码。
- 假若有算法
$$A$$ ,可以判定编码为$$x$$ 的算法(即$$P_x$$ )输入$$y$$ 是否终止,则我们可以定义算法$$T$$ ,它计算$$A$$ 输入$$x$$ 和$$x$$ 得到的值,并利用该结果确保它自身的输出“在对角线上”不同于每个$$P_x$$ 。$$T$$ 自身必定有一个编码$$t$$ 。运行$$T$$ (输入$$t$$ )将产生一个不可能的结果。- 因此没有这样的算法
$$T$$ ,因而也就没有这样的算法$$A$$ 。