计算复杂性理论

计算复杂性理论（Computational complexity theory）是理论计算机科学和数学的一个分支，它致力于将可计算问题根据它们本身的复杂性分类，以及将这些类别联系起来。一个可计算问题被认为是一个原则上可以用计算机解决的问题，亦即这个问题可以用一系列机械的数学步骤解决，例如算法。

如果一个问题的求解需要相当多的资源（无论用什么算法），则被认为是难解的。计算复杂性理论通过引入数学计算模型来研究这些问题以及定量计算解决问题所需的资源（时间和空间），从而将资源的确定方法正式化了。其他复杂性测度同样被运用，比如通信量（应用于通信复杂性），电路中门的数量（应用于电路复杂性）以及中央处理器的数量（应用于并行计算）。计算复杂性理论的一个作用就是确定一个能或不能被计算机求解的问题的所具有的实际限制。

在理论计算机科学领域，与此相关的概念有算法分析和可计算性理论。两者之间一个关键的区别是前者致力于分析用一个确定的算法来求解一个问题所需的资源量，而后者则是在更广泛意义上研究用所有可能的算法来解决相同问题。更精确地说，它尝试将问题分成能或不能在现有的适当受限的资源条件下解决这两类。相应地，在现有资源条件下的限制正是区分计算复杂性理论和可计算性理论的一个重要指标：后者关心的是何种问题原则上可以用算法解决。

## 简介

计算复杂性理论所研究的资源中最常见的是时间（要通过多少步演算才能解决问题）和空间（在解决问题时需要多少内存）。其他资源亦可考虑，例如在并行计算中，需要多少并行处理器才能解决问题。

时间复杂度是指在电脑科学与工程领域完成一个算法所需要的时间，是衡量一个算法优劣的重要参数。时间复杂度越小，说明该算法效率越高，则该算法越有价值。

空间复杂度是指电脑科学领域完成一个算法所需要占用的存储空间，一般是输入参数的函数。它是算法优劣的重要度量指标，一般来说，空间复杂度越小，算法越好。我们假设有一个图灵机来解决某一类语言的某一问题，设有$X$个字（word）属于这个问题，把$X$放入这个图灵机的输入端，这个图灵机为解决此问题所需要的工作带格子数总和称为空间。

复杂度理论和可计算性理论不同，可计算性理论的重心在于问题能否解决，不管需要多少资源。而复杂性理论作为计算理论的分支，某种程度上被认为和算法理论是一种“矛”与“盾”的关系，即算法理论专注于设计有效的算法，而复杂性理论专注于理解为什么对于某类问题，不存在有效的算法。

## 历史

在20世纪50年代，Trahtenbrot和Rabin的论文被认为是该领域最早的文献。而一般说来，被公认为奠定了计算复杂性领域基础的是Hartmanis和Stearns的1960年代的论文On the computational complexity of algorithms。在这篇论文中，作者引入了时间复杂性类$\text{TIME}(f(n))$的概念，并利用对角线法证明了时间层级定理（Time Hierarchy Theorem）。

在此之后，许多研究者对复杂性理论作出了贡献。期间重要的发现包括：对随机算法的去随机化（derandomization）的研究，对近似算法的不可近似性（hardness of approximation）的研究，以及交互式证明系统理论和零知识证明（Zero-knowledge proof）等。特别的复杂性理论对近代密码学的影响非常显著，而最近，复杂性理论的研究者又进入了博弈论领域，并创立了“算法博弈论”（algorithmic game theory）这一分支。

## 基本概念和工具

### 计算模型与计算资源

计算复杂性理论的研究对象是算法在执行时所需的计算资源，而为了讨论这一点，我们必须假设算法是在某个计算模型上运行的。常讨论的计算模型包括图灵机（Turing machine）和电路（circuit），它们分别是一致性（uniform）和非一致性（non-uniform）计算模型的代表。而计算资源与计算模型是相关的，如对图灵机我们一般讨论的是时间、空间和随机源，而对电路我们一般讨论电路的大小。

由邱奇-图灵论题（Church-Turing thesis），所有的一致的计算模型与图灵机在多项式时间意义下是等价的。而由于我们一般将多项式时间作为有效算法的标志，该论题使得我们可以仅仅关注图灵机而忽略其它的计算模型。

### 判定性问题和可计算性

