CS170 · 第 9 讲

NP 完全问题

本章是全书的转折点:在经历了七章高效算法之后,我们直面那些历经数十年仍未找到多项式时间解法的计算问题。我们将建立 NP 完全性理论,揭示这些看似迥异的难解问题本质上是同一问题的不同伪装,并展示如何通过多项式时间归约将它们串联成一个紧密的网络。

预计用时:150 分钟难度:困难Algorithms · Graph Algorithms · Dynamic Programming · Linear Programming

学习目标

先修回顾

搜索问题导论:指数爆炸与多项式效率

本章的核心研究对象是搜索问题(search problem):给定一个实例 I(输入数据),寻找一个满足特定规格的对象 S(解),或报告不存在这样的解。

本书前七章讨论的最短路径、最小生成树、最大流、最大匹配等问题,虽然候选解空间呈指数级增长——\(n\) 个男孩与 \(n\) 个女孩有 \(n!\) 种匹配方式、\(n\) 个顶点的图有 \(n^{n-2}\) 棵生成树——但都找到了巧妙绕过穷举搜索的高效算法。这些算法的运行时间是输入大小的多项式函数(如 \(n\)、\(n^2\)、\(n^3\))。

然而本章引入的新问题,至今未发现任何显著优于穷举搜索的多项式时间算法。为了理解这种困难性的深远意义,我们先考察指数增长的威力。

指数增长的寓言:根据传说,国际象棋发明者 Sissa 请求国王在棋盘第一格放 1 粒米、第二格 2 粒、第三格 4 粒,依此类推翻倍至第 64 格。总数为

\[2^{64} - 1 = 18\,446\,744\,073\,709\,551\,615\]

足以将印度铺满好几遍。Malthus 由此论证人口的指数增长终将耗尽线性增长的资源;Moore 则观察到芯片晶体管密度每 18 个月翻番,这便是 Moore 定律。

摩尔定律的两面性:表面上看,硬件的指数级进步似乎让指数时间算法变得可接受——只需等待即可。但事实恰恰相反。假设一个 \(O(2^n)\) 的布尔可满足性(SAT)算法运行 1 小时,1975 年可解 25 个变量、1985 年 31 个、1995 年 38 个、如今约 45 个——每增加一个变量需等待约 1.5 年,而应用需求的增速远超于此。相比之下,\(O(n)\) 或 \(O(n\log n)\) 算法每十年可处理的问题规模乘以约 \(100\) 倍;\(O(n^2)\) 算法乘以约 \(10\) 倍;即便是 \(O(n^6)\) 这样不够优雅的多项式算法,每十年也能将可解规模翻倍有余。

这一反转揭示了一个深刻事实:指数算法在摩尔定律下仅能取得多项式级别的进展,而多项式算法则能取得指数级别的进展。要让摩尔定律真正转化为解决问题的能力,我们需要高效的多项式算法。

搜索问题的形式化:

一个搜索问题由以下性质定义:对于实例 I,存在一个验证算法 \(C(I, S)\),当且仅当 S 是 I 的解时输出 \(\text{true}\),且 \(C\) 在 \(|I|\) 的多项式时间内运行。此外,解 S 的长度必须是 \(|I|\) 的多项式(以保证可读性)。

以 SAT 为例:\(I\) 是一个合取范式(CNF)布尔公式,\(S\) 是一个真值赋值,验证算法只需检查 \(S\) 是否使每个子句中至少一个文字为 \(\text{true}\),这显然是多项式时间的。

推导示例

工作示例:摩尔定律下不同复杂度算法的可解规模对比

假设某固定计算任务耗时 1 小时,比较不同复杂度算法在硬件每 10 年提速约 \(100\) 倍(对应摩尔定律)时可处理的问题规模 \(n\):

  • \(O(2^n)\) 算法:每 10 年规模仅增加约 \(\log_2(100) \approx 6.6\)。从 1975 年的 \(n=25\) 到 2025 年仅增至约 \(n=25 + 5 \times 6.6 \approx 58\)。进展极其缓慢。

  • \(O(n)\) 算法:规模乘以 \(100\)。1975 年 \(n=25\),1985 年 \(n=2500\),1995 年 \(n=250\,000\),呈爆炸式增长。

  • \(O(n^6)\) 算法:规模乘以 \(100^{1/6} \approx 2.15\)。每十年仍能翻倍,远优于指数算法。

