前言:学习笔记对应的书籍为 Sanjeev Arora and Boaz Barak 的 Computational Complexity: A Modern
Approach,可以在这里下载电子书的 pdf 文件。也可以加我的 qq 2589436581 讨论书籍和笔记的内容。
第一章 计算模型
1.1 一些约定
设 是从 01 字符串到 01 输出的函数,称其为 01 函数。这个函数可以用一个集合 来描述,并把这个集合叫做语言或者决策问题(decision problems)。
记号: 表示一个二元组。
举个例子:对于无向图的最大独立集问题,它对应的语言就是 。
记号: 表示 的二进制表示。
对于一个算法,一般用 表示再输入长度为 的前提下,算法运行完毕所需要的最大基本操作次数。
复杂度记号: 表示 不超过 的阶, 反之, 表示 与 同阶, 表示 的阶低于 , 反之。
1.2 对计算和效率建立模型
先来一个不太严谨的说法:对于一个 的算法就是一系列规则。每条规则必须是固定的,但是对规则的使用次数没有限制。每条规则涉及以下一种或多种基本操作:
-
从输入(input)读一个 bit。
-
从写字板(scratch pad)或者一个工作空间(working space)读一个 bit。
-
根据读入的结果,在写字板上写一个 bit。
-
根据读入的结果,或是停止并输出答案 0/1,或是选择下一条需要使用的规则并继续。
注意:任何给定的字符集都可以和固定长度的 0/1 字符串构造双射,所以上文中的一个 bit 也可以是某个给定的字符集中的一个字符。
1.2.1 图灵机(Turing Machine,简称 TM)
纸带图灵机是上文不严谨的说法的一个具体实现。它有一个字符集 。纸带全部都是单向的(这里没搞懂单向的意义,也可能是我理解错了,原文 A tape is an infinite one-directional line of
cells...),有无穷个单元,每个单元可以存放一个 中的字符。每个纸带还有一个带头,可以每次对单元写入或读入一个字符,也可以左右移动。
第一个纸带是输入纸带,不允许写入字符。其他 个纸带叫做工作纸带,可读可写。最后一个纸带还是输出纸带,输出最终答案。
图灵机有一个有限的状态集合 ,以及一个可以用于存放一个状态的寄存器(register),表示当前所处状态。当前状态决定了下一步要进行的操作,操作分为以下几种:
-
读入 个带头下面的字符。
-
对于可写的 个纸带,同时在每个带头下面写入新的字符(如果你想保持不变的话,就写入与之前相同的字符)。
-
改变寄存器里的状态。
-
将每个带头向左或向右移动或者不动。
图灵机的严格定义:
一个 TM 可以用三元组 描述。
是一个字符集。一般假设其中至少有一个表示空的字符 ,和一个表示开始的字符 和数字 。
是一个状态集。一般假设其中至少包括一个 和一个 。
是一个函数,描述 的映射。这个链接 里说一般的图灵机是不允许 (stay)的,只有 ,只有一些变体允许 。这本书之前说必须左移或右移,这里又写了 ,只能说作者太不仔细了,当然这并不影响图灵机的本质。
TM 的初始配置(configuration)是这样的:所有带头在最左端,每个纸带最左端是 ,读入纸带的后面是输入内容,再后面为空;其他纸带的后面为空。初始状态在 。当状态到达 时即停机。在复杂性理论中,我们只关心对于所有输入都在有限步内停机的算法。
接下来我们正式地定义算法的运行时间。如果对于任意输入 ,一个 的算法 能在 步以内停机并正确输出 ,则称 能以 时间计算 。
时间可构函数:对于 ,若 ,且存在图灵机 能以 时间计算 的函数,则称 是时间可构的。
白话理解是:如果计算出 的复杂度都不止 ,那么这个复杂度就没有意义了。
1.2.2 图灵机定义的鲁棒性
注意到关于图灵机的许多定义是非常随意的。对工作纸带的数量 没有特别规定;对字符集 的大小没有规定,只要求必须含有 ,对纸带是否是双向无限长的都没有限制。这是因为可以证明能在 纸带图灵机上以 运行的算法一定可以以不超过 的操作次数在 纸带图灵机上运行;双向无限长的纸带上的 可以转化为在单向无限长的纸带上的 , 大小的字符集上的 可以转化为 上的 等等。这些证明都不难,所以就略去了。
Rmk. 纸带头的移动方式只和读入的长度有关,而与读入的内容无关的图灵机叫做健忘的图灵机(oblivious TM)。事实上,所有图灵机都可以被健忘的图灵机模拟。
1.2.3 图灵机的强大表达能力
图灵机可以模拟任意一个编程语言的任意一个程序;同时,大多数编程语言也可以模拟一台图灵机。
1.3 图灵机的字符串表示和通用图灵机
因为图灵机的字符集和状态集都是有限的,并且可以用转移函数来完整地描述这个图灵机,所以一定可以用一个 0/1 字符串来表示图灵机。同时,一个图灵机也可以被无限个 0/1 字符串表示(在末尾添加任意个无用的 1 即可,类似于程序的注释)。
下文中用 表示图灵机 的字符串表示, 表示 这个字符串所代表的图灵机。
1.3.1 通用图灵机
通用图灵机 可以以任意一个图灵机 作为输入来模拟这个图灵机。严谨来说,
Thm.(高效通用图灵机)存在图灵机 ,对于任意 ,有 。或者说,如果 对于一个 能在 步内停机,那么 将在至多 步内停机,其中 是一个只和 的各个参数相关的常数。
这个证明是比较 trivial 的。不妨设 只有一个工作纸带,那么只需要令 有一个功能和 完全相同的工作纸带,以及一个写有所有转移函数的信息的工作纸带和一个表示 当前所处的状态就可以了。
Rmk.(没看懂)考虑给 额外加入一个输入 ,并让 维护一个计数器,当且仅当 在 步内停机时输出 ,否则输出一个表示失败的符号。然后上面的证明可以小改一下给出具有同样复杂度的通用图灵机。(原文 It is sometimes useful to consider a variant of the universal TM that gets a number as an
extra input (in addition to and ), and outputs if and only if halts on within
steps (otherwise outputting some special failure symbol). By adding a counter to , the proof of
Theorem 1.13 can be easily modified to give such a universal TM with the same efficiency.)
1.4 不可计算的函数
Thm. 存在函数 不可被任何 TM 计算,即任何 TM 都无法对任意合法输入都在有限步内停机并得出正确结果。
Proof. 令 表示以 是否会在有限步内停机并输出 ,如果是,则 ,否则 。假设有一个图灵机 可以计算这个函数 ,注意到 ,然而根据定义 ,产生了矛盾,所以这个 是一个确实存在但不可能被计算出来的函数。
Rmk 1. 这个证明方法叫做“对角线法”(diagnoalization)
Rmk 2. 这里逻辑是没有问题的,我最初的理解不够深刻。当我们确定图灵机的编码规则之后,生成了一系列图灵机 ,自然 函数的所有点值都已经确定了。这些图灵机都有某种计算功能,我们称图灵机 能计算 当且仅当 ,这个证明是在说:不存在能在所有输入上都和 相等的图灵机。
当然这个函数太奇怪了,我们应该也不会想要去计算它。所以接下来我们将看到另一个有实际意义的不可计算函数:停机函数 ,它实际上是前面的 函数的一个更一般化的函数。
1.4.1 停机问题
停机问题以 两个参数作为输入,输出 是否会在有限步内结束,分别用 1/0 表示,下文不再重述。
Thm. 不可被任何 计算。
Proof. 假设存在图灵机 可以计算 ,记其为 。那么对于之前提到的 函数,我们可以如此构造一个可以计算 函数的图灵机 :对于一个输入 ,如果 ,那么输出 ,否则运行通用图灵机 ,计算 ,输出 。这与之前得到的“ 函数不可被计算”的结论矛盾。
Rmk. 这个证明方法叫“规约”(reduction),意思是如果一个能解决问题 的方法能被简单改造后解决问题 ,那么问题 至少是不弱于问题 的。
1.5 确定性时间和 类问题
使用某个给定资源可以计算的函数构成的集合叫做一个复杂度类。
Def.(DTIME 类)设 是一个函数。可以用 的时间计算的 01 函数( 是一个大于 的常数)构成的集合记作 。
Def.( 类)
关于 类问题的定义有一些经典的质疑:
-
要求最坏复杂度不超过多项式级别是否过于严苛?此外,有时求出近似解的复杂度会远远低于求出精确解的高复杂度,此时能否对 给出另外一种合理的定义?
-
在不同计算模型上的 类问题集合是否相同?
Hypothesis.(Church-Turing 命题)所有物理上可以实现的计算模型都可以用图灵机来模拟。
Hypothesis.(加强版 Church-Turing 命题)所有物理上可以实现的计算模型都可以用图灵机来模拟,并且其每一步操作都可以转化为图灵机上的关于 的多项式步操作。
如果这个命题成立的话,所有计算模型上的 类问题集合全部相同。
但是:
(1)精度问题。图灵机涉及的东西都是离散的,而现实世界是连续的,所以图灵机真的什么都能模拟吗?
(2)随机数能否加快图灵机的速度?
(3)量子计算。现在确切地存在一些问题是量子计算意义下的 但不是图灵机意义下的 ,但是量子计算机到底能不能在物理上造出来并不清楚。
(4)其他算法
- 有些计算问题并不能简单转化为判定问题(或者可能代价过高)。
章节总结:我们学了什么?
1. 计算模型很多,我们用图灵机作为代表,因为我们假定所有计算模型的能力是等价的;
2. 图灵机可以用 0/1 字符串表示,所以存在一个可以模拟任意图灵机的通用图灵机;
3. 存在一些不可能被图灵机计算出来的函数,如 函数;
4. 我们认为 类问题是可以高效解决的,因为图灵机的各类参数,如纸带数量,字符集大小等等不会改变 所包含的问题,所以这些参数都是无所谓的,我们证明一些命题的时候一般选取最符合我们要求的参数。
习题:
Exercise 8.
Question:证明 oblivious TM 的可行性,oblivious TM 的纸带头的位置只和输入的长度和经过的步数相关。具体来说,证明任意一个能在任意一个图灵机上以 运行的算法必然能在 oblivious TM 上以 运行。
Answer:这个看起来挺怪的,其实不难。不妨假设工作纸带只有 条,并且是单向的,设原来的图灵机是 ,新造的 oblivious TM 是 ,我们考虑在状态里额外维护一个 ,表示当前状态下,如果是在 中,下一个要读取的位置在哪里。在 中的每次移动一格纸带头的操作,在 中都改成扫描纸带向右 格的所有格子,通过 来判断哪一位的信息是真正要读取进来的。因为原本的复杂度是 ,所以新的复杂度不超过 。
Exercise 9.
Question:证明 Exercise 8 的加强版:把 改进到 。
书中给出了一个建造复杂度不超过 的通用图灵机的方法:
先假设原来的图灵机 只有 条纸带(否则可以通过扩充字符集来代替多条纸带),然后我们建造的通用图灵机 也只有 条纸带,字符集中有一个特殊符号 表示空白。这条纸带不妨设它是双向的,在正半轴上维护若干区间 ,在负半轴上 ,和一个原点 。这里 不超过 。我们还是把原来纸带上要写的东西按顺序写在这些区间里,只是未必连着写:时刻保证 这个位置有我们正在读入的字符,其他每个区间要么全满,要么全空白,要么恰好一半是有意义的字符,一半是空白。并且, 这两个区间的字符加起来正好是 个有意义的字符。初始状态下可以使所有区间都半满,多出来的用任意非空白的字符填上就行。
如此一来我们要进行纸带头向右移动 个单位的操作的时候,本质上就是让纸带上的有效字符都向左移动 个单位。现在跑得慢的原因是原本纸带头的移动一次的时候,现在都要把所有字符移动一位,复杂度直接乘上 。可以这样做:先找到最小的 满足 不是全空。然后将 最靠左的 个字符移动到 ,接下来靠左的 个字符放到 里面使得每个半满。把 (如果非空的话)的 个有意义字符都移到最靠左的 ,再把 (这些之前全满)里的靠左的 个字符左移到 中靠右的部分 ,最后把 中剩余的字符往 按照原来的顺序各填一半就可以了。注意我们的操作维持了我们需要维护的性质。并且,搞完一次 后,虽然我们大概用了 步操作,但是此时 都已经是半满状态了,再想要操作到 ,至少要再经过大约 步才可以。也就是说总共的步数大概是 。
但是书里给的这个方法细节上有问题: 长度都是 ,没法拿出一半。这个细节需要修正,也肯定可以修正,但是真的太烦了,我觉得只要知道核心思路是倍增就好了。这种图灵机的证明细节和实现细节都是重量级,真要写个 C++ 程序模拟这种转化 + 抽象了很多次的通用图灵机能把这世界上最能写的代码手整自闭。所以啊,图灵机这东西还是停留在理论上就够了,用已经封装好的现代计算机写高级语言它不香吗???
不过其实还没结束呢。这我们只是造出了一个 的通用图灵机,怎么转化成一个能执行某个算法的 oblivious TM 呢?
破防了,大家看看这个 lecture notes 吧,反正我摆烂了
Exercise 15.
设 partial function 是定义在 的一个子集 上的值域为 的函数,称图灵机 能计算 ,当且仅当 有定义时 , 无定义时 永不停机对所有 成立。定义 是一些这样的 构成的集合,定义 当且仅当 能计算 中的某一个函数,否则 。
Thm.(Rice's Theorem)当 不平凡,即 不为空集或全集时, 不可计算。
Question:
(1)用 Rice's Theorem 证明停机函数 不可计算。
如果 可以计算,那么对于给定的一个 中的每个元素(函数)(注意到 是可数集),设计这样一个图灵机 :给定一个输入 ,依次跑遍所有 中的字符串 ,比较 是否和 相符。即,如果 没有定义,用停机函数 计算 是否会停机;如果 有定义,先检查 是否会停机,如果不会停机的话,再计算 。中间任何一步如果发现 的行为与 不相符,立刻停机。这一过程保证了如果 和 永远相符,则 在输入 上不会停机;否则一定停机。再基于此设计一个图灵机 , 得到一个输入 ,依次跑遍所有 中的元素 ,用停机函数 计算 是否会停机。如果在某个 上不停机,则停机并输出 。根据定义这个 可以计算 ,与 Rice's Theorem 产生矛盾,故 不可计算。
(2)证明 Rice's Theorem。
反证法。假设 Rices's Theorem 是错误的,即存在一族函数 ,满足 可以被计算。不妨设 中不存在 函数, 函数的定义域是空集。若不然,可以将 变成 对全体函数的补集,在这两个集合上做判定显然是等价的。并且因为 非空非满,所以还存在一个函数 。接下来我们这样构造一个计算 函数的图灵机 :输入参数 ,再构造一个图灵机 , 输入一个参数 ,执行的操作是先模拟一遍 ,再根据 是否存在选择输出 或死循环。 执行的操作是求出 。由于 是一个确定的图灵机 + 输入,我们来考虑 的实际功能只可能有两种:要么 停机,那么 能计算 ,此时 ;要么 不停机,那么 就是一个对任何输入都不停机的图灵机,能计算 ,此时 。所以根据 我们能够判定 是否会停机,这与之前得出的停机函数不可计算的结论产生了矛盾,假设不成立。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?