我们考虑对一个算法问题，什么样的回答是我们所需要的。比如搜索问题：给定数组$A$，和一个数$s$，我们要问$s$在不在$A$中（判定性问题，decision problem）。而进一步的，$s$如果在$A$中的话，$s$的位置是什么（搜索型问题，search problem）。再比如完美匹配问题（perfect matching）：给定一个二分图$G=(V,E)$，我们问是不是存在边集$E$，使得二分图中每个结点恰好属于该边集的一条边（判定型问题）。而进一步的，$E$存在的话，$E$具体是什么（搜索型问题）。

自然的，我们会发现对于一般的算法问题$A$，我们都可以这样来问：首先，解是不是存在的？其次，如果解存在，这个解具体是什么？这就是$A$的判定型问题和$A$的搜索型问题（又称函数型问题）区分来源的直观解释。对判定型问题的回答只需是“是”或“否”，而对搜索型问题，需要返回解的具体形式或者“解不存在”。所以一个对$A$的搜索型问题的算法自然的也是对$A$的判定型问题的算法。反之，给定了一个$A$的判定型问题的算法，是否存在$A$的搜索型问题的算法，在可计算性理论和计算复杂性理论中有着不同的回答，这也是理解计算复杂性理论与它的前身可计算性理论不同的一个基本的观察。

在可计算性理论中，可以说明，判定型问题和搜索型问题在可计算性的意义下是等价的。而在计算复杂性中，Khuller和Vazirani在1990年代证明了在P≠NP的假设下，平面图4-着色问题的判定型问题是在P中的，而寻找其字典序第一的着色是NP难的。

所以在可计算性理论中，只关注判定型问题是合理的。在计算复杂性理论中，虽然一些基本的复杂性类（如P，NP和PSPACE），以及一些基本的问题（P和NP关系问题等）是用判定型问题来定义的，但函数型问题复杂性类也被定义（如FP，FNP等），而且一些特别的函数型问题复杂性类，如TFNP，也正在逐渐受到关注。

### 算法分析

上面提到计算复杂性理论的研究对象是执行一项计算任务所用的资源，特别的，时间和空间是最重要的两项资源。

我们用时间作例子来讨论算法分析的一些基础知识。如果将输入的长度（设为$n$）作为变量，而我们关注的是算法运行时间与$n$的函数关系$T(n)$。因为一个算法在不同的计算模型上实现时$T(n)$可能会有常数因子的差别（参见可计算性理论），我们使用大O表达式来表示$T(n)$，这使得我们可以忽略在不同计算模型上实现的常数因子。

以搜索这个计算任务为例。在搜索问题中，给定了一个具体的数$s$，和长度为$n$的数组$A$（数组中数的位置用1到$n$作标记），任务是当$s$在$A$中时，找到$s$的位置，而$s$不在$A$中时，需要报告"未找到"。这时输入的长度即为$n+1$。下面的过程即是一个最简单的算法：我们依次扫过$A$中的每个数，并与$s$进行比较，如果相等即返回当前的位置，如果扫遍所有的数而算法仍未停止，则返回"未找到"。

如果我们假设$s$在$A$中每个位置的几率都相同，那么算法在找到$s$的条件下需要$\frac{1}{n}(1+2+\cdots+n)=\frac{n(n+1)}{2n}=\frac{n+1}{2}$的时间。如果$s$不在$A$中，那么需要$(n+1)$的时间。由大O表达式的知识我们知道算法所需的时间即为$O(n)$。

而如果我们进一步假设$A$是已排序的，那么我们有二分查找算法，使得算法的运行时间是$O(\log n)$。可以看出执行一项计算任务，不同的算法在运行时间上是有很大差异的。

### 复杂性类

将计算问题按照在不同计算模型下所需资源的不同予以分类，从而得到一个对算法问题“难度”的类别，就是复杂性理论中复杂性类概念的来源。例如一个问题如果在确定性图灵机上所需时间不会超过一个确定的多项式（以输入的长度为多项式的不定元），那么我们称这类问题的集合为P（polynomial time Turing machine）。而将前述定义中的“确定性图灵机”改为“不确定性图灵机”，那么所得到的问题集合为NP（non-deteministic polynomial time Turing machine）。类似的，设$n$为输入的长度，那我们可以定义“在确定性图灵机上所需空间不超$O(\log n)$的算法问题的集合”（即为$L$），“存在深度为$O(\log n)$，输入的度（fan-in）为$O(1)$的电路族（circuit family）的算法问题的集合”（即为NC1）等等复杂性类。