这直观展示了为什么指数算法在实际中几乎无用,而多项式算法能充分利用硬件进步。

从 SAT 到 TSP:经典难解问题巡礼

本节系统介绍一系列重要的搜索问题,其中一些问题我们已在前面章节见过高效算法,而另一些则因微小的结构差异变得极其困难。

布尔可满足性 SAT 与 3SAT

SAT 的实例是合取范式(CNF)公式,即若干子句的合取,每个子句是若干文字的析取(文字 = 变量或其否定)。例如:

\[(x \lor y \lor z) \land (x \lor y) \land (\lnot y \lor z) \land (\lnot z \lor x) \land (x \lor y \lor z)\]

SAT 要求找到使每个子句至少有一个文字为真的赋值。对于 \(n\) 个变量,朴素算法需检查 \(2^n\) 种赋值。

SAT 有两个多项式时间可解的特例:Horn SAT(每个子句至多一个正文字,可用贪心算法求解)和 2SAT(每个子句两个文字,可通过强连通分量在线性时间求解)。然而一旦允许子句包含三个文字,得到的 3SAT 问题就再次变得困难。

旅行商问题 TSP

给定 \(n\) 个城市及其两两之间的距离 \(d_{ij}\),以及预算 \(b\),寻找一条恰好访问每个城市一次并返回起点的回路(tour),使其总长度 \(\leq b\)。TSP 原本是优化问题(求最短回路),但我们将其改写为搜索问题以便统一框架:优化版本可通过二分搜索调用搜索版本来求解(见习题 8.1)。

TSP 与最小生成树(MST)形成鲜明对比:MST 是多项式时间可解的,而 TSP 可视为 MST 的"困难表亲"——TSP 要求树不能分叉(即为一条路径/环),这一结构限制显著增加了复杂度。

Euler 路径与 Rudrata 环

Euler 路径访问每条边恰好一次,其多项式时间可解性由 Euler 刻划:连通且至多两个顶点度数为奇数。

Rudrata 环(Hamilton 环)访问每个顶点恰好一次,与 TSP 惊人地相似,至今未有多项式时间算法。Euler 问题与 Rudrata 问题的复杂度鸿沟源于"边 vs 顶点"的差异,而非"路径 vs 环"的差异(后者可通过简单归约等价化)。

割与平衡割

最小割(Min-Cut)可通过 \(n-1\) 次最大流计算在多项式时间求解。但平衡割(Balanced Cut)要求将顶点分为两个大小均 \(\geq n/3\) 的集合且割边数 \(\leq b\),这是一个困难问题,在图像分割等应用中有重要价值。

整数线性规划 ILP 与零一方程 ZOE

ILP 要求整数向量 \(x\) 满足 \(Ax \leq b\),即使 LP 有多项式时间算法,ILP 仍是困难的。ZOE 是 ILP 的特例:寻找 0-1 向量 \(x\) 使 \(Ax = \mathbf{1}\)(\(A\) 为 0-1 矩阵),其简洁形式在归约理论中极有价值。

3D 匹配

二分匹配(男孩-女孩)可归约为最大流而高效求解。3D 匹配引入第三维度(男孩-女孩-宠物),要求找出 \(n\) 个不相交的三元组覆盖所有成员,这一微小推广使其变为困难问题。

独立集、顶点覆盖、团

独立集(Independent Set)要求 \(g\) 个两两不相邻的顶点;顶点覆盖(Vertex Cover)要求 \(b\) 个覆盖所有边的顶点;团(Clique)要求 \(g\) 个两两相邻的顶点。三者在一般图上均为困难问题,在树上独立集可高效求解。

最长路径与背包问题

最短路径可高效求解,但最长路径(简单路径、总权重 \(\geq g\))是困难的。背包问题(Knapsack)的 \(O(nW)\) 动态规划在输入规模上是指数级的(因涉及 \(W\) 而非 \(\log W\))。子集和(Subset Sum)是背包的特例(价值=权重、目标=容量),同样困难。

下表总结了困难问题(NP 完全)与容易问题(在 P 中)的对比:

