你有没有想过:你正在阅读这篇文章的设备究竟是什么?什么是电脑?
计算科学可以追溯到在这些现代计算机设备还没有被想象出来之前很长一段时间。在一个更经常被问到的问题中,围绕着编程语言、框架和库的问题,我们常常想当然地认为,计算机的基本概念是必不可少的。
但是这些电脑似乎拥有无尽的潜力,它们有任何限制吗?有使用计算机却解决不了的问题吗?
在本文中,我们将通过不再讨论编程语言和计算机体系结构的细节来解决这些问题。通过了解计算机和算法的能力和局限性,我们可以改进我们的思考方式,并更好地解释不同的策略。
计算机的抽象观点产生了经得起时间考验的结果,与上世纪70年代最初发展时一样,对今天的我们来说依旧是有价值的。
在学校里,我们经常被教授一个问题和功能的心理模型,就像这样:
函数是一个通过输入x以便获得一个输出f(x)的过程。
原来数学的定义是不一样的:
一个函数是一组有序对,每对的第一个元素来自一个集合X(称为域),每个对的第二个元素来自一个集合Y(称为陪域或范围),该域中每个元素都与该范围中的唯一一个元素相配对(即X中每一个元素都存在惟一的Y中的元素与之对应)。
这是相当的拗口,但是,这到底意味着什么呢?
这个定义告诉我们,计算机是计算函数的机器。
为什么?
由于计算机将任意输入转换为某些输出。换句话说,他们解决问题。函数的两个定义,我们熟悉的和正式的,都有很多实际的用途。
然而,数学定义使我们能够得出一些有趣的结论,例如存在不可计算的函数(即无法解决的问题):
因为,并不是每个函数都可以被描述为一个算法。
游戏规则
为了使我们的论点有所帮助,让我们将计算机想象成机器接受一些输入,执行一系列操作,并在一段时间后给出一些输出。
我们把输入称为机器的字母表;也就是说,机器的字母表可能是二进制的(0和1),也可能是ASCII字符集,任何有限的字符序列都是一个字符串,例如“0110”。
此外,我们将机器的输出表示为二进制接受 - 拒绝决策,一旦机器(希望)完成其计算就交付。这个抽象符合早期函数的数学定义。
给定这些参数,对重新定义一个类型是很重要的:字符串集合。也许我们关心某些机器接受的字符串集合,或者我们正在建立一个接受某个集合中的字符串而不接受其他字符串的机器,或者我们问是否可以设计一个只接受某些特定下的所有东西的机器。
在所有这些情况下,一组字符串被称为一种语言,例如,表示偶数的所有二进制字符串的集合或具有偶数个字符的字符串的集合。事实证明,像数字这样的语言可以用诸如串联(concatenation),联合(union ),交叉(intersection)等操作符来操作。
一个重要的运算符是Kleene星形( Kleene star)运算符,它也用于正则表达式。这可以被认为是所有可能的语言权力的结合。例如,如果我们的语言A是字符串{'01','1'}的集合,则A *的一个成员是字符串'0101111'。
在我们证明并不是所有的函数都是可计算的之前,最后一个问题是可数性的概念。直观地说,我们的证明会显示有更多的语言;这是比可能的解决方案更多的问题。这是因为字符串是否属于语言(yes/no)本身就是一个问题。
更确切地说, 我们的证据声称, 可能的程序集是可无穷大的, 而在字母表的语言集是不可无穷大。
在这一点上,你可能会想,“无穷大本身就是一个奇怪的想法;现在我必须处理其中的两个!”
好吧,没那么糟。可数无限集是可以枚举的。可以这样说,这是第一个元素,这是第二个元素,等等,最终给集合中的每个元素分配一个数字,例如,取偶数的集合。我们可以说,2 是第一个, 4 第二, 6 第三, 依此类推。这种集合是可无穷或可数的。
尽管有一些集合, 像实数一样, 你有多聪明也无所谓。根本没有枚举。这些集是不可数的无限或无数。
可数的许多程序
首先,我们要证明计算机程序集是可数的。出于我们的目的,我们通过观察一个有限字母的所有字符串的集合是可数的。这是可行的,因为计算机程序本身就是有限的字符串。
这个证明是直接的,我们不涉及细节。关键的一点是,那里的计算机程序和自然数字一样多。
重申:
任何字母表上的所有字符串(例如,所有计算机程序的集合)的集合都是可数的。
再一次,我们不涉及到证明。
后果
虽然他们可能不会立即显现出来,但语言的不可数性和所有计算机程序的可计数性的后果是深刻的。
为什么?
假设A是一组ASCII字符,ASCII字符就是组成计算机程序所需要的。我们可以看到,表示JavaScript程序的字符串集合是A *的一个子集(这里的*是Kleene星形运算符)。JavaScript的选择是任意的。由于这套程序是可数集的一个子集,我们已经知道这套JavaScript程序是可数的。
另外,让我们考虑对于任何语言L,我们可以定义一些函数f,如果一些字符串x在L中,则函数f的值为1 ,否则为0。所有这些功能是不同的。因为与所有语言的集合有1:1的对应关系,并且因为所有语言的集合都是不可数的,所以我们知道所有这些函数的集合是不可数的。
这是深刻的一点:
由于所有有效程序的集合都是可数的,但函数的集合是不可数的,所以必定有一些函数我们不能编写程序。
我们还不知道这些函数或问题是什么样的,但我们知道它们存在。这是一个令人谦卑的认识,因为有一些问题没有解决。我们认为电脑是非常强大和有能力的,但有些东西甚至超出了他们的范围。
现在问题变成了“这些问题是什么样子的?”在我们继续描述这些问题之前,我们必须首先以一种普遍的方式来模拟计算。
计算机的第一个数学模型之一是由Alan Turing开发的。这个称为图灵机的模型是一个非常简单的设备,完全可以捕捉我们的可计算性概念。
机器的输入是输入已写入的磁带。使用读/写头,机器通过一系列步骤将输入转换为输出。在每一步中,都要做一个决定:要写什么,要写什么,要不要把它左右移动。这个决定是基于两件事:
而已。
普遍性
1926年,阿兰图灵不仅开发了图灵机,而且还在他的著名论文““On Computable Numbers”上,对计算的性质有了许多其他重要的见解。他意识到,计算机程序本身可以被认为是计算机的输入。有了这个观点,他有了一个漂亮的想法:图灵机可以模拟或执行输入。
Church-Turing Thesis
在我们继续之前,让我们来看一个重要的观点:我们知道图灵机是计算的一个模型,但它是否足够普遍?为了回答这个问题,我们转向 Church-Turing Thesis,其相信下面的陈述:
一切可计算的东西都可以通过图灵机来计算。
在图灵开发图灵机作为计算模型的同时,阿朗佐教会也开发了一种称为lambda演算的计算模型。这些模型是强大的,因为它们都描述计算,并且以与当今任何计算机或任何计算机相同的方式进行计算。这意味着我们可以使用图灵机来描述我们所寻求的无法解决的问题,因为我们知道我们的发现将适用于所有可能的计算机过去,现在和以后。
可识别性和可判定性
在我们具体描述一个无法解决的问题之前,我们不得不覆盖更多的背景知识,即语言识别器和语言决策者的概念。
一个语言是可识别的,如果有一个图灵机识别它。 和 一个语言是可判定的,如果有一个图灵机决定它。
为了成为一种语言的识别器,图灵机必须接受语言中的每一个字符串,并且不能接受任何不属于该语言的东西。它可能拒绝或循环这样的字符串。要成为一个决定者,图灵机必须始终停止输入,要么接受要么拒绝。
在这里,停止输入的想法是至关重要的。事实上,我们看到决策者比识别者更强大。此外,一个问题是可以解决的,换句话说,只有存在决定函数描述的语言的图灵机时,函数才是可判定的。
不可判定性
如果你曾经写过一个计算机程序,当程序执行的时候,你一定知道坐在那里只是看着计算机旋转它的轮子的感觉。您不知道该程序是否花了很长时间,或者代码中有错误导致无限循环。你甚至可能想知道为什么编译器不检查代码,看它是否会在运行时停机或循环。
编译器没有这样的检查,因为它根本无法完成。并不是编译器工程师不够聪明或缺乏资源; 根本不可能检查任意的计算机程序以确定它是否停止。
我们可以使用图灵机来证明这一点。图灵机可以被描述为字符串,所以有一个可数的数字。假设M 1,M 2等组成了所有图灵机的集合。让我们定义下面的函数:
f(i, j) = 1 if Mi accepts <Mj>, 0 otherwise
这里,<M>是“M的字符串编码”的语法,并且这个函数表示如果M i通过接受M j作为输入而停止,则输出1,否则输出0的问题。请注意, Mi 必须停止(即成为决定者)。这是必需的,因为我们希望描述一个不可解释的函数(即无法解决的问题)。
现在, 让我们也定义一种语言 L, 它由不接受自己描述的图灵机的字符串编码组成:
L = { <M> | M does not accept <M> }
例如,一些机器M 1可能在输入<M 1 > 上输出0,而另一个机器M 2可能在输入<M 2 > 上输出1 。为了证明这种语言是不可判定的,我们会问决定语言L的机器M L是什么,当它给出自己的描述<M L >作为输入时。有两种可能性:
M L接受<M L > 要么 M L拒绝<M L >
如果M L接受自己的编码,那么这意味着<M L >不在语言L中; 但是,如果是这样的话,M L本来就不应该接受它的编码。另一方面,如果M L不接受自己的编码,那么<M L >是语言L,所以M L应该接受它的字符串编码。
在这两种情况下,我们都有矛盾,或者用数学术语来说是一个矛盾,证明L语言是不可判定的。因此,我们已经描述了我们的第一个无法解决的问题。
停机问题(Halting Problem)
虽然我们刚刚描述的问题似乎并不相关,但可以将其归结为具有实际重要性的其他无法解决的问题,最明显的是停机问题:
图灵机编码的语言停在空串上。
停机问题适用于为什么编译器无法检测到以前的无限循环的问题。如果我们不能确定程序是否在空字符串上终止,那么我们怎么可能确定它的执行是否会导致无限循环呢?
在这一点上,似乎我们只是挥挥手来得出一些简单的结论;然而,我们实际上已经意识到,没有一个图灵机可以告诉计算机程序是永久停止还是停留在一个循环中。这是实际应用中的一个重要问题,在图灵机或任何其他类型的计算机上都无法解决。iPhone不能解决这个问题。具有多个内核的桌面无法解决此问题。云无法解决这个问题。即使有人要发明量子计算机,也无法解决停机问题。
在我们对可计算性理论的研究中,我们已经看到了如何有许多函数是不可计算的。我们通过计算精确地定义了我们的意思,从他自己的笔和纸的经验一直回到图灵的灵感来正式化图灵机。我们已经看到了这个模型如何计算任何今天的计算机或者明天可以预见的计算机,并且我们意识到一类根本不可计算的问题。
可计算性还有一个缺点。只是因为我们可以解决问题并不意味着我们可以快速解决问题。毕竟,如果计算机在未来几千万年的太阳升起之前还没有完成,那么计算机有什么好处呢?
离开可计算的功能和语言背后,我们现在讨论计算的复杂性,调查有效的计算和着名的P对NP问题。
计算机科学家认识到各种各样的问题,我们关心的两个类别包括计算机可以快速或有效地解决的问题,称为P,问题的解决方案可以快速验证,但无法快速获得,称为NP。
例如,假设你负责开发在线约会服务的算法,并且有人提出了“每个人都可以约会”的问题。答案归结为将兼容的个人配对,使每个人都配对。原来有解决这个问题的高效算法。这个问题在P集中。
那么,如果我们想确定我们用户中最大的集团呢?我们所说的集团,是指所有相互兼容的最大的个人网络。当用户数量低时,这个问题可以很快解决。我们可以很容易地识别出3个用户的派系。然而,当我们开始寻找更大的集团时,问题变得越来越难以解决。这个问题在NP中。
P是多项式时间可解的一组问题。也就是说,计算步骤的数量由关于问题大小的多项式函数限定。我们知道“人人都可以约会吗?”这个问题,也称为双边匹配问题,在P中。
NP是在多项式时间内可验证的一组问题。这当然包括P中的每个问题; 但是,我们不知道这个遏制是否严格。我们知道可以有效验证但不能有效解决的问题,但是我们不知道问题是否真的难以解决。集团问题就是这样一个问题。我们知道我们可以高效地验证解决方案,但是我们不确定能否有效地解决问题。
最后,NP-complete是NP中最难解决的一系列问题。他们被认为是最难的,因为NP中的任何问题都可以有效地转化为NPC。结果是,如果有人能够找到有效解决NPC问题的办法,那么整个NP类将被P所吸收。集团问题也在NPC中。
因此,我们得出P与NP的问题。许多计算机科学家和数学家坚信,P和NP是不相等的。如果是的话,其影响将是深远的。现在的大部分数字基础设施都依赖于NP中没有P中存在的问题。如果情况并非如此,那么例如,密码方法就会在一夜之间崩溃,从而使一个拥有有效解决NPC问题的人甚至可以破坏最严密的安全协议。
对于计算机科学新手来说, 匹配和派系问题之间的区别似乎不是什么大不了的事情。事实上, P 中的问题和 NP 中的问题之间的区别是非常微妙的。能够辨别差异对于任何在现实世界中设计算法的人来说都很重要。
考虑最短路径问题。给定两个位置,目的是确定它们之间的最短路径。iPhone在几毫秒内计算出来。这是一个计算上易于处理的问题。
另一方面,考虑一下旅行推销员的问题,目标是在尽可能短的距离内访问一个可能的目的地的子集。这个问题类似于最短路径问题,但是NP-complete;这也解释了为什么供应链物流是一个10亿美元的产业。
我们其实可以更微妙。不要求最短路径(P),我们可以要求没有周期的最长路径。发现最长的路径问题也是NP-complete。
这个微妙的区别还有更多的例子,包括在二分图与一般图中的顶点覆盖的确定,或者在每个子句中有两个与三个文字的布尔公式的满意度。问题在于P或NP中是否存在问题并不明显,这就是为什么运行时间分析是关键技能的原因。如果必须设计的算法是针对P中的问题,那么我们知道有一个有效的解决方案。另一方面,如果问题是NP,那么我们就有一个强有力的理由来反对寻求解决方案,因为一般来说,这个算法会花很长时间来解决这个问题。
在这个复杂性的考察中,我们定义了问题P和NP的类别。P非正式地表示可以通过计算机有效解决的问题,而NP表示可以有效验证的问题。
没有人能够证明P不等于NP。这两类问题是否等价,就是众所周知的P vs. NP问题,而且它是当今理论计算机科学中最重要的开放问题,如果不是所有的数学问题的话。事实上,在2000年,克莱数学研究所将P vs. NP问题列为数学中七个最重要的开放问题之一,并提供了数百万美元的奖金,以证明这个问题的解决方案。
在这篇文章中,我们深入到可计算性和复杂性的领域,回答诸如“什么是电脑”这样的大问题。虽然细节可以压倒一切,但值得记住的是一些深刻的东西:
比细节更重要的是思考计算和计算问题的方法。在我们的职业生涯中,甚至在我们的日常生活中,我们可能会遇到以前从未见过的问题,我们可以使用可靠的工具和技术来确定最佳的行动方案。
是否存在没有解决方案的计算问题?
由于所有有效程序的集合都是可数的,但函数的集合是不可数的,所以必定有一些函数我们不能编写程序。
什么是 Church-Turing Thesis?
Church-Turing Thesis指出,所有可计算的东西都可以通过图灵机来计算。
什么是 P versus NP问题?
确定每个计算问题的解可以在多项式时间内验证的问题也可以用多项式时间求解。