定义复杂性类问题的目的是为了将所有的算法问题进行分类，以确定当前算法的难度，和可能的前进方向。这是复杂性理论的一个主线之一：对算法问题进行抽象和分类。例如透过大O表达式，我们可以对忽略因计算模型不同而引入的常数因子。而第二个重要的理论假设，就是将多项式时间作为有效算法的标志（与之对应的是指数时间）。这样，复杂性类使得我们可以忽略多项式阶的不同而专注于多项式时间和指数时间的差别。（对多项式时间作为有效算法的标志这一点是有一定争议的，比如，如果算法的运行时间$n^{10}$，那它也可以看作是缓慢的，见理论与实践。）在本文的其余章节，“有效算法”等价于“多项式算法”

### 归约

归约（reduction）是将不同算法问题建立联系的主要的技术手段，并且在某种程度上，定义了算法问题的相对难度。简单来说，假设我们有算法任务$A$和$B$，如果我们想说“$A$比$B$简单”（记为$A\leq B$），它应该是什么意思呢？从归约的观点来看，就是说如果我们有了$B$的有效算法$M$，那么我们有一个有效算法$N$，它可以引用$M$，最终它要解决$A$问题。

我们以点集覆盖问题（vertex cover）和独立集问题（independent set）为例来进行说明。这两个问题都是图论中的问题。假设给定了无向图$G=(V,E)$，和一个自然数$k$，点集覆盖问题是要找到$V$的子集$S$，使得对$\forall e\in E$，有$s\in S$，使得$s\in e$，且$|S|\leq k$；而独立集问题也是要找$V$的子集$S$，要求是$\forall s_1,s_2\in S, (s_1,s_2)\notin E$，且$|S|\leq k$。

一个简单的观察即是：对$G=(V,E)$，一个$S\subset V$是覆盖点集，当且仅当$S$在$G$的补图中是独立点集（而且保持集合大小）。利用这个观察，假设我们有了解决覆盖点集问题的算法$M$，我们设计解决独立点集的算法N如下：