NP 完全(困难) P(容易)
3SAT 2SAT, Horn SAT
TSP 最小生成树
最长路径 最短路径
3D 匹配 二分匹配
背包 一元背包
独立集 树上的独立集
ILP 线性规划
Rudrata 路径 Euler 路径
平衡割 最小割
推导示例

工作示例:SAT 实例的验证与不可满足性分析

考虑 SAT 实例:\((x \lor y \lor z) \land (x \lor y) \land (\lnot y \lor z) \land (\lnot z \lor x) \land (x \lor y \lor z)\)。

验证算法 \(C(I, S)\) 的执行过程(设赋值 \(x=\text{true}, y=\text{true}, z=\text{true}\)): 1. 子句 \((x \lor y \lor z)\):\(x\) 为真 ✓ 2. 子句 \((x \lor y)\):\(x\) 为真 ✓ 3. 子句 \((\lnot y \lor z)\):\(z\) 为真 ✓ 4. 子句 \((\lnot z \lor x)\):\(x\) 为真 ✓ 5. 子句 \((x \lor y \lor z)\):\(x\) 为真 ✓

该赋值满足前四个子句但最后一个子句要求至少一个变量为真——等等,最后一个子句也是 \((x \lor y \lor z)\),在 \(x=\text{true}\) 时为真。实际上需检查所有赋值:中间三个子句 \((x \lor y)\)、\((\lnot y \lor z)\)、\((\lnot z \lor x)\) 迫使 \(x=y=z\),而结合首末两个子句的约束可证明不存在满足全部五个子句的赋值(提示:分析三个中间子句如何约束三者取相同值)。

此例展示了验证可在多项式时间完成(逐个子句检查),但寻找满足赋值可能需要穷举。

P 与 NP:多项式时间可解与可验证

为精确讨论计算复杂性,我们引入两个核心复杂性类。

P 类(Polynomial time)

P 是所有可在多项式时间内求解的搜索问题的集合。即存在算法,输入实例 \(I\),在 \(O(|I|^k)\) 时间内返回一个解 \(S\)(如果存在),或正确报告无解。前一章讨论的最短路径、最小生成树、最大流等问题均属于 P。

NP 类(Nondeterministic Polynomial time)

NP 是所有多项式时间可验证的搜索问题的集合。即存在验证算法 \(C(I, S)\),当且仅当 \(S\) 是 \(I\) 的解时输出 \(\text{true}\),且运行时间为 \(|I|\) 的多项式。

NP 名称源于"非确定性多项式时间":想象一种能在每一步正确猜测的非确定性算法,它能在多项式时间内找到一个解并验证。这等价于"多项式时间可验证"的搜索问题定义。

历史注记:NP 最初定义为判定问题(是/否问题)的类,源于形式语言理论的研究传统。搜索问题版本与其本质相通。

P 与 NP 的关系

显然 \(\text{P} \subseteq \text{NP}\):若一个问题可在多项式时间求解,则给出解后自然可在多项式时间验证(只需求解并比较)。核心问题是反向包含是否成立:\(\text{P} \stackrel{?}{=} \text{NP}\)。

绝大多数研究者相信 \(\text{P} \neq \text{NP}\)。理由包括:数十年来无数聪明头脑未能找到这些困难问题的多项式算法;数学定理的"寻找证明"是一个搜索问题且属于 NP——若 \(\text{P} = \text{NP}\),则将存在高效方法证明任何定理,数学家就不再被需要了。

然而证明 \(\text{P} \neq \text{NP}\) 本身极其困难,是数学和计算机科学中最重要的未解难题之一。

搜索问题的空间结构

假设 \(\text{P} \neq \text{NP}\),搜索问题的空间可想象为一个从易到难的谱系:底部是 P(多项式时间可解),顶部是 NP 完全(NP 中最难的问题),中间可能存在既非 P 也非 NP 完全的问题(如因数分解)。

推导示例

工作示例:多项式时间验证算法的执行

以 3SAT 为例。验证算法 \(C(I, S)\) 的伪代码:

function VerifySAT(I, S):
    // I = CNF 公式, S = 真值赋值
    for each clause C in I:
        clause_satisfied = false
        for each literal l in C:
            if l is variable x and S[x] == true:
                clause_satisfied = true
            if l is negated variable not-x and S[x] == false:
                clause_satisfied = true
        if clause_satisfied == false:
            return false
    return true

