为什么算法的定义非得要求它既有限又必须终止呢?

摘要:第一次学算法时,大多数人都会觉得课本里的定义有点“严格过头”: 为什么一定要有有限条指令? 为什么一定要求对每个输入都终止? 难道一个“很像算法”的过程,只要偶尔不结束,就不算算法了吗?最短路,并查集,排序,难道还有终止不了的算法? 其实,
第一次学算法时,大多数人都会觉得课本里的定义有点“严格过头”: 为什么一定要有有限条指令? 为什么一定要求对每个输入都终止? 难道一个“很像算法”的过程,只要偶尔不结束,就不算算法了吗?最短路,并查集,排序,难道还有终止不了的算法? 其实,这些要求不是为了咬文嚼字,而是为了把“算法”这个概念和一般的程序、搜索过程、模糊做法区分开。没有“有限性”和“终止性”,我们就很难保证一个过程真的是一种明确、可执行、可用于解题的方法。 算法定义里的关键词 算法通常包含这些核心要素: 它是一个过程(procedure) 它由有限条指令组成(finite set of instructions) 它接收输入(input) 它产生输出(output) 它的执行是系统化、明确的(systematic execution) 它对每个输入都应该停机(halt on every input) 每条指令都应在有限时间内完成 每个合法输入对应的输出应当是唯一的 其中,最容易让人疑惑的,正是有限性和终止性。 为什么要有“有限性” “有限性”可以理解为三个层面: 指令的数量是有限的 每条指令本身能在有限时间内完成 输入的长度是有限的 这些要求的核心目的,是保证算法是一个可描述、可执行的机械过程。 代码规则有限 没有有限性,规则可能根本写不完。设想一个“过程”是这样定义的: 第1步处理输入为1的情况 第2步处理输入为2的情况 第3步处理输入为3的情况 …… 看起来它似乎在“处理所有输入”,但问题在于:这需要一份无限长的代码。这就不再是我们想要的算法了。算法不应该给每一个输入单独写一条规则,而是用有限的规则统一处理无限多可能的输入。 单个步骤有限 再看另一种情况。假设某一步写成: “检查所有自然数都不满足某性质,然后再继续下一步。” 这条指令听起来很清楚,但它本身就需要无限时间,因为“检查所有自然数”永远做不完。 也就是说,即使总共只有三五步,只要其中某一步本身无法在有限时间内完成,这个过程也不能算算法。 为什么要有“终止性” 终止性指的是:对每一个输入,算法都必须在有限时间内结束。 这个要求的意义非常直接:如果一个过程永远不结束,那么你就永远得不到答案。比如下面这个过程: while x != 0: x = x + 1 如果输入 x = 1,那么变量会变成: 1 → 2 → 3 → 4 → 5 → ... 它永远不可能回到 0,因此程序永远不会结束。这个过程即使写得很明确、每一步都能执行,也仍然可能不终止。而一旦不终止,它就不满足严格意义上的算法定义。 更常见的反例:有解时能停,没解时不停 还有一种更有代表性的情况: 设想我们要判断某个方程是否有整数解,于是设计如下过程: 枚举所有可能的整数对 检查它们是否满足方程 一旦找到解就输出并停止 这个过程很有用,但它有个问题: 如果方程有解,它迟早能找到,于是停机 如果方程无解,它会一直枚举下去,永远不停 所以它并不满足“对每个输入都终止”的要求。 这类过程在理论计算机中很常见,它们不是严格意义上的算法,而更像是一种半判定过程。 有限性和终止性的意义 这两个要求,其实是在防止三类“伪算法”混进来。 第一类:无限长说明书:对每个输入单独写一条规则,导致整个“方法”无法用有限方式描述。 第二类:单步无限耗时:表面上步骤不多,但其中某一步本身就永远做不完。 第三类:永远不给答案的程序:过程确实开始执行了,也一步一步在跑,但在某些输入上会陷入无限循环,永远得不出结果。