- 算法$N$。
- 输入：给定无向图$G=(V,E)$，自然数$k$。
- 输出：一个大小小于等于$k$的独立点集（如果存在，否则返回“不存在”）。
- 已知：算法$M$，输入为(无向图$G$, 自然数$k$)，输出大小小于等于$k$的覆盖点集，如果这样的点集存在。否则返回“不存在”。
- 对$G$，产生$G$的补图$G'$。
- 调用$M$，输入为$(G',k)$。
- 如果$M$返回“不存在”，输出不存在。如果$M$返回$S\subset V$，输出$S$。

可以看出若产生补图这一步是有效的，那么如果$M$有效，$N$也是有效的。一般的，如果我们有一个$B$有效的算法$M$，和利用$B$作为“神谕”（oracle）的解决$A$问题的算法$N$，那么如果$N$是有效的，则我们有有效的解决$A$问题的算法$N'$——只需将$N$中查询B的操作换作具体的$M$算法即可。而这一性质的基本解释是：将多项式的不定元用另一个多项式代替，那么得到的仍是一个多项式。

所以从归约的观点来看，下面的说法可以看作与“$A$比$B$简单”（记为$A\leq B$）等价：

- $A$归约到$B$（reduces $A$ to $B$, or $A$ is reducible to $B$, or $A$ can be reduced to $B$）。
- 存在通过查询$B$问题来解决$A$问题的算法（there exists an algorithm that asks oracles of $B$, and solves $A$）。

## NP与P关系问题及相关理论

计算复杂性理论最成功的成果之一是NP完备理论。通过该理论，我们可以理解为什么在程序设计与生产实践中遇到的很多问题至今没有找到多项式算法。而该理论更为计算复杂性中的核心问题：P与NP的关系问题指明了方向。

### NP和P的定义

在上面我们已经知道，NP是指“在非确定性图灵机上有多项式时间算法的问题”的集合，而P是指“在确定性图灵机上有多项式时间算法的问题”的集合。这里我们都考虑的是判定型问题，即考虑一个语言$L$，我们要判断一个字符串$x$是不是在$L$中。那么，一个等价的理解是：NP是指对在$L$中的$x$，有多项式长度的证据$w$，而且对语言$(x,w)$是有多项式时间算法的；而P是指对$L$中的$x$，有多项式时间算法判断$x$在不在$L$中。

举个例子，就是考虑完美匹配问题、点集覆盖问题和图不同构问题。这三个问题都有图论背影，问题的描述如下：

- 完美匹配问题：给定图$G=(V,E)$，找到边的子集$F\subset E$，使得对任意的$v\in V$，存在唯一的$e\in F,v\in e$。
- 点集覆盖问题：给定图$G=(V,E)$，和自然数$k$，找到点的子集$U\subset V$，使得对任意的$e\in E$，存在$v\in U, v\in e$，且$|U|\leq k$。
- 图不同构问题：给定图$G=(V,E), H=(U,F), |G|=|H|$。我们说$G$和$H$是同构的，是指$\exists T:V\rightarrow U$，对任意的$s, t\in V$，满足$E(s,t)=F(T(s),T(t))$（这里我们把边集$E$看作$V\times V\rightarrow \{0,1\}$的映射）。图不同构是问对$G$和$H$，是不是不存在这样的映射。

关于这三个问题，它们在复杂性理论中，目前的地位如下：

- 完美匹配问题：在P中。可以利用艾德蒙德算法得到$O(\sqrt V E)$运行时间的算法。
- 点集覆盖问题：在NP中，而不知道是否在P中。实际上，它是NP完备问题，给出它的多项式算法将意味着证明NP=P。它在NP中，原因是给定一个点的子集$U\subset V$，我们可以在多项式时间中验证这是否是一个满足$|U|\leq k$的点集覆盖：$U$的大小很好验证。然后只需对每一条边$e$，遍历$U$中每一个元素$v$，检查是否有$v\in e$即可。运行时间至多为$O(VE)$。
- 图不同构问题：在AM中，而不知道是否在NP中。它之所以困难，一个直观的想法是：给定两个图$G$和$H$，首先这个问题的“证据”很难定义——不像点集覆盖问题中，一个解就是一个点集，而且点集大小$\leq k\leq |V|$是多项式大小。这里最直接的证据的定义，是说必须遍历所有的映射$T:V\rightarrow U$，并对所有的映射验证是否满足同构的定义。而这样一个证据是指数大小的。

这样我们有了：在P中、在NP而不知道是不是在P中、在AM中而不知道是不是在NP中的三个问题。

### NP与P关系问题

<image>

由于在多项式时间可以判断$x$在不在$L$中，蕴含着$x$本身就是其在$L$中的证据的含义，所以P⊂NP。这个包含关系是不是严格的呢？或者说，是不是有语言$L\in$NP，使得$L\notin$P？这就是著名的NP与P关系问题。从这个问题在1970年代被正式的提出之后，有NP完备理论赋予了它在实践上的重要性，有证明复杂性理论赋予了它纯数学理论上的重要性，有PCP理论和NP完备理论赋予了它算法理论上的重要性。这些理论或者在根本上依赖于NP和P关系问题的某些假设，或者本身就是试图去理解NP和P关系问题而发展出来的，这使得它成为了理论计算机科学乃至数学的中心问题之一。在2000年，克雷数学研究所 提出了新世纪的数学中七个中心问题，NP与P关系问题就是其中的一个。

关于NP与P关系问题最早发展出的理论是NP完备理论。我们在下面一节简单了解NP完备理论。

### NP完备理论

由上面归约的知识我们知道，算法问题之间可以根据归约来定义相对的难度。即对问题$A$和问题$B$，我们认为$A$比$B$简单，记为$A\leq B$，就是存在使用$B$问题解来解决$A$问题的算法$M$，且$M$是多项式时间的。那么，在一个复杂性类中，有没有可能存在“最难的问题”呢？具体的对NP，就是说是不是存在问题$A\in$NP，使得对$\forall B\in$NP，有$B\leq A$呢？对这样的问题，我们称它是NP完备的。

这个问题乍看起来很不容易把握。因为这需要对所有的NP中的语言L，去找到一个L到A的归约算法。然而1970年代的由史蒂芬·库克和列昂尼德·列文分别发现的库克-列文定理，证明了布尔表达式（Boolean formula）的可满足性问题（SAT问题）是NP完备的。概括的说，他们证明了，有一个通用的过程对NP中任意语言在非确定性图灵机上运行历史用布尔表达式来编码，使得该布尔表达式是可满足的，当且仅当该运行历史是对给定输入，接受该输入的。这样，我们就有了第一个被证明是NP完备的问题。

在库克给出SAT问题是NP完备之后不久，理查德·卡普证明了21个图论、组合数学中常见的问题都是NP完备的。这赋予了NP完备问题在实践中的重要性。现在，已经有成千个在实践中遇到的算法问题被证明是NP完备的（参见NP完备问题列表），特别的有许多问题，如旅行商问题等的最优算法会带来很大的经济效益（旅行商问题的最优解可以给出最优的电路布线方案，而SAT的最优算法会促进程序验证等问题的进步）。由NP完备的定义，我们知道对这其中任何一个问题的多项式算法都将给出所有NP问题，也包括所有NP完备问题的多项式算法。然而尽管实际问题中遇到很多NP完备的问题，而且有很多问题在不同领域有着相当的重要性而被大量研究，至今，仍没有对NP完备问题的多项式算法，这是一些理论计算机科学家认为NP≠P的理由之一。

对NP和P关系问题，NP完备理论给出了如下的暗示：如果要证明NP=P，一个可能的方向是对NP完备问题给出多项式算法；如果要证明NP≠P，那么必然的一个结果是NP完备问题没有多项式算法。

### 电路复杂性

电路复杂性理论在1990年代以前，被众多研究者认为是解决NP与P关系问题的可能的途径之一。电路复杂性研究的对象是非一致性的计算模型电路，并考虑计算一个布尔函数所需的最小的电路的深度（depth）和大小（size）等资源。其中，大小为多项式大小的电路族可以计算的布尔函数被记为P/poly。可以证明，P包含在P/poly之中，而卡普-利普顿定理（Karp-Lipton theorem）表明若P/poly在NP之中，则多项式层级（polynomial hierarchy）将会坍缩至第二层，这是一个不大可能的结果。这两个结果结合起来表明，P/poly可以当作是分离NP与P的一个中间的工具，具体的途径就是证明任一个NP完全问题的电路大小的下界。在直观上说，电路复杂性也绕过了NP与P问题的第一个困难：相对化证明困难（relativizing proofs）。

在1980年代，电路复杂性途径取得了一系列的成功，其中包括奇偶性函数（Parity function）在$AC^0$中的下界为指数，以及团问题（clique problem）在单调性电路（monotone circuit）中的下界为指数。然而在1994年Razborov和Rudich的著名论文自然性证明（Natural proof）中指出，上面所用证明电路下界的方法，在单向函数存在的前提下是不可能分离NP和P的。该结果使很多专家对证明电路下界来分离NP和P的前景表示不乐观。

### 其它NP与P关系问题相关的理论

- 去随机化理论：包括伪随机数发生器（pseudo random number generator）和extractor的构造等。
- 不可近似性：以PCP定理为基础，基于NP≠P和更强的唯一性游戏假设（Unique game conjecture），可以证明对一些问题不存在某近似比的近似算法。

## 理论与实践

计算复杂性的初衷是理解不同算法问题的难度，特别的是一些重要算法问题的困难性。为了确切的描述一个问题的困难性，计算复杂性的第一步抽象是认为多项式时间是有效的，非多项式时间是困难的。这基于指数函数增长速度的“违反直觉”的特性：如果一个算法的时间复杂性为$2^n$，取输入的规模是100，在运算速度是$10^{12}$每秒（关于CPU速度，参见Instructions per second，其中报告截止2009年，主流个人电脑的运算速度可以看作是$4\times 10^{10}$每秒）的情况下，该程序将会运行$4\times 10^{10}$年，几乎是宇宙年龄。这为多项式时间被看作是有效时间提供了直观上的证据。

然而多项式的指数很大的时候，算法在实践中也不能看作是有效的。如$n^{10}$的多项式算法，取问题规模大小为1000，那么几乎就是$2^{100}$的大小。另一方面，即便一个问题没有多项式算法，它可能会有近似比很好的近似算法（参见近似算法），或有很好的启发式算法（heuristics）。启发式算法的特点是在理论上没有精确的行为的分析，或者可以表明存在很坏的输入，在这些输入上运行很慢。然而在大多数时候，它都能快速解决问题。计算复杂性中对应的理论分析是平均复杂性理论（average-case complexity theory）和光滑分析（smooth analysis）。实际中的例子包括、布尔可满足性问题（参见SAT solver）和背包问题。

## 外部链接

- The Complexity Zoo
- What are the most important results (and papers) in complexity theory that every one should know?
- Scott Aaronson: Why Philosophers Should Care About Computational Complexity