复杂度分析:设公式有 \(m\) 个子句、\(n\) 个变量,每个子句最多 3 个文字。外层循环 \(m\) 次,内层最多检查 3 个文字,赋值查找为 \(O(1)\)(数组索引)。总时间 \(O(3m) = O(m)\),是输入大小的多项式。

这展示了 NP 定义的核心:验证一个给定解是高效的,但寻找解可能需要穷举 \(2^n\) 种赋值。

归约与 NP 完全性

要证明某个问题没有高效算法,我们需要比"尚未发现算法"更强有力的证据。归约(reduction) 提供了这种证据。

搜索问题的归约

从搜索问题 A 到搜索问题 B 的归约由两个多项式时间算法组成:

必须满足:\(f(I)\) 有解当且仅当 \(I\) 有解;若 \(f(I)\) 无解,则 \(I\) 也无解。

归约的图示:

I ──f──> f(I)
│          │
│ h        │ 算法 for B
│          │
h(S) <── S ──

这意味着:任何求解 B 的算法都可被"夹在" \(f\) 和 \(h\) 之间,转化为求解 A 的算法。

归约的方向

若记归约为 \(A \to B\),则:

在本章中,我们使用归约的"反常"目的:已知 A 困难,通过 \(A \to B\) 证明 B 也困难。

归约的复合

归约具有传递性:若 \(A \to B\) 且 \(B \to C\),则 \(A \to C\)。复合归约的前处理为 \(f_{BC} \circ f_{AB}\),后处理为 \(h_{AB} \circ h_{BC}\)。这使得归约链可以高效地扩展 NP 完全性的证明范围。

NP 完全的定义

一个搜索问题是 NP 完全的(NP-complete),当且仅当所有其他搜索问题都可归约到它。这是一个极强的要求:NP 完全问题必须能用来解决世界上任何搜索问题!

NP 完全问题的存在令人惊讶,但确实存在。一旦我们证明某个问题是 NP 完全的,就可用它证明新的 NP 完全问题:只需将已知的 NP 完全问题归约到新问题即可。

归约证明困难性的逻辑

所有 NP 问题都归约到 NP 完全问题,因此每个 NP 完全问题都包含了所有搜索问题的复杂性。若任何一个 NP 完全问题属于 P,则 \(\text{P} = \text{NP}\)。既然我们相信 \(\text{P} \neq \text{NP}\),这些搜索问题就都是困难的。

因数分解的不同

因数分解(FACTORING)也是著名的困难问题,但其困难性质与 NP 完全问题不同。因数分解总是有解(任何整数都可分解为素数乘积),且未被相信是 NP 完全的。第 10 章将展示因数分解可被量子计算机高效求解,而 SAT、TSP 等 NP 完全问题似乎不能。

推导示例

工作示例:归约的结构与正确性验证

以 TSP 优化问题归约为搜索问题为例,展示归约的双向正确性:

  • 前处理 \(f\):给定距离矩阵 \(D\) 和预算 \(b\),搜索版本直接以 \((D, b)\) 为实例。

  • 后处理 \(h\):若搜索版本返回一个 tour \(T\),则 \(h(T) = T\)(直接输出)。

情况 1:若搜索版本有解(存在长度 \(\leq b\) 的 tour),则优化版本取其最短 tour,若其长度 \(\leq b\) 则返回,否则报告无解。

情况 2:若搜索版本无解(不存在长度 \(\leq b\) 的 tour),则优化版本的最短 tour 长度必 \(> b\),故无解。

多项式时间检查:\(f\) 只是复制输入,\(O(n^2)\);\(h\) 是恒等映射,\(O(1)\)。两者均为多项式时间。

这展示了归约的核心:两个方向的正确性(解的存在性对应)和多项式时间约束。

核心归约链(上):布尔逻辑与图论之间的桥梁

本节展示两个关键归约:3SAT 到独立集、SAT 到 3SAT,以及独立集与顶点覆盖、团之间的简单归约。

3SAT \(\to\) INDEPENDENT SET

这是连接布尔逻辑与图论的关键归约。给定 3SAT 实例 \(I\),构造独立集实例 \((G, g)\):

  1. 对每个子句(如 \((x \lor y \lor z)\)),在 \(G\) 中创建一个三角形,顶点标号为 \(x, y, z\)(两文字子句创建一条边)。
  2. 对任意两个代表相反文字(\(x\) 与 \(\lnot x\))的顶点,添加一条边。
  3. 设目标 \(g\) = 子句数量。

为什么这样构造?

解的回推:若独立集 \(S\) 有 \(g\) 个顶点,则对每个变量 \(x\):若 \(S\) 含标号 \(x\) 的顶点,令 \(x = \text{true}\);若含 \(\lnot x\),令 \(x = \text{false}\);否则任意赋值。此赋值满足所有子句。

反之,若有满足赋值,从每个子句选一个为真的文字加入 \(S\),则 \(S\) 是大小为 \(g\) 的独立集。

SAT \(\to\) 3SAT

这是"问题归约为自身特例"的典范。要将 SAT 归约为 3SAT,只需消除长文字子句。对子句 \((a_1 \lor a_2 \lor \ldots \lor a_k)\)(\(k > 3\)),引入新变量 \(y_1, y_2, \ldots, y_{k-3}\),替换为:

\[(a_1 \lor a_2 \lor y_1) \land (\lnot y_1 \lor a_3 \lor y_2) \land \cdots \land (\lnot y_{k-3} \lor a_{k-1} \lor a_k)\]

等价性证明:

进一步可限制每个文字至多出现两次:将变量 \(x\) 的 \(k\) 次出现替换为 \(x_1, \ldots, x_k\),并添加等价链 \((x_1 \lor x_2) \land (x_2 \lor x_3) \land \cdots \land (x_k \lor x_1)\)。

INDEPENDENT SET \(\to\) VERTEX COVER

基于补集关系:\(S\) 是顶点覆盖 \(\iff\) \(V \setminus S\) 是独立集。

因此,要判断图 \(G\) 是否有大小为 \(g\) 的独立集,只需检查是否存在大小为 \(|V| - g\) 的顶点覆盖。

INDEPENDENT SET \(\to\) CLIQUE

基于补图关系:\(S\) 是 \(G\) 的独立集 \(\iff\) \(S\) 是补图 \(\overline{G}\) 的团。

因此将实例 \((G, g)\) 映射为 \((\overline{G}, g)\),解完全相同。

推导示例

工作示例:3SAT 到独立集的构造与解的回推

考虑 3SAT 实例(4 个子句):

\[(x \lor y \lor z) \land (x \lor y \lor \lnot z) \land (x \lor \lnot y \lor z) \land (x \lor y \lor \lnot z)\]

步骤 1:创建三角形/边

  • 子句 1 → 三角形 \((x, y, z)\)

  • 子句 2 → 三角形 \((x, y, \lnot z)\)

  • 子句 3 → 三角形 \((x, \lnot y, z)\)

  • 子句 4 → 三角形 \((x, y, \lnot z)\)

步骤 2:添加矛盾边

  • \((y, \lnot y)\) 之间:子句 1 的 \(y\) 与子句 3 的 \(\lnot y\) 连边,等等。

  • \((z, \lnot z)\) 之间:子句 1 的 \(z\) 与子句 2 的 \(\lnot z\) 连边,等等。

步骤 3:设 \(g = 4\)(子句数量)

验证:若赋值 \(x=\text{true}, y=\text{false}, z=\text{true}\) 满足公式,则从每个子句选一个为真文字的顶点:

  • 子句 1:选 \(x\)(为真)

  • 子句 2:选 \(x\)(为真)

  • 子句 3:选 \(x\)(为真)或 \(z\)(为真)

  • 子句 4:选 \(x\)(为真)

选子句 1 的 \(x\)、子句 2 的 \(x\)、子句 3 的 \(x\)、子句 4 的 \(x\)——它们都是正文字 \(x\),彼此无边,构成独立集!故 \(S = \{x_1, x_2, x_3, x_4\}\) 是大小为 4 的独立集。

回推:\(S\) 全为正文字 \(x\),故令 \(x=\text{true}\),\(y, z\) 任意,验证满足所有子句。

核心归约链(下):从匹配、方程到环与旅行商

本节继续沿归约链深入:3SAT 到 3D 匹配、3D 匹配到 ZOE、ZOE 到子集和、ZOE 到 ILP,以及 ZOE 到 Rudrata 环再到 TSP。

3SAT \(\to\) 3D MATCHING

这是归约理论中最精巧的构造之一,需要用三元组模拟布尔变量和逻辑门。

变量 gadget:对每个变量 \(x\),创建两个男孩 \(b_0, b_1\)、两个女孩 \(g_0, g_1\) 和四个宠物 \(p_0, p_1, p_2, p_3\),组成四个三元组构成的"菱形"结构。任何匹配必须选择其中两组:\((b_0, g_1, p_0), (b_1, g_0, p_2)\)(表示 \(x=\text{true}\))或 \((b_0, g_0, p_1), (b_1, g_1, p_3)\)(表示 \(x=\text{false}\))。

子句 gadget:对子句 \(c = (x \lor y \lor z)\),创建新男孩 \(b_c\) 和新女孩 \(g_c\),添加三个三元组对应三种满足方式(\(x=\text{true}\)、\(y=\text{false}\)、\(z=\text{true}\)),每个三元组使用变量 gadget 中特定的宠物。若文字为真,对应宠物空闲可用;若为假,宠物已被占用,无法匹配。

收尾:未匹配的宠物通过"通用动物爱好者"夫妇来匹配。

3D MATCHING \(\to\) ZOE

ZOE 的 0-1 变量天然描述组合选择:对每个三元组 \(i\),设 \(x_i \in \{0,1\}\)(选中为 1)。对每个男孩/女孩/宠物,写方程 \(\sum_{j \in T} x_j = 1\)(\(T\) 是该实体出现的三元组集合),要求恰好一个三元组被选中。这直接得到 \(Ax = \mathbf{1}\) 形式。

ZOE \(\to\) SUBSET SUM

关键观察:0-1 向量可编码为整数。将 \(A\) 的每列视为二进制整数,寻找列的子集使其和为 \(\mathbf{1}\)(全 1 向量)对应子集和问题。

但二进制加法有进位问题!解决方案:将列视为 base \((n+1)\) 整数(\(n\) 为列数)。由于至多 \(n\) 个数相加且每位数字为 0 或 1,base \((n+1)\) 下不会产生进位,向量加法与整数加法等价。

ZOE \(\to\) ILP

ILP 要求整数向量 \(x\) 满足 \(Ax \leq b\)。将 ZOE 的每个方程 \(x_{j_1} + \cdots + x_{j_k} = 1\) 拆为两个不等式 \(\leq 1\) 和 \(\geq 1\),并添加 \(0 \leq x_i \leq 1\),即得 ILP 形式。

ZOE \(\to\) RUDRATA CYCLE

分两步:先归约为带边对 Rudrata 环(Rudrata Cycle with Paired Edges),再消除边对。

ZOE 到带边对 Rudrata 环:构造一个包含 \(m+n\) 组平行边的简单环结构(\(m\) 个方程、\(n\) 个变量)。每组平行边选择一个,对应变量赋值或方程中选择哪个变量。边对约束 \(C\) 确保:若变量 \(x_i=1\)(选择 \(x_i=1\) 边),则方程侧不能选 \(x_i\) 相关边(因与 \(x_i=0\) 边配对),从而保证每个方程恰好一个变量为 1。

消除边对:设计一个 12 顶点 gadget(图 8.12),在 Rudrata 环中只能以两种方式遍历,模拟一对边中恰好选一条的约束。替换所有边对后得到纯 Rudrata 环。

RUDRATA CYCLE \(\to\) TSP

给定图 \(G=(V,E)\),构造 TSP:城市 = \(V\),距离 \(d_{uv} = 1\) 若 \(\{u,v\} \in E\),否则 \(d_{uv} = 1+\epsilon\)(\(\epsilon > 1\)),预算 \(b = |V|\)。

取 \(\epsilon = 1\) 得满足三角不等式的 TSP(第 9 章可近似);取 \(\epsilon\) 大则产生"间隙"性质:要么有长度 \(\leq n\) 的解,要么所有解长度 \(\geq n + \epsilon\)(间隙),这蕴含不可近似性(除非 \(\text{P} = \text{NP}\))。

推导示例

工作示例:ZOE 到子集和的进制转换

考虑 ZOE 实例(矩阵列视为向量):

\[A = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 1 & 1 & 0 \\ 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \end{pmatrix}\]

列向量为 \(c_1=(1,0,0,1,0)\), \(c_2=(0,0,1,0,1)\), \(c_3=(0,0,1,0,0)\), \(c_4=(0,1,0,0,0)\)。

若用二进制读:\(c_1=10001_2=17\), \(c_2=10100_2=20\), \(c_3=00100_2=4\), \(c_4=01000_2=8\),目标和 \(= 11111_2 = 31\)。

但 \(c_2 + c_3 = (0,0,1,0,1) + (0,0,1,0,0) = (0,0,2,0,0) \neq\) 任何合法向量,而二进制 \(20+4=24 \neq 31\),此处碰巧不冲突。但考虑若 \(c_2+c_3+c_4 = 20+4+8=32 \neq 31\) 也 OK。

真正的问题:假设有列 \(a=00101_2=5\), \(b=00110_2=6\), \(c=10100_2=20\),则 \(a+b+c=31\) 但向量和为 \((1,0,3,1,1) \neq (1,1,1,1,1)\)。二进制进位破坏了等价性。

解决方案:用 base \((n+1)\),其中 \(n=3\)(列数),即 base 4:

  • \(a = 00101_4 = 4+1 = 5\)

  • \(b = 00110_4 = 4+4 = 8\)(注意:base 4 下数字不能 \(\geq 4\),但此处数字为 0/1,合法)

  • \(c = 10100_4 = 64+4 = 68\)

\(a+b+c = 5+8+68 = 81\),而目标 \((1,1,1,1,1)_4 = 1+4+16+64+256 = 341\),不相等,正确反映了向量和不相等。

由于 base \((n+1)\) 下每位数字 \(\in \{0,1\}\) 且至多 \(n\) 个数相加,每位和 \(\leq n <\) base,无进位,向量加法与整数加法完全等价。归约正确。

所有 NP 问题皆可归约为 SAT 与不可解问题

本节完成归约链的闭环:证明所有 NP 问题都可归约到 SAT,从而确立 SAT 的 NP 完全性,并简要讨论超越 NP 的不可解问题。

CIRCUIT SAT:SAT 的推广

CIRCUIT SAT 的实例是一个布尔电路(有向无环图),包含五种门:AND(入度 2)、OR(入度 2)、NOT(入度 1)、已知输入(标号 \(\text{true}\)/\(\text{false}\))、未知输入(标号 \(?\))。一个汇点被指定为输出门。

CIRCUIT SAT 要求找到未知输入的赋值使输出门为 \(\text{true}\)。

CIRCUIT SAT 归约为 SAT

对每个门 \(g\) 引入变量,用子句模拟门的行为:

所得 CNF 公式的满足赋值与电路满足赋值一一对应。

任意 NP 问题归约为 CIRCUIT SAT

这是通用归约:设 \(A\) 是任意 NP 问题,\(C(I, S)\) 是其多项式时间验证算法。

关键论证(第 7.7 节):任何多项式时间算法都可表示为电路——输入门编码输入比特,门的数量是输入规模的多项式。

因此,对 \(A\) 的实例 \(I\),可在多项式时间构造电路:已知输入 = \(I\) 的比特,未知输入 = \(S\) 的比特,输出 = \(\text{true} \iff S\) 是 \(I\) 的解。此电路的满足赋值与 \(I\) 的解一一对应。

归约链闭环:

\[\forall A \in \text{NP}: A \to \text{CIRCUIT SAT} \to \text{SAT} \to \text{3SAT} \to \cdots\]

这证明了 CIRCUIT SAT 和 SAT 是 NP 完全的:所有 NP 问题都可归约到它们。

不可解问题

NP 完全问题至少还有指数时间算法。但存在完全没有算法的合理计算问题!

停机问题(Halting Problem):给定程序 \(p\) 和输入 \(x\),判断 \(p(x)\) 是否停机。

Turing 1936 年的证明:假设存在程序 \(\text{terminates}(p, x)\),构造

function paradox(z):
    if terminates(z, z) goto loop  // 若 z(z) 停机则循环
    otherwise halt                  // 否则停机

执行 \(\text{paradox}(\text{paradox})\):若它停机 \(\Rightarrow\) \(\text{terminates}(\text{paradox}, \text{paradox}) = \text{true}\) \(\Rightarrow\) 它循环,矛盾;若它循环 \(\Rightarrow\) \(\text{terminates} = \text{false}\) \(\Rightarrow\) 它停机,矛盾。故 \(\text{terminates}\) 不存在。

这展示了计算的极限:有些问题无论多少时间都无法通过算法解决。编程永远需要纪律、创造力和技巧——你无法自动检测程序是否有无限循环。

推导示例

工作示例:CIRCUIT SAT 到 SAT 的编码

考虑一个简单电路:输出门 \(g\) 是 AND 门,\(g = h_1 \land h_2\),其中 \(h_1\) 是已知输入 \(\text{true}\),\(h_2\) 是未知输入。

门行为子句:

  • AND 门 \(g = h_1 \land h_2\):

  • \((\lnot g \lor h_1)\):若 \(g\) 真则 \(h_1\) 必真

  • \((\lnot g \lor h_2)\):若 \(g\) 真则 \(h_2\) 必真

  • \((g \lor \lnot h_1 \lor \lnot h_2)\):若 \(h_1, h_2\) 都真则 \(g\) 必真

  • 已知 \(h_1 = \text{true}\):子句 \((h_1)\)

  • 输出门 \(g\) 为真:子句 \((g)\)

化简:由 \((g)\) 得 \(g=\text{true}\);由 \((\lnot g \lor h_2)\) 和 \(g=\text{true}\) 得 \(h_2=\text{true}\)。

因此唯一满足赋值:\(g=\text{true}, h_1=\text{true}, h_2=\text{true}\)。对应电路:未知输入 \(h_2 = \text{true}\) 使输出为 \(\text{true}\)。

验证:代入电路,\(h_1=\text{true}, h_2=\text{true}\),AND 门输出 \(\text{true}\),满足要求。编码正确。

交互推演

以 3SAT 到 CLIQUE 为例逐项审查归约方向、构造、双向等价与 NP 成员性。

依据本地课程资料 lectures/chap8.pdf、dis10.pdf、dis11.pdf、dis12.pdf 重绘;交互用于解释算法状态与证明不变量,不替代正文中的完整论证。

从 3SAT 到 CLIQUE 的归约与四项证明检查

常见误区

NP 代表'No Problem'(没问题),因为这些问题虽然难但总有办法解决

NP 代表'Nondeterministic Polynomial time'(非确定性多项式时间)。NP 类包含一些极难的问题(如 SAT、TSP),我们尚不知道它们是否有高效算法。NP 不是'No Problem'。

如果能在多项式时间内验证一个解,那么也一定能在多项式时间内找到这个解

这是 P = NP 的核心问题,但绝大多数研究者相信 P ≠ NP。验证解(NP)比寻找解(P)容易得多——SAT 的验证只需逐子句检查,但寻找满足赋值可能需要穷举 2^n 种可能。可验证性不蕴含可解性。

归约 A → B 意味着如果 A 有多项式时间算法,则 B 也有多项式时间算法

恰恰相反。归约 A → B 意味着困难性沿箭头方向传播(A 难则 B 难),而高效算法逆箭头方向传播(B 有多项式算法则 A 也有)。要用归约证明 B 困难,需将已知的困难问题 A 归约到 B。

NP 完全问题是最难的问题,所以它们是不可解的(没有算法能解)

NP 完全问题有算法(如穷举搜索),只是目前只知道指数时间算法。'不可解问题'(如停机问题)是另一类——它们根本没有算法(无论多项式还是指数)。NP 完全 ≠ 不可解。

如果 P = NP,那么所有搜索问题都立即变得可高效求解

P = NP 只保证存在多项式时间算法,但这些算法的指数可能非常高(如 O(n^100)),或者我们尚未发现具体算法。理论上的存在性与实际可计算性之间仍有差距。

知识检查

问题:关于多项式时间算法与摩尔定律的关系,以下哪项最准确?

问题:下列关于 NP 类问题的描述,哪一项最准确?

问题:若存在从搜索问题 A 到搜索问题 B 的多项式时间归约(A → B),则以下哪项成立?

问题:关于顶点覆盖(VERTEX COVER)与独立集(INDEPENDENT SET)的关系,以下哪项正确?

本讲要点

一手资料