算法理论知识点

113 minAdvanced2026/7/20

计算复杂性理论的核心体系:以 Turing 1936《On Computable Numbers, with an Application to the Entscheidungsproblem》Proc. LMS 42:230-265 图灵机模型与 Church 1936《An Unsolvable Problem of Elementary Number Theory》Amer. J. Math. 58(2):345-363 λ-演算为根基,梳理 Gödel 1931 不完备性定理、Rice 1953《Classes of Recursively Enumerable Sets and Their Decision Problems》Trans. AMS 74:358-366、Hartmanis-Stearns 1965《On the Computational Complexity of Algorithms》Trans. AMS 117:285-306 复杂性类奠基、Cook 1971《The Complexity of Theorem-Proving Procedures》STOC 151-158 Cook-Levin 定理、Karp 1972《Reducibility Among Combinatorial Problems》21 个 NP 完全问题、Levin 1973《Universal Search Problems》Probl. Peredachi Inf. 9(3):115-116 独立发现、Savitch 1970《Relationships Between Nondeterministic and Deterministic Tape Complexities》JCSS 4(2):177-192、Baker-Gill-Solovay 1975《Relativizations of the P=?NP Question》SICOMP 4(4):431-442 相对化屏障、Ladner 1975《On the Structure of Polynomial Time Reducibility》JACM 22(1):155-171 NP-intermediate、PCP 定理(Arora-Safra 1998《Probabilistic Checking of Proofs》JACM 45(1):70-122;Arora-Lund-Motwani-Sudan-Szegedy 1998 JACM 45(3):501-555;Dinur 2007 组合证明)的完整脉络,覆盖 P/NP/NP-Hard/NP-Complete 形式化定义、多项式归约、摊还分析三方法(Sleator-Tarjan 1985 CACM 28(2):202-208)、竞争分析(Sleator-Tarjan 1985)、在线算法、数据流模型、P vs NP 千禧年大奖问题,附 Python/C++/Java 多语言实现与经典归约链(SAT → 3-SAT → CLIQUE → VERTEX-COVER → HAMILTONIAN-CYCLE → TSP)及 LeetCode 经典题解。

1. 概述与学习目标

1.1 什么是算法理论

算法理论(Algorithm Theory)是研究算法的计算能力、求解效率与本质限界的数学理论体系,其核心包含三大支柱:

  1. 可计算性理论(Computability Theory):研究哪些问题可以用算法求解。奠基者是 Turing 1936 图灵机模型与 Church 1936 λ-演算,回答”什么是可计算”这一根本问题。Gödel 1931 不完备性定理与 Turing 1936 停机问题不可判定性是这一领域的两大负面结果——存在不可解的问题。

  2. 计算复杂性理论(Computational Complexity Theory):研究可解问题的求解难度。奠基者是 Hartmanis-Stearns 1965 复杂性类与 Cobham 1964/Edmonds 1965 P 类定义。复杂性理论的核心是复杂性类(complexity class)——按求解所需资源(时间、空间、随机性、交互性)将问题分类。

  3. 算法设计与分析(Algorithm Design and Analysis):研究如何高效求解具体问题。包括分治、贪心、动态规划、回溯、分支限界等设计范式,以及摊还分析、竞争分析等分析方法。

算法理论层次模型:

                            算法理论
                                |
        ┌────────────────────┬────────────────────┐
   可计算性理论          计算复杂性理论          算法设计与分析
        │                    │                       │
   ┌────┴────┐         ┌─────┴─────┐           ┌────┴────┐
  图灵机      λ-演算    P/NP/NPC    PSPACE      摊还分析   竞争分析
  Church-Turing         EXPTIME    BPP/QC      聚合/核/势能 在线算法
  停机问题              P vs NP    PCP 定理     数据流      近似算法
  Rice 定理             归约        不可近似性   随机化      启发式

为什么算法理论重要?算法理论为工程师提供”判断问题难度”的科学依据。当你面对一个新问题:

  • 若证明其为 NP-Hard,则不应追求多项式时间精确算法(除非 P=NP),应转向近似算法、启发式或指数级精确算法
  • 若证明其为 P,则应寻找高效多项式算法,避免过度工程化
  • 若证明其 不可判定(如停机问题变体),则应放弃追求完美算法,转而设计部分解或保守近似

算法理论在工业界的应用

领域应用关键理论
密码学RSA、ECC、零知识证明P vs NP、单向函数、PCP
形式化验证芯片验证、程序分析SAT 求解、模型检测(PSPACE)
人工智能推理、规划、学习NP 完全性、近似算法
运筹优化调度、路径、排班NP-Hard、近似算法、CP-SAT
数据库查询优化、并发控制复杂性下界、可串行化
大数据流式算法、 sketches数据流模型、空间下界
区块链共识协议、智能合约随机化、博弈论、复杂性

1.2 算法理论的学习路径

入门(可计算性):
  Turing 机 → Church-Turing 论题 → 停机问题 → Rice 定理

进阶(复杂性类):
  P/NP 定义 → 多项式归约 → Cook-Levin 定理 → Karp 21 NP 完全问题

高级(结构理论):
  Savitch 定理 → 层次定理 → Ladner 定理 → Baker-Gill-Solovay → PCP 定理

应用(算法设计):
  摊还分析 → 竞争分析 → 近似算法 → 随机化算法 → 在线算法

前沿(现代方向):
  量子复杂性(BQP)→ 交互式证明(IP=PSPACE)→ 不可近似性(UGC)→ 元复杂性

1.3 学习目标

完成本章学习后,读者应能够:

  1. 记忆(Remember):Turing 1936 图灵机、Church 1936 λ-演算、Gödel 1931 不完备性、Cook 1971 Cook-Levin 定理、Karp 1972 21 NP 完全问题、Levin 1973 独立发现、Savitch 1970 定理、Baker-Gill-Solovay 1975 相对化屏障、Ladner 1975 NP-intermediate、PCP 定理(Arora-Safra 1998、Dinur 2007)的历史脉络
  2. 理解(Understand):P/NP/NP-Hard/NP-Complete 形式化定义、多项式归约的数学语义、Cook-Levin 定理证明思路、Savitch 定理 NSPACE ⊆ DSPACE 平方关系、Baker-Gill-Solovay 为何阻拦 P vs NP 传统证明、PCP 定理与不可近似性联系
  3. 应用(Apply):使用 4 步法证明新问题 NP 完全、使用聚合/核算/势能三方法分析摊还代价、使用竞争分析评估在线算法、设计 2-近似度量 TSP 与 Christofides 1.5-近似算法
  4. 分析(Analyze):经典归约链 SAT → 3-SAT → CLIQUE → VERTEX-COVER → HAMILTONIAN-CYCLE → TSP 的正确性、Count-Min Sketch 与 HyperLogLog 的空间复杂度、LRU k-竞争证明
  5. 评估(Evaluate):各复杂性类(P/NP/co-NP/NPC/NP-Hard/PSPACE/EXPTIME)包含关系、P vs NP 为何难解、摊还分析三方法适用场景、确定性 vs 随机化在线算法竞争比差距
  6. 对比(Compare):摊还 vs 平均 vs 最坏情况分析、精确 vs 近似 vs 启发式算法、DPLL/CDCL SAT 求解 vs CP-SAT vs ILP 求解器在工业级组合优化上的优劣
  7. 创造(Create):设计基于复杂性理论的工业解决方案——SAT 求解器用于芯片验证、CP-SAT 用于员工排班、近似算法用于物流路径、数据流算法用于实时监控

2. 历史动机与演进

2.1 可计算性理论的奠基(1930s)

2.1.1 Hilbert 的判定问题与 Gödel 不完备性

1900 年 David Hilbert 在巴黎国际数学家大会上提出 23 个未解决问题,其中第 10 问题(判定 Diophantus 方程是否有解)与第 2 问题(算术的一致性)奠定了可计算性理论的动机。1928 年 Hilbert 与 Wilhelm Ackermann 在《Grundzüge der theoretischen Logik》中明确提出判定问题(Entscheidungsproblem):

是否存在一个算法,能够判定一阶谓词逻辑中的任意命题是否可证?

这一问题深刻影响了 1930s 的数理逻辑发展。1931 年 Kurt Gödel 在《Über formal unentscheidbare Sätze der Principia Mathematica und verwandter Systeme I》(Monatshefte für Mathematik und Physik 38(1):173-198, DOI:10.1007/BF01700692)中证明了不完备性定理

任何包含初等算术的一致形式系统都存在真但不可证的命题;且该系统的一致性不能在系统内部证明。

Gödel 的证明使用了Gödel 编码——将形式系统的命题与证明编码为自然数,从而让系统”谈论自身”。这一技巧直接启发了后来的 Turing 与 Church。

2.1.2 Turing 1936 图灵机

1936 年 Alan Turing 在《On Computable Numbers, with an Application to the Entscheidungsproblem》(Proceedings of the London Mathematical Society s2-42(1):230-265, DOI:10.1112/plms/s2-42.1.230)中给出了判定问题的负面解答。Turing 的核心贡献是引入了图灵机(Turing Machine)作为可计算性的形式化模型:

图灵机形式定义:
  M = (Q, Σ, Γ, δ, q0, q_accept, q_reject)

  其中:
    Q          有限状态集
    Σ          输入字母表(不含空白符)
    Γ          带字母表(Σ ∪ {空白符})
    δ: Q × Γ → Q × Γ × {L, R}   转移函数
    q0 ∈ Q     初始状态
    q_accept   接受状态
    q_reject   拒绝状态

图灵机由无限长磁带、读写头、有限状态控制器组成,每步根据当前状态与磁带符号决定下一状态、写入符号与读写头移动方向。Turing 证明了图灵机可模拟任意”机械化计算过程”,并提出 Church-Turing 论题:图灵机可计算的函数 = 直观可计算的函数。

Turing 同篇论文证明了停机问题不可判定

不存在图灵机 H,使得对任意图灵机 M 与输入 w,H 能判定 M 在 w 上是否停机。

证明使用对角线法:假设 H 存在,构造对角机器 D(M) = 如果 H(M, M) = 停机则不停机,否则停机;则 D(D) 产生矛盾。

1937 年 Turing 在《Computability and λ-definability》(Journal of Symbolic Logic 2(4):153-163)中证明图灵机可计算 = Church λ-可定义,二者等价,确立 Church-Turing 论题。

2.1.3 Church 1936 λ-演算

1936 年 Alonzo Church 在《An Unsolvable Problem of Elementary Number Theory》(American Journal of Mathematics 58(2):345-363, DOI:10.2307/2371045)中独立给出判定问题的负面解答,使用 λ-演算(lambda calculus)作为可计算性模型。λ-演算是函数式编程的理论基础:

λ-演算语法(BNF):
  <expr> ::= <var>
          | λ<var>.<expr>
          | <expr> <expr>

  规约规则:
    α-变换:λx.M → λy.M[x:=y]   (变量重命名)
    β-归约:(λx.M) N → M[x:=N]   (函数应用)
    η-变换:λx.M x → M           (外延性)

Church-Turing 论题断言:图灵机可计算 = λ-可定义 = 一般递归 = 直观可计算。这一论题虽不可严格证明(“直观可计算”非形式化),但所有已知计算模型(递归函数、Post 系统算子、Markov 算法、量子计算机)在多项式时间内可互相模拟,强有力地支持了论题。

2.2 复杂性理论的诞生(1960s)

2.2.1 Hartmanis-Stearns 1965 复杂性类

1965 年 Juris Hartmanis 与 Richard Stearns 在《On the Computational Complexity of Algorithms》(Transactions of the American Mathematical Society 117:285-306, DOI:10.1090/S0002-9947-1965-0170805-7)中奠定了计算复杂性理论的数学基础。他们引入:

  1. 多带图灵机(Multi-tape Turing Machine):每条带独立读写头,比单带更接近实际计算机
  2. 时间复杂性类 TIME(f(n)):在 O(f(n))O(f(n)) 时间内可判定的语言类
  3. 层次定理(Hierarchy Theorem):若 ff 可构造且 g=ω(flogf)g = \omega(f \log f),则 TIME(f(n)) ⊊ TIME(g(n))——更多时间允许判定更多语言

Hartmanis-Stearns 因这一奠基性工作获 1993 年 Turing Award。该论文确立了”算法难度可按渐近资源消耗分类”的范式,是复杂性理论的开山之作。

2.2.2 Cobham 1964 / Edmonds 1965 P 类定义

1964 年 Alan Cobham 在《The intrinsic computational difficulty of functions》(Proc. 1964 Int. Congr. Logic Method. Phil. Sci., North-Holland, pp. 24-30)中首次将多项式时间(polynomial time)作为可处理性的形式化标准,定义 P 类:

P=k1TIME(nk)\mathrm{P} = \bigcup_{k \geq 1} \mathrm{TIME}(n^k)

1965 年 Jack Edmonds 在《Paths, trees, and flowers》(Canadian Journal of Mathematics 17:449-467, DOI:10.4153/CJM-1965-045-4)中独立提出多项式时间作为”好算法”的标准,并给出最大匹配的多项式算法。Edmonds 同时引入了”NP”的非形式化概念,预言了 NP 完全性的发现。

Cobham-Edmonds 论题:P 类 = 实际可处理的计算问题。这一论题虽是经验性的(多项式时间算法的高次项在大规模下仍不实际),但提供了复杂性分类的实用基准。

2.3 NP 完全性的发现(1970s)

2.3.1 Cook 1971 Cook-Levin 定理

1971 年 Stephen Cook 在《The Complexity of Theorem-Proving Procedures》(Proc. 3rd Annual ACM Symposium on Theory of Computing (STOC 71), pp. 151-158, DOI:10.1145/800157.805047)中证明:

SAT(布尔可满足性问题)是 NP 完全的。

即:对任意 NP 问题 L,存在多项式时间归约 LpSATL \leq_p \mathrm{SAT}。Cook 的证明思路:

  1. 任取 LNPL \in \mathrm{NP},存在非确定图灵机 M 在多项式时间判定 L
  2. 对输入 w,构造布尔公式 ϕM,w\phi_{M,w} 编码 “M 接受 w” 的计算 tableau
  3. ϕM,w\phi_{M,w} 的变量表示 tableau 中每个格子的状态、符号、读写头位置
  4. ϕM,w\phi_{M,w} 的子句编码:(a) 初始格局正确;(b) 转移函数合法;(c) 接受状态可达
  5. ϕM,w\phi_{M,w} 可满足 ⟺ M 接受 w
  6. 构造时间为 ϕM,w=O(wO(1))|\phi_{M,w}| = O(|w|^{O(1)}),即多项式

Cook 因此获 1982 年 Turing Award。这一定理确立了 SAT 作为 NP 完全性归约的”种子问题”地位。

2.3.2 Karp 1972 21 NP 完全问题

1972 年 Richard Karp 在《Reducibility Among Combinatorial Problems》(Proc. Symp. Complexity of Computer Computations, IBM Thomas J. Watson Research Center, March 20-22, 1972; in R.E. Miller, J.W. Thatcher (eds.), The Complexity of Computer Computations, Plenum Press, New York, pp. 85-103)中证明 21 个经典组合问题 NP 完全:

  1. SATISFIABILITY
  2. 0-1 INTEGER PROGRAMMING
  3. CLIQUE
  4. SET PACKING
  5. VERTEX COVER
  6. SET COVERING
  7. FEEDBACK NODE SET
  8. FEEDBACK ARC SET
  9. DIRECTED HAMILTONIAN CYCLE
  10. UNDIRECTED HAMILTONIAN CYCLE
  11. 3-SAT
  12. CHROMATIC NUMBER
  13. CLIQUE COVER
  14. EXACT COVER
  15. HITTING SET
  16. STEINER TREE
  17. 3-DIMENSIONAL MATCHING
  18. KNAPSACK
  19. JOB SEQUENCING
  20. PARTITION
  21. MAX-CUT

Karp 的归约链:

SAT → 3-SAT → CLIQUE → VERTEX COVER → FEEDBACK ARC SET
              ↓                       ↓
            SET PACKING           HAMILTONIAN CYCLE

                                  TSP
3-SAT → EXACT COVER → 3-DIM MATCHING → KNAPSACK → PARTITION

Karp 因 NP 完全性与组合优化贡献获 1985 年 Turing Award。Karp 21 问题使 NP 完全性从理论概念变为工程实践工具——工程师遇到新问题,可尝试归约到这 21 个问题之一。

2.3.3 Levin 1973 独立发现

1973 年 Leonid Levin 在《Universal Search Problems》(Проблемы передачи информации 9(3):115-116)中独立证明 SAT 的 NP 完全性。Levin 在苏联独立于 Cook 与 Karp 完成 NP 完全性理论,定义了 6 个”通用搜索问题”(universal perebor problems)。Levin 的工作因语言障碍与冷战延误,直到 1970s 末才被西方学者认识。

Cook-Levin 定理以二位独立发现者命名,体现了科学发现的同时性。Levin 后续在不可近似性、平均情况复杂性等方向有重要贡献。

2.4 结构理论的深化(1970s-1990s)

2.4.1 Savitch 1970 空间复杂性

1970 年 Walter Savitch 在《Relationships Between Nondeterministic and Deterministic Tape Complexities》(Journal of Computer and System Sciences 4(2):177-192, DOI:10.1016/S0022-0000(70)80006-X)中证明:

Savitch 定理:对任意可构造 f(n)lognf(n) \geq \log nNSPACE(f(n))DSPACE(f(n)2)\mathrm{NSPACE}(f(n)) \subseteq \mathrm{DSPACE}(f(n)^2)

证明使用可达性算法(reachability):非确定图灵机的接受计算可视为配置图中的路径问题,使用分治判定 s 到 t 是否可达,递归深度 O(logN)O(\log N),每层存储 O(f(n))O(f(n)) 配置,总空间 O(f(n)2)O(f(n)^2)

Savitch 定理蕴含 NPSPACE=PSPACE\mathrm{NPSPACE} = \mathrm{PSPACE}——非确定性在空间资源上仅平方损失,与时间资源的指数差距(P vs NP 疑题)形成鲜明对比。

2.4.2 Baker-Gill-Solovay 1975 相对化屏障

1975 年 Theodore Baker、John Gill、Robert Solovay 在《Relativizations of the P=?NP Question》(SIAM Journal on Computing 4(4):431-442, DOI:10.1137/0204037)中证明:

存在 oracle A 使得 PA=NPA\mathrm{P}^A = \mathrm{NP}^A;也存在 oracle B 使得 PBNPB\mathrm{P}^B \neq \mathrm{NP}^B

具体构造:

  • AA 为 PSPACE 完全语言 QBFQBF,则 PA=NPA=PSPACE\mathrm{P}^A = \mathrm{NP}^A = \mathrm{PSPACE}
  • BB 为随机 oracle,Bennett-Gill 1981 证明对随机 B 几乎必然 PBNPB\mathrm{P}^B \neq \mathrm{NP}^B

相对化屏障的含义:任何”对 oracle 保持不变”的证明技术(即所谓 relativizing proof)都无法解决 P vs NP 问题。这阻拦了大量传统证明技术:

  • 对角线法(diagonalization)
  • 层次定理证明
  • 模拟论证(simulation argument)

P vs NP 的解决需要”非相对化”新技术。

2.4.3 Ladner 1975 NP-intermediate

1975 年 Richard Ladner 在《On the Structure of Polynomial Time Reducibility》(Journal of the ACM 22(1):155-171, DOI:10.1145/321864.321877)中证明:

Ladner 定理:若 PNP\mathrm{P} \neq \mathrm{NP},则存在 LNPPL \in \mathrm{NP} \setminus \mathrm{P} 既非 P 中也非 NP 完全。

即 NP 中存在”中间难度”问题(NP-intermediate)。候选 NP-intermediate 问题包括:

  • 图同构问题(Graph Isomorphism):Babai 2015 给出准多项式时间算法 2O((logn)5)2^{O((\log n)^5)}
  • 离散对数(Discrete Logarithm):在经典计算机上无多项式算法,但量子计算机 Shor 算法可解
  • 最短向量问题(Shortest Vector Problem, SVP):格密码学基础
  • 整数分解(Integer Factorization):Shor 算法可量子求解

Ladner 定理表明 NP 的内部结构远比”P + NP-Complete”二元划分复杂。

2.4.4 Razborov-Rudich 1997 自然证明屏障

1997 年 Alexander Razborov 与 Steven Rudich 在《Natural Proofs》(Journal of Computer and System Sciences 55(1):24-35, DOI:10.1006/jcss.1997.1494)证明:

一大类”自然”的组合证明技术无法分离 P 与 NP。

具体地,若存在满足”可构造性 + 宽广性”的证明下界技术,则可破解伪随机生成器,从而破解基于单向函数的密码学。鉴于密码学假设的可信度,自然证明技术无法解决 P vs NP。

Razborov-Rudich 与 Baker-Gill-Solovay 并列为 P vs NP 的两大屏障:

屏障年代含义
相对化屏障1975relativizing 技术无法解决
自然证明屏障1997natural proofs 技术无法解决
代数化屏障2008 (Aaronson-Wigderson)algebrizing 技术无法解决

2.5 PCP 定理与不可近似性(1990s-2000s)

2.5.1 PCP 定理的发现

1992 年 Sanjeev Arora、Carsten Lund、Rajeev Motwani、Madhu Sudan、Mario SzegedySanjeev Arora、Shmuel Safra 分别证明 PCP 定理,发表于 1998 年:

PCP 定理NP=PCP[O(logn),O(1)]\mathrm{NP} = \mathrm{PCP}[O(\log n), O(1)]

即 NP 语言的证明可被概率验证器在 O(logn)O(\log n) 随机位、O(1)O(1) 次证明查询下验证。验证器只读证明的常数位即可高概率判定真伪。

PCP 定理的革命性意义

  1. 重新定义了”证明”的概念:传统证明需线性时间读完,PCP 证明只需常数次随机查询
  2. 建立了 NP 与概率可验证证明的等价
  3. 直接蕴含大量不可近似性结果:MAX-3SAT 不能近似到 7/8+ϵ7/8 + \epsilon 之内(除非 P=NP)

Arora-Safra 与 Arora-Lund-Motwani-Sudan-Szegedy 共获 2001 年 Gödel Prize。

2.5.2 Dinur 2007 组合证明

2007 年 Irit Dinur 在《The PCP Theorem by Gap Amplification》(JACM 54(3):Article 12, DOI:10.1145/1132516.1132534)给出 PCP 定理的纯组合证明。Dinur 的证明使用间隙放大(gap amplification)技术:

  1. 将 NP 语言的判定实例表示为约束图(constraint graph)
  2. 通过 expanders 与随机游走放大不可满足性间隙
  3. 迭代放大直到常数间隙,每步保持图规模多项式有界

Dinur 的证明比原始代数证明简洁许多,使 PCP 定理成为更易教学的工具。Dinur 因此获 2019 年 Gödel Prize。

2.5.3 PCP 定理的不可近似性蕴含

PCP 定理直接导致现代不可近似性理论的诞生:

问题最佳近似比不可近似下界来源
MAX-3SAT7/8(随机)7/8+ϵ7/8 + \epsilonHåstad 2001
MAX-CLIQUEO(n/(logn)2)O(n / (\log n)^2)n1ϵn^{1-\epsilon}Zuckerman 2007
VERTEX COVER2(NP 近似)1.361.36Dinur-Safra 2002
SET COVERlnn\ln n(贪心)(1o(1))lnn(1-o(1)) \ln nFeige 1998
TSP(度量)1.5(Christofides)123/122123/122Karpinski-Lampis-Schmied 2015

2.6 摊还分析与竞争分析的兴起(1980s)

2.6.1 Sleator-Tarjan 1985 摊还分析

1985 年 Daniel Sleator 与 Robert Tarjan 在《Amortized efficiency of list update and paging rules》(Communications of the ACM 28(2):202-208, DOI:10.1145/2786.2793)中系统化摊还分析(amortized analysis),引入势能方法(potential method):

定义势能函数 Φ:数据结构状态R0\Phi: \text{数据结构状态} \to \mathbb{R}_{\geq 0},满足 Φ(D0)=0\Phi(D_0) = 0。操作的摊还代价 c^i=ci+Φ(Di)Φ(Di1)\hat{c}_i = c_i + \Phi(D_i) - \Phi(D_{i-1}),其中 cic_i 为实际代价。总摊还代价 c^i=ci+Φ(Dn)Φ(D0)ci\sum \hat{c}_i = \sum c_i + \Phi(D_n) - \Phi(D_0) \geq \sum c_i

Sleator-Tarjan 用势能法分析:

  • 动态数组(dynamic array):append 摊还 O(1)O(1)
  • Splay 树(splay tree):所有操作摊还 O(logn)O(\log n)
  • 页面替换 LRU:k-竞争

Tarjan 1986 因数据结构与算法分析获 Turing Award。

2.6.2 在线算法与竞争分析

Sleator-Tarjan 同篇论文引入竞争分析(competitive analysis):

在线算法 ALG 是 c-竞争的,若对所有输入序列 IIALG(I)cOPT(I)+b|\mathrm{ALG}(I)| \leq c \cdot |\mathrm{OPT}(I)| + b,其中 OPT 为最优离线算法。

经典结果:

在线问题算法竞争比
Paging(k-缓存)LRUk
Ski Rental(租借-购买)租 B-1 天后买2
Ski Rental(随机)指数分布e/(e1)1.58e/(e-1) \approx 1.58
k-Server(一般度量)Work Functionk
k-Server(随机)O(log2k)O(\log^2 k)

竞争分析开创了在线算法这一新领域,Borodin-El-Yaniv 1998《Online Computation and Competitive Analysis》是该领域标准教材。

2.7 现代发展(2000s 至今)

2.7.1 P vs NP 千禧年大奖

2000 年 Clay Mathematics Institute 设立 7 个千禧年大奖问题(Millennium Prize Problems),每个悬赏 $1,000,000。P vs NP 是其中唯一的计算机科学问题,由 Cook 在 Clay 官方陈述中形式化:

P = NP?

Cook 2003 在《The P versus NP Problem》(Clay Mathematics Institute Monograph)中阐述了该问题的历史与重要性。截至 2026 年,该问题仍开放,多数理论计算机科学家相信 P ≠ NP。

2.7.2 量子复杂性

1994 年 Peter Shor 给出多项式时间量子算法分解整数与离散对数,威胁 RSA 密码。Shor 算法引发了量子复杂性理论研究:

  • BQP(Bounded-error Quantum Polynomial time):量子多项式时间
  • 已知 BQP ⊆ PSPACE,BQP 与 NP 的关系未明
  • 量子计算机是否可解 NP 完全问题?多数理论学家相信 NP ⊄ BQP

2.7.3 元复杂性

21 世纪兴起元复杂性(Meta-complexity)方向:研究”判断问题难度”本身的难度。典型问题:

  • MCSP(Minimum Circuit Size Problem):给定真值表与整数 s,是否存在大小 ≤ s 的电路计算该函数?
  • MCSP 是否 NP 完全?这是复杂性理论 21 世纪核心开放问题之一

3. 形式化定义

3.1 图灵机与计算模型

3.1.1 确定性图灵机

定义 3.1(确定性图灵机,DTM) 确定性图灵机是七元组 M=(Q,Σ,Γ,δ,q0,qacc,qrej)M = (Q, \Sigma, \Gamma, \delta, q_0, q_{acc}, q_{rej})

  • QQ:有限状态集
  • Σ\Sigma:输入字母表,不含空白符 \sqcup
  • Γ\Gamma:带字母表,Σ{}Γ\Sigma \cup \{\sqcup\} \subseteq \Gamma
  • δ:Q×ΓQ×Γ×{L,R}\delta: Q \times \Gamma \to Q \times \Gamma \times \{L, R\}:转移函数
  • q0Qq_0 \in Q:初始状态
  • qaccQq_{acc} \in Q:接受状态
  • qrejQq_{rej} \in Q:拒绝状态,qrejqaccq_{rej} \neq q_{acc}

配置(configuration)(q,T,i)(q, T, i) 表示当前状态 qq、带内容 T:ZΓT: \mathbb{Z} \to \Gamma、读写头位置 iZi \in \mathbb{Z}

计算(computation)是配置序列 C0C1C2C_0 \vdash C_1 \vdash C_2 \vdash \cdots,其中 C0=(q0,w,0)C_0 = (q_0, w, 0) 为初始配置,Ci+1C_{i+1}CiC_iδ\delta 转移得到。若到达 qaccq_{acc} 则接受;若到达 qrejq_{rej} 则拒绝;否则可能死循环。

3.1.2 非确定性图灵机

定义 3.2(非确定性图灵机,NTM) 非确定性图灵机的转移函数为 δ:Q×ΓP(Q×Γ×{L,R})\delta: Q \times \Gamma \to \mathcal{P}(Q \times \Gamma \times \{L, R\}),即每步可能有多个合法转移。

NTM 的接受定义为:存在一个接受计算分支。这与”猜测 + 验证”的直觉一致——NTM 猜测正确分支并验证。

关键事实:DTM 可模拟 NTM,但时间开销可能指数增长。这是 P vs NP 问题的根源。

3.1.3 多带图灵机

实际复杂性理论使用多带图灵机(multi-tape TM):含 kk 条带与 kk 个读写头。Hartmanis-Stearns 1965 证明:

单带 TM 与多带 TM 在多项式时间内等价(即 tt 步多带 TM 可被 O(t2)O(t^2) 步单带 TM 模拟)。

因此 P 类对图灵机变体(单带/多带/二维带/随机访问)鲁棒,证明 Cobham-Edmonds 论题的合理性。

3.2 复杂性类的形式化定义

3.2.1 时间复杂性类

定义 3.3(DTIME, NTIME) 设 f:NNf: \mathbb{N} \to \mathbb{N}

DTIME(f(n))={L DTM M,M 在 O(f(n)) 时间内判定 L}\mathrm{DTIME}(f(n)) = \{ L \mid \exists \text{ DTM } M, M \text{ 在 } O(f(n)) \text{ 时间内判定 } L \} NTIME(f(n))={L NTM M,M 在 O(f(n)) 时间内判定 L}\mathrm{NTIME}(f(n)) = \{ L \mid \exists \text{ NTM } M, M \text{ 在 } O(f(n)) \text{ 时间内判定 } L \}

定义 3.4(P, NP):

P=k1DTIME(nk),NP=k1NTIME(nk)\mathrm{P} = \bigcup_{k \geq 1} \mathrm{DTIME}(n^k), \qquad \mathrm{NP} = \bigcup_{k \geq 1} \mathrm{NTIME}(n^k)

NP 的等价定义(验证视角):

NP={Lp 多项式,VP,x:xL    y,yp(x),V(x,y)=1}\mathrm{NP} = \{ L \mid \exists p \text{ 多项式}, V \in \mathrm{P}, \forall x: x \in L \iff \exists y, |y| \leq p(|x|), V(x, y) = 1 \}

其中 yy 称为证书(certificate/witness),VV 为验证器。例如 SAT 的证书是满足赋值,3-SAT 验证只需 O(n)O(n) 时间。

3.2.2 空间复杂性类

定义 3.5(DSPACE, NSPACE):

DSPACE(f(n))={L DTM M,M 在 O(f(n)) 空间内判定 L}\mathrm{DSPACE}(f(n)) = \{ L \mid \exists \text{ DTM } M, M \text{ 在 } O(f(n)) \text{ 空间内判定 } L \} NSPACE(f(n))={L NTM M,M 在 O(f(n)) 空间内判定 L}\mathrm{NSPACE}(f(n)) = \{ L \mid \exists \text{ NTM } M, M \text{ 在 } O(f(n)) \text{ 空间内判定 } L \}

定义 3.6(PSPACE, NPSPACE, L, NL):

PSPACE=k1DSPACE(nk),NPSPACE=k1NSPACE(nk)\mathrm{PSPACE} = \bigcup_{k \geq 1} \mathrm{DSPACE}(n^k), \quad \mathrm{NPSPACE} = \bigcup_{k \geq 1} \mathrm{NSPACE}(n^k) L=DSPACE(logn),NL=NSPACE(logn)\mathrm{L} = \mathrm{DSPACE}(\log n), \quad \mathrm{NL} = \mathrm{NSPACE}(\log n)

Savitch 定理蕴含 NPSPACE=PSPACE\mathrm{NPSPACE} = \mathrm{PSPACE}

3.2.3 指数复杂性类

定义 3.7(EXPTIME, NEXPTIME):

EXPTIME=k1DTIME(2nk),NEXPTIME=k1NTIME(2nk)\mathrm{EXPTIME} = \bigcup_{k \geq 1} \mathrm{DTIME}(2^{n^k}), \quad \mathrm{NEXPTIME} = \bigcup_{k \geq 1} \mathrm{NTIME}(2^{n^k})

层次定理(Hartmanis-Stearns 1965)保证严格包含:

PEXPTIME,NPNEXPTIME\mathrm{P} \subsetneq \mathrm{EXPTIME}, \quad \mathrm{NP} \subsetneq \mathrm{NEXPTIME}

3.3 多项式归约

定义 3.8(多项式时间多一归约) 语言 L1L_1 多项式时间多一归约到 L2L_2,记 L1pL2L_1 \leq_p L_2,若存在多项式时间可计算函数 f:ΣΣf: \Sigma^* \to \Sigma^* 使得:

xΣ:xL1    f(x)L2\forall x \in \Sigma^*: x \in L_1 \iff f(x) \in L_2

性质 3.1(归约的传递性) 若 L1pL2L_1 \leq_p L_2L2pL3L_2 \leq_p L_3,则 L1pL3L_1 \leq_p L_3

性质 3.2(归约的封闭性) 若 L1pL2L_1 \leq_p L_2L2PL_2 \in \mathrm{P},则 L1PL_1 \in \mathrm{P}

定义 3.9(NP-Hard) 语言 LL 是 NP-Hard 的,若对任意 LNPL' \in \mathrm{NP}LpLL' \leq_p L

定义 3.10(NP-Complete) 语言 LL 是 NP 完全的(NPC),若 LNPL \in \mathrm{NP}LL 是 NP-Hard。

定理 3.1(NP 完全性的归约判据) 设 LNPCL_{NPC} 为已知 NP 完全语言。若 LNPCpLL_{NPC} \leq_p LLNPL \in \mathrm{NP},则 LL 是 NP 完全的。

证明:对任意 LNPL' \in \mathrm{NP},由 LNPCL_{NPC} 完全性 LpLNPCL' \leq_p L_{NPC};由假设 LNPCpLL_{NPC} \leq_p L;由传递性 LpLL' \leq_p L。故 LL 是 NP-Hard。又 LNPL \in \mathrm{NP},故 LL NP 完全。\square

3.4 NP 完全性证明的 4 步法

4 步法(Standard 4-step method)证明问题 LL NP 完全:

  1. 证明 LNPL \in \mathrm{NP}:给出多项式时间验证器,描述证书形式与验证过程
  2. 选择已知 NP 完全问题 LNPCL_{NPC}:通常选 SAT、3-SAT、VERTEX COVER、CLIQUE、SUBSET SUM 等
  3. 构造多项式归约 f:LNPCLf: L_{NPC} \to L:将 LNPCL_{NPC} 实例变换为 LL 实例
  4. 证明归约正确性
    • 完备性(completeness):xLNPCf(x)Lx \in L_{NPC} \Rightarrow f(x) \in L
    • 可靠性(soundness):f(x)LxLNPCf(x) \in L \Rightarrow x \in L_{NPC}
    • 多项式时间性:ff 可在多项式时间计算

例 3.1(3-SAT NP 完全)证明 3-SAT 是 NP 完全的:

  1. 3-SAT ∈ NP:证书是满足赋值,验证器在 O(m)O(m) 时间检查每个子句
  2. 选择 SAT 为已知 NPC(Cook-Levin 定理)
  3. 构造归约 SAT → 3-SAT:使用 Tseytin 变换将任意 CNF 公式转为 3-CNF:
    • 对长度 > 3 的子句 (l1l2lk)(l_1 \lor l_2 \lor \cdots \lor l_k),引入新变量 y1,y2,,yk3y_1, y_2, \ldots, y_{k-3} 与子句 (l1l2y1)(¬y1l3y2)(¬yk3lk1lk)(l_1 \lor l_2 \lor y_1) \land (\lnot y_1 \lor l_3 \lor y_2) \land \cdots \land (\lnot y_{k-3} \lor l_{k-1} \lor l_k)
    • 对长度 1 的子句 (l)(l),引入新变量 a,ba, b(lab)(la¬b)(l¬ab)(l¬a¬b)(l \lor a \lor b) \land (l \lor a \lor \lnot b) \land (l \lor \lnot a \lor b) \land (l \lor \lnot a \lor \lnot b)
    • 对长度 2 的子句类似处理
  4. 正确性:Tseytin 变换保持可满足性(新变量编码子句的真值)

\square


4. 理论推导与复杂度分析

4.1 Cook-Levin 定理证明思路

定理 4.1(Cook-Levin 1971)SAT 是 NP 完全的。

证明思路

  1. SAT ∈ NP:证书是满足赋值,验证在 O(n)O(n) 时间
  2. 对任意 LNPL \in \mathrm{NP} 构造归约 LpSATL \leq_p \mathrm{SAT}
    • 设 NTM M=(Q,Σ,Γ,δ,q0,qacc,qrej)M = (Q, \Sigma, \Gamma, \delta, q_0, q_{acc}, q_{rej})O(nk)O(n^k) 时间判定 LL
    • 对输入 www=n|w| = n),MMT=O(nk)T = O(n^k) 步内停机
    • 构造布尔公式 ϕM,w\phi_{M,w} 编码 “M 接受 w” 的计算
  3. tableau 编码
    • 计算可视为 T×TT \times T 的 tableau(每行一个配置)
    • 变量 xt,i,sx_{t,i,s} 表示时刻 tt、位置 ii、符号 ss
    • 变量 ht,ih_{t,i} 表示时刻 tt 读写头在位置 ii
    • 变量 qt,sq_{t,s} 表示时刻 tt 状态为 ss
  4. 约束子句
    • 初始格局q0,q0=1q_{0,q_0} = 1x0,i,wi=1x_{0,i,w_i} = 1(输入符号),其他位置空白
    • 合法性:每格恰好一个符号(互斥子句)
    • 转移一致性:相邻行配置满足 δ\delta(局部约束,相邻格子组合决定)
    • 接受:某时刻 tt 满足 qt,qacc=1q_{t,q_{acc}} = 1
  5. 规模分析:变量数 O(T2)=O(n2k)O(T^2) = O(n^{2k}),子句数 O(T2)=O(n2k)O(T^2) = O(n^{2k}),构造时间多项式

\square

4.2 经典归约链

4.2.1 3-SAT → CLIQUE

归约:3-CNF 公式 ϕ=C1C2Cm\phi = C_1 \land C_2 \land \cdots \land C_m,每子句 3 文字。构造图 GG

  • 顶点:每个文字出现对应一顶点,标签为 (i,j)(i, j)(第 ii 子句第 jj 文字)
  • 边:(i,j)(i, j)(i,j)(i', j') 相邻,当且仅当 iii \neq i' 且两文字不矛盾(即不是 ll¬l\lnot l

定理 4.2 ϕ\phi 可满足     \iff GG 有大小 m\geq m 的团。

证明

  • ()(\Rightarrow)ϕ\phi 可满足,每子句有真文字,对应顶点构成大小 mm 的团
  • ()(\Leftarrow)GG 有大小 mm 的团,团中顶点来自不同子句(无自环),矛盾文字不相邻,故可同时赋真,满足 ϕ\phi

\square

4.2.2 CLIQUE → VERTEX COVER

归约:图 G=(V,E)G = (V, E) 与整数 kk。构造补图 Gˉ=(V,Eˉ)\bar{G} = (V, \bar{E}),其中 Eˉ=(V2)E\bar{E} = \binom{V}{2} \setminus E

定理 4.3 GG 有大小 kk 的团     \iff Gˉ\bar{G} 有大小 Vk|V| - k 的顶点覆盖。

证明

  • SSGG 的团     \iff SSGG 中两两相邻     \iff VSV \setminus SGˉ\bar{G} 中两两不相邻     \iff VSV \setminus S 覆盖 Gˉ\bar{G} 所有边(任一边至少一端不在 VSV \setminus S 中,即两端都在 SS 中,但 SSGˉ\bar{G} 中独立)

\square

4.2.3 VERTEX COVER → HAMILTONIAN CYCLE

此归约较复杂,使用图配件(gadget)技术:

  • 顶点 vv 对应”选择”配件(菱形结构)
  • ee 对应”连接”配件
  • 适当连接使得 Hamilton 圈恰好选择 VC|VC| 个菱形横穿

详见 Karp 1972 原文或 Garey-Johnson 1979《Computers and Intractability》附录 A1。

4.2.4 HAMILTONIAN CYCLE → TSP

归约:图 G=(V,E)G = (V, E)。构造 TSP 实例:

  • 城市:VV
  • 距离矩阵:d(u,v)=1d(u, v) = 1(u,v)E(u, v) \in E,否则 d(u,v)=2d(u, v) = 2
  • 阈值:B=VB = |V|

定理 4.4 GG 有 Hamilton 圈     \iff TSP 实例有长度 B\leq B 的环游。

证明:Hamilton 圈对应长度 V=B|V| = B 的 TSP 环游;反之长度 B\leq B 的 TSP 环游必全用距离 1 边,即 GG 中边,构成 Hamilton 圈。\square

4.3 Savitch 定理证明

定理 4.5(Savitch 1970)对可构造 f(n)lognf(n) \geq \log nNSPACE(f(n))DSPACE(f(n)2)\mathrm{NSPACE}(f(n)) \subseteq \mathrm{DSPACE}(f(n)^2)

证明思路

  1. 设 NTM MMf(n)f(n) 空间判定 LLMM 的配置数 2O(f(n))\leq 2^{O(f(n))}
  2. 接受计算对应配置图中从初始 ss 到接受 tt 的路径,长度 2O(f(n))\leq 2^{O(f(n))}
  3. 定义递归过程 CANYIELD(c1,c2,t)\mathrm{CANYIELD}(c_1, c_2, t):判断 c1c_1 能否在 t\leq t 步到达 c2c_2
  4. 递归式:CANYIELD(c1,c2,t)=cCANYIELD(c1,c,t/2)CANYIELD(c,c2,t/2)\mathrm{CANYIELD}(c_1, c_2, t) = \bigvee_{c'} \mathrm{CANYIELD}(c_1, c', t/2) \land \mathrm{CANYIELD}(c', c_2, t/2)
  5. 递归深度 O(logt)=O(f(n))O(\log t) = O(f(n)),每层存储 O(f(n))O(f(n)) 配置,总空间 O(f(n)2)O(f(n)^2)

推论 4.1 NPSPACE=PSPACE\mathrm{NPSPACE} = \mathrm{PSPACE}

\square

4.4 层次定理

定理 4.6(空间层次定理)若 ff 空间可构造且 g=ω(f)g = \omega(f),则 DSPACE(f(n))DSPACE(g(n))\mathrm{DSPACE}(f(n)) \subsetneq \mathrm{DSPACE}(g(n))

定理 4.7(时间层次定理)若 ff 时间可构造且 glogg=ω(f)g \log g = \omega(f),则 DTIME(f(n))DTIME(g(n))\mathrm{DTIME}(f(n)) \subsetneq \mathrm{DTIME}(g(n))

推论 4.2 PEXPTIME\mathrm{P} \subsetneq \mathrm{EXPTIME}NPNEXPTIME\mathrm{NP} \subsetneq \mathrm{NEXPTIME}

层次定理是已知少数几个无条件分离结果,利用对角线法证明——构造语言 LL 使得任何 f(n)f(n) 时间机器在某些输入上”被对角”。

4.5 PCP 定理与不可近似性

定理 4.8(PCP 定理,Arora-Safra 1998 + Arora-Lund-Motwani-Sudan-Szegedy 1998)NP=PCP[O(logn),O(1)]\mathrm{NP} = \mathrm{PCP}[O(\log n), O(1)]

直观含义:NP 语言的证明可被常数次随机查询高概率验证。

不可近似性蕴含

定理 4.9(MAX-3SAT 不可近似性,Håstad 2001)除非 P=NP,MAX-3SAT 不能在多项式时间近似到 7/8+ϵ7/8 + \epsilon 之内。

证明思路:由 PCP 定理,3-SAT 实例 ϕ\phi 可归约到 MAX-3SAT 实例 ψ\psi,使得:

  • ϕ\phi 可满足 \Rightarrow ψ\psi 所有子句可满足
  • ϕ\phi 不可满足 \Rightarrow ψ\psi 至多 7/8+ϵ7/8 + \epsilon 比例子句可满足

若存在 (7/8+ϵ)(7/8 + \epsilon)-近似多项式算法 A,则:

  • ϕ\phi 可满足 \Rightarrow A(ψ\psi) 给出 1\geq 1 比例
  • ϕ\phi 不可满足 \Rightarrow A(ψ\psi) 给出 7/8+ϵ\leq 7/8 + \epsilon 比例

A 可在多项式时间判定 SAT 可满足性,矛盾 P ≠ NP。\square

4.6 复杂性类的包含关系图

                         ┌────────────┐
                         │  EXPTIME   │
                         │  (EXP)     │
                         └────────────┘

                         ┌────┴────┐
                         │ PSPACE  │
                         └─────────┘

                    ┌─────────┴─────────┐
                    │        NP         │
                    │  ┌────────────┐   │
                    │  │ NP-Complete│   │
                    │  │  (NPC)     │   │
                    │  └────────────┘   │
                    └─────────┬─────────┘

                    ┌─────────┴─────────┐
                    │        P          │
                    └───────────────────┘

                    ┌─────────┴─────────┐
                    │   NL ⊆ P          │
                    │   ┌───┐           │
                    │   │ L │           │
                    │   └───┘           │
                    └───────────────────┘

已知:L ⊆ NL ⊆ P ⊆ NP ⊆ PSPACE ⊆ EXPTIME
已知:L ⊊ PSPACE, P ⊊ EXPTIME(层次定理)
开放:L vs P, P vs NP, NP vs PSPACE

5. 代码示例

5.1 SAT 求解器(DPLL 算法)

DPLL(Davis-Putnam-Logemann-Loveland)算法是 SAT 求解的回溯算法基础:

from typing import List, Set, Dict, Optional

# CNF 公式表示:子句列表,每个子句为文字集合
# 文字:正整数表示正变量,负整数表示否定变量
Clause = frozenset
Formula = List[Clause]


def dpll(formula: Formula, assignment: Dict[int, bool]) -> Optional[Dict[int, bool]]:
    """DPLL 算法求解 CNF-SAT

    输入:
        formula: CNF 公式,子句列表
        assignment: 当前部分赋值

    返回:
        完整满足赋值,或 None 表示不可满足
    """
    # 单元传播(unit propagation)
    formula, assignment = unit_propagate(formula, assignment)
    if formula is None:
        return None  # 冲突
    if not formula:
        return assignment  # 所有子句满足

    # 纯文字消除(pure literal elimination)
    formula, assignment = pure_literal_eliminate(formula, assignment)
    if not formula:
        return assignment

    # 选择分支变量(启发式:最常出现的变量)
    var = choose_branching_variable(formula)

    # 尝试 var = True
    result = dpll(simplify(formula, var, True), {**assignment, var: True})
    if result is not None:
        return result

    # 尝试 var = False
    return dpll(simplify(formula, var, False), {**assignment, var: False})


def unit_propagate(formula: Formula, assignment: Dict[int, bool]) -> tuple:
    """单元传播:反复处理单文字子句"""
    while True:
        # 寻找单文字子句
        unit_clause = None
        for clause in formula:
            unassigned = [lit for lit in clause if abs(lit) not in assignment]
            if len(unassigned) == 0:
                # 子句已全部赋值
                if not any(is_satisfied(lit, assignment) for lit in clause):
                    return None, assignment  # 冲突
            elif len(unassigned) == 1:
                unit_clause = unassigned[0]
                break

        if unit_clause is None:
            return formula, assignment

        # 赋值使单元子句满足
        var = abs(unit_clause)
        value = unit_clause > 0
        assignment[var] = value
        formula = simplify(formula, var, value)


def simplify(formula: Formula, var: int, value: bool) -> Formula:
    """简化公式:删除满足子句,删除矛盾文字"""
    new_formula = []
    for clause in formula:
        # 检查子句是否已满足
        lit = var if value else -var
        if lit in clause:
            continue  # 子句已满足
        # 删除矛盾文字
        new_clause = clause - {-lit}
        if not new_clause:
            return None  # 空子句,冲突
        new_formula.append(new_clause)
    return new_formula


def is_satisfied(lit: int, assignment: Dict[int, bool]) -> bool:
    var = abs(lit)
    if var not in assignment:
        return False
    return assignment[var] == (lit > 0)


def pure_literal_eliminate(formula: Formula, assignment: Dict[int, bool]) -> tuple:
    """纯文字消除:仅以一种极性出现的文字可立即赋值"""
    polarity = {}
    for clause in formula:
        for lit in clause:
            var = abs(lit)
            sign = lit > 0
            if var in polarity:
                if polarity[var] != sign:
                    polarity[var] = None  # 双极
            else:
                polarity[var] = sign

    for var, sign in polarity.items():
        if sign is not None and var not in assignment:
            assignment[var] = sign
            formula = simplify(formula, var, sign)
            if formula is None:
                return None, assignment

    return formula, assignment


def choose_branching_variable(formula: Formula) -> int:
    """选择分支变量:使用最多出现次数启发式"""
    count = {}
    for clause in formula:
        for lit in clause:
            var = abs(lit)
            count[var] = count.get(var, 0) + 1
    return max(count, key=count.get)


# 测试:(x1 ∨ ¬x2) ∧ (¬x1 ∨ x3) ∧ (x2 ∨ x3)
formula = [frozenset([1, -2]), frozenset([-1, 3]), frozenset([2, 3])]
result = dpll(formula, {})
print(f"满足赋值: {result}")
# 输出示例:{1: True, 2: True, 3: True}

5.2 子集和问题(NP 完全性归约)

子集和问题是经典 NP 完全问题,可从 PARTITION 归约:

#include <iostream>
#include <vector>
#include <set>
using namespace std;

// 子集和问题:给定正整数集合 S 与目标 T,判断是否存在子集和为 T
// NP 完全性证明:3-SAT → SUBSET-SUM(Karp 1972 原始归约)

// 伪多项式 DP 解法:O(n * T) 时间
bool subset_sum_dp(const vector<long long>& S, long long T) {
    int n = S.size();
    // dp[i] = true 表示存在子集和为 i
    vector<char> dp(T + 1, 0);
    dp[0] = 1;
    for (long long x : S) {
        // 逆序更新避免重复使用
        for (long long i = T; i >= x; --i) {
            if (dp[i - x]) dp[i] = 1;
        }
    }
    return dp[T];
}

// 指数级精确解法(用于小规模 n ≤ 30):Meet-in-the-Middle
// 时间 O(2^(n/2)),空间 O(2^(n/2))
bool subset_sum_mitm(const vector<long long>& S, long long T) {
    int n = S.size();
    int half = n / 2;

    // 枚举前半所有子集和
    set<long long> left_sums;
    for (int mask = 0; mask < (1 << half); ++mask) {
        long long s = 0;
        for (int i = 0; i < half; ++i) {
            if (mask & (1 << i)) s += S[i];
        }
        left_sums.insert(s);
    }

    // 枚举后半,查找补集
    int right_half = n - half;
    for (int mask = 0; mask < (1 << right_half); ++mask) {
        long long s = 0;
        for (int i = 0; i < right_half; ++i) {
            if (mask & (1 << i)) s += S[half + i];
        }
        if (left_sums.count(T - s)) return true;
    }
    return false;
}

int main() {
    vector<long long> S = {3, 34, 4, 12, 5, 2};
    long long T = 9;
    cout << "DP: " << (subset_sum_dp(S, T) ? "Yes" : "No") << endl;
    cout << "MitM: " << (subset_sum_mitm(S, T) ? "Yes" : "No") << endl;
    // 输出:DP: Yes (4 + 5 = 9)
    //       MitM: Yes
    return 0;
}

5.3 动态数组的摊还分析(Python)

import math
from typing import Any, Iterator


class DynamicArray:
    """动态数组实现,演示摊还分析

    时间复杂度(摊还):
        - append:     O(1) 摊还
        - pop:        O(1) 摊还
        - access:     O(1) 最坏
        - insert(0):  O(n) 摊还
        - delete(0):  O(n) 摊还
    """

    def __init__(self, capacity: int = 1):
        self._capacity = max(1, capacity)
        self._size = 0
        self._data = [None] * self._capacity
        self._copy_count = 0  # 统计元素拷贝次数

    def __len__(self) -> int:
        return self._size

    def __getitem__(self, i: int) -> Any:
        if i < 0:
            i += self._size
        if not 0 <= i < self._size:
            raise IndexError("index out of range")
        return self._data[i]

    def __setitem__(self, i: int, value: Any) -> None:
        if i < 0:
            i += self._size
        if not 0 <= i < self._size:
            raise IndexError("index out of range")
        self._data[i] = value

    def append(self, value: Any) -> None:
        """追加元素,必要时扩容"""
        if self._size == self._capacity:
            self._resize(self._capacity * 2)
        self._data[self._size] = value
        self._size += 1

    def _resize(self, new_capacity: int) -> None:
        """调整容量,统计元素拷贝"""
        new_data = [None] * new_capacity
        for i in range(self._size):
            new_data[i] = self._data[i]
            self._copy_count += 1
        self._data = new_data
        self._capacity = new_capacity

    def pop(self) -> Any:
        """弹出末尾元素,必要时缩容"""
        if self._size == 0:
            raise IndexError("pop from empty array")
        value = self._data[self._size - 1]
        self._data[self._size - 1] = None
        self._size -= 1
        # 缩容:当 size < capacity / 4 时,容量减半
        if self._size < self._capacity // 4 and self._capacity > 1:
            self._resize(max(1, self._capacity // 2))
        return value

    def __iter__(self) -> Iterator:
        for i in range(self._size):
            yield self._data[i]


def verify_amortized_analysis(n: int = 10000) -> None:
    """验证 append 的摊还 O(1) 性质

    聚合分析:n 次 append 总拷贝次数 < 2n
    """
    arr = DynamicArray()
    for i in range(n):
        arr.append(i)

    # 总拷贝次数应 < 2n(理论上 n + n/2 + n/4 + ... = 2n)
    print(f"n = {n}")
    print(f"实际拷贝次数: {arr._copy_count}")
    print(f"理论上界 (2n): {2 * n}")
    print(f"摊还代价 (拷贝次数/n): {arr._copy_count / n:.4f}")
    # 输出示例:摊还代价约 1-2,符合 O(1) 摊还

    # 验证势能函数 Φ(D) = 2*size - capacity 的非负性
    # 当 size > capacity/2 时 Φ > 0;扩容后 size = capacity/2,Φ = 0
    # 操作序列中 Φ 始终非负


verify_amortized_analysis(10000)

5.4 LRU 缓存(k-竞争在线算法)

import java.util.*;

/**
 * LRU(Least Recently Used)缓存实现
 *
 * 竞争分析(Sleator-Tarjan 1985):
 *   对容量 k 的缓存,LRU 是 k-竞争的
 *   即对任意请求序列 σ,cost(LRU, σ) ≤ k · cost(OPT, σ) + k
 *
 * 证明思路:
 *   将 σ 划分为 k-相位(k-phase),每个相位至多 k 个不同页面
 *   LRU 每相位至多 k 次未命中;OPT 每相位至少 1 次未命中
 *   故 cost(LRU) / cost(OPT) ≤ k
 */
public class LRUCache<K, V> {
    private final int capacity;
    private final Map<K, V> cache;
    private long hitCount = 0;
    private long missCount = 0;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        // LinkedHashMap 按 access-order 维护,最近访问在末尾
        this.cache = new LinkedHashMap<K, V>(capacity, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
                return size() > LRUCache.this.capacity;
            }
        };
    }

    public V get(K key) {
        V value = cache.get(key);
        if (value != null) {
            hitCount++;
            return value;
        }
        missCount++;
        return null;
    }

    public void put(K key, V value) {
        if (!cache.containsKey(key)) {
            missCount++;
        } else {
            hitCount++;
        }
        cache.put(key, value);
    }

    public long getHitCount() { return hitCount; }
    public long getMissCount() { return missCount; }

    public static void main(String[] args) {
        // 模拟 paging 请求序列
        int k = 3;  // 缓存容量
        int[] requests = {1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5};

        LRUCache<Integer, Boolean> lru = new LRUCache<>(k);
        for (int page : requests) {
            if (lru.get(page) == null) {
                lru.put(page, true);
            }
        }

        System.out.println("LRU 竞争比验证 (k = " + k + ")");
        System.out.println("命中: " + lru.getHitCount());
        System.out.println("未命中: " + lru.getMissCount());
        System.out.println("理论竞争比上界: " + k);
        // 输出:LRU 应是 3-竞争的
    }
}

5.5 度量 TSP 的 2-近似算法

from typing import List, Tuple
import heapq


def tsp_2_approx(points: List[Tuple[float, float]]) -> float:
    """度量 TSP 的 2-近似算法(基于 MST)

    算法:
        1. 构造最小生成树 T
        2. 对 T 进行前序遍历,得到访问顺序
        3. 返回按前序顺序访问所有点的环游长度

    近似比证明:
        - MST 权重 w(T) ≤ OPT(删除 OPT 一条边得到生成树)
        - 前序遍历长度 ≤ 2w(T)(每边访问两次)
        - 故算法解 ≤ 2w(T) ≤ 2·OPT
    """
    n = len(points)
    if n <= 1:
        return 0.0
    if n == 2:
        return 2 * dist(points[0], points[1])

    # 步骤 1:构造 MST(Prim 算法)
    mst_edges = prim_mst(points)

    # 步骤 2:构造邻接表
    adj = [[] for _ in range(n)]
    for u, v in mst_edges:
        adj[u].append(v)
        adj[v].append(u)

    # 步骤 3:前序遍历
    visited = [False] * n
    preorder = []
    def dfs(u):
        visited[u] = True
        preorder.append(u)
        for v in adj[u]:
            if not visited[v]:
                dfs(v)
    dfs(0)

    # 步骤 4:计算环游长度(前序顺序 + 回到起点)
    total = 0.0
    for i in range(n):
        u = preorder[i]
        v = preorder[(i + 1) % n]
        total += dist(points[u], points[v])
    return total


def prim_mst(points: List[Tuple[float, float]]) -> List[Tuple[int, int]]:
    """Prim 算法构造 MST,O(n^2) 时间"""
    n = len(points)
    in_mst = [False] * n
    min_dist = [float('inf')] * n
    parent = [-1] * n
    min_dist[0] = 0

    edges = []
    for _ in range(n):
        # 选择未加入 MST 的最小距离顶点
        u = -1
        for v in range(n):
            if not in_mst[v] and (u == -1 or min_dist[v] < min_dist[u]):
                u = v

        in_mst[u] = True
        if parent[u] != -1:
            edges.append((parent[u], u))

        # 更新邻居距离
        for v in range(n):
            if not in_mst[v]:
                d = dist(points[u], points[v])
                if d < min_dist[v]:
                    min_dist[v] = d
                    parent[v] = u
    return edges


def dist(p1: Tuple[float, float], p2: Tuple[float, float]) -> float:
    """欧几里得距离(满足三角不等式,故为度量 TSP)"""
    return ((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) ** 0.5


# 测试
points = [(0, 0), (1, 1), (2, 0), (1, -1), (3, 2)]
approx = tsp_2_approx(points)
print(f"2-近似解: {approx:.4f}")
# 输出示例:2-近似解: 11.5396

5.6 Christofides 1.5-近似算法

from typing import List, Tuple, Dict
import networkx as nx


def christofides_tsp(points: List[Tuple[float, float]]) -> float:
    """Christofides 1.5-近似算法(度量 TSP)

    算法(Christofides 1976):
        1. 构造 MST T
        2. 找出 T 中所有奇度顶点 O
        3. 在 O 上构造最小权重完美匹配 M
        4. T ∪ M 形成 Eulerian 多重图
        5. 求 Euler 回路
        6. shortcut 重复顶点得到 Hamilton 圈

    近似比证明:
        - w(MST) ≤ OPT
        - w(perfect matching on O) ≤ OPT/2
        - 故 w(Euler) = w(MST) + w(M) ≤ 3·OPT/2
        - shortcut 不增加长度(三角不等式)
        - 故算法解 ≤ 1.5·OPT
    """
    n = len(points)
    if n <= 2:
        return 0.0 if n <= 1 else 2 * dist(points[0], points[1])

    # 构造完全图
    G = nx.Graph()
    for i in range(n):
        for j in range(i + 1, n):
            G.add_edge(i, j, weight=dist(points[i], points[j]))

    # 步骤 1:MST
    T = nx.minimum_spanning_tree(G)

    # 步骤 2:找奇度顶点
    odd_degree = [v for v in T.nodes() if T.degree(v) % 2 == 1]

    # 步骤 3:奇度顶点上的最小权重完美匹配
    # 使用 NetworkX 的 max_weight_matching(取负权转最小)
    subgraph = G.subgraph(odd_degree).copy()
    for u, v in subgraph.edges():
        subgraph[u][v]['weight'] = -subgraph[u][v]['weight']
    matching = nx.max_weight_matching(subgraph, maxcardinality=True)

    # 步骤 4:T ∪ M 形成 Eulerian 多重图
    multigraph = nx.MultiGraph(T)
    for u, v in matching:
        multigraph.add_edge(u, v, weight=dist(points[u], points[v]))

    # 步骤 5:Euler 回路
    euler_circuit = list(nx.eulerian_circuit(multigraph, source=0))

    # 步骤 6:shortcut 重复顶点
    visited = set()
    tour = []
    for u, v in euler_circuit:
        if u not in visited:
            tour.append(u)
            visited.add(u)
    tour.append(0)  # 回到起点

    # 计算总长度
    total = sum(dist(points[tour[i]], points[tour[i + 1]]) for i in range(len(tour) - 1))
    return total


def dist(p1, p2):
    return ((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) ** 0.5


points = [(0, 0), (1, 1), (2, 0), (1, -1), (3, 2)]
approx = christofides_tsp(points)
print(f"Christofides 1.5-近似解: {approx:.4f}")

5.7 Count-Min Sketch(数据流算法)

import numpy as np
import mmh3  # MurmurHash3
from typing import List


class CountMinSketch:
    """Count-Min Sketch 频率估计算法

    空间复杂度:O(d * w) = O((1/ε) · log(1/δ))
    估计误差:|f̂ - f| ≤ ε·||f||_1,概率 ≥ 1 - δ

    应用:
        - 网络流量监控(按 IP 统计包数)
        - 搜索引擎热门查询
        - 推荐系统频率统计
    """

    def __init__(self, epsilon: float, delta: float):
        """
        epsilon: 频率估计误差上界(相对于总流量)
        delta:  估计失败概率
        """
        self.w = int(np.ceil(1 / epsilon))   # 列数
        self.d = int(np.ceil(np.log(1 / delta)))  # 行数(哈希函数数)
        self.table = np.zeros((self.d, self.w), dtype=np.int64)
        # 使用不同 seed 生成 d 个独立哈希函数
        self.seeds = [i * 31 + 17 for i in range(self.d)]

    def update(self, key: str, count: int = 1) -> None:
        """更新 key 的频率估计"""
        for i in range(self.d):
            j = mmh3.hash(key, self.seeds[i], signed=False) % self.w
            self.table[i][j] += count

    def estimate(self, key: str) -> int:
        """估计 key 的频率(上界)"""
        return min(
            self.table[i][mmh3.hash(key, self.seeds[i], signed=False) % self.w]
            for i in range(self.d)
        )


# 测试
cms = CountMinSketch(epsilon=0.01, delta=0.01)
print(f"Sketch 大小: {cms.d} 行 × {cms.w} 列 = {cms.d * cms.w} 计数器")

# 模拟数据流
stream = ["apple", "banana", "apple", "cherry", "apple", "banana"] * 1000
for item in stream:
    cms.update(item)

# 估计频率
for item in ["apple", "banana", "cherry", "grape"]:
    print(f"  {item}: 估计 = {cms.estimate(item)}, 真实 = {stream.count(item)}")
# 输出示例:估计值略大于真实值(Count-Min 总是高估)

6. 对比分析

6.1 复杂性类对比

复杂性类定义包含关系典型问题
LDSPACE(log n)⊆ NL ⊆ Ps-t 连通性(无向图)
NLNSPACE(log n)⊆ Ps-t 连通性(有向图)
P\bigcup DTIME(nkn^k)⊆ NP排序、最短路、最大匹配
NP\bigcup NTIME(nkn^k)⊆ PSPACESAT、TSP、子集和
co-NPNP 的补与 NP 关系开放UNSAT、TAUT
BPP概率多项式时间⊆ P/poly, ⊆ Σ₂素性测试、多项式恒等
NP-CompleteNP ∩ NP-Hard⊆ NPSAT、3-SAT、CLIQUE
NP-Hard≥ NPC 难度不一定在 NP停机问题、TSP 优化版
PSPACE\bigcup DSPACE(nkn^k)= NPSPACE, ⊆ EXPTIMEQBF、围棋、PSPACE-Complete
EXPTIME\bigcup DTIME(2nk2^{n^k})⊋ P广义国际象棋、Checkers
BQP量子多项式时间⊆ PSPACE因式分解、离散对数

6.2 摊还分析 vs 平均情况 vs 最坏情况

分析方法定义适用场景局限性
最坏情况$\max_{x=n} T(x)$
平均情况Exμ[T(x)]\mathbb{E}_{x \sim \mu}[T(x)]需输入分布假设分布假设主观
摊还分析操作序列的平均数据结构操作序列仅适用序列场景
竞争分析ALG / OPT在线算法难以反映实际性能
平滑分析maxμExμ[T(x)]\max_\mu \mathbb{E}_{x \sim \mu}[T(x)]简单x扰动难以选择扰动尺度

摊还分析的核心特点

  1. 不涉及概率:与平均情况不同,摊还分析对任意操作序列给硬保证
  2. 针对操作序列:分析 n 次操作的总代价,平均到每次
  3. 三种方法等价:聚合、核算、势能法在表达能力上等价

6.3 在线算法的确定性 vs 随机化

问题确定性竞争比随机化竞争比下界
PagingkO(log2k)O(\log^2 k)(和谐算法)Ω(logk)\Omega(\log k)
Ski Rental2e/(e1)1.58e/(e-1) \approx 1.58e/(e1)e/(e-1)
k-Server(一般度量)kO(log2k)O(\log^2 k)Ω(logk)\Omega(\log k)
寻址(列表更新)21.581.58

Yao 原理:要证明随机化算法的下界,只需构造输入分布使任何确定性算法期望代价 ≥ c·OPT。这是随机化在线算法下界证明的核心工具。

6.4 SAT 求解器对比

求解器算法时间复杂度工业应用
DPLL(1962)回溯 + 单元传播 + 纯文字O(2n)O(2^n) 最坏教学用
CDCL(1996)冲突驱动子句学习O(2n)O(2^n) 最坏工业级 SAT 求解
WalkSAT(1997)局部搜索 + 噪声概率多项式(平均)随机 SAT 实例
Survey Propagation(2002)统计物理消息传递接近线性随机 3-SAT 相变点
CP-SAT(OR-Tools)CDCL + 约束传播O(2n)O(2^n) 最坏整数规划、调度
Concorde(TSP)分支割平面O(2n)O(2^n) 最坏大规模 TSP(85900 城市)

工业级 CDCL 求解器(如 CaDiCaL、Kissat)能解决百万变量规模的工业 SAT 实例,远超理论最坏界。这表明”NP 完全”并不等同于”实际不可解”。

6.5 近似算法 vs 精确算法 vs 启发式

方法性质保证时间复杂度适用场景
精确算法最优解指数级(NPC 问题)小规模、必须最优
近似算法近似比保证多项式NPC 优化问题
启发式算法经验有效,无理论保证通常多项式工业实践
随机化算法高概率正确多项式容忍小错误
参数化算法FPT:f(k)ncf(k) \cdot n^c多项式(小 k)参数 k 小
启发式 + 近似双重保证多项式工业实践

7. 常见陷阱

7.1 误将 NP 当作”多项式时间不可解”

陷阱:认为 NP 完全问题就一定多项式时间不可解。

澄清

  • NP 完全性是条件性结果——除非 P=NP,否则 NPC 问题无多项式算法
  • 实际中 NPC 问题在小规模(如 n100n \leq 100)可用 CDCL、CP-SAT、Concorde 等工业求解器高效解决
  • 许多 NPC 问题有好的近似算法或参数化算法

正确认识:NPC 是”难度警告”,不是”不可解判决”。

7.2 归约方向错误

陷阱:证明 L 是 NP 完全时,错误地将 L 归约到已知 NPC 问题,即 LpLNPCL \leq_p L_{NPC}

澄清

  • 证明 L NP 完全需要 LNPCpLL_{NPC} \leq_p L(即 LNPCL_{NPC} 归约到 LL
  • 这表明 LL 至少和 LNPCL_{NPC} 一样难
  • 反方向 LpLNPCL \leq_p L_{NPC} 只能说明 LNPL \in \mathrm{NP}(若 LNPCNPL_{NPC} \in \mathrm{NP}

记忆方法:箭头方向 = 难度增加方向。ApBA \leq_p B 意味 BB 不比 AA 容易。

7.3 忽略归约的多项式时间性

陷阱:构造归约时忽略构造 ff 的计算时间,仅证明存在性。

澄清:归约 ff 必须在多项式时间内可计算。常见错误:

  • 输出长度指数(如归约产生 2n2^n 大小的输出)
  • 中间构造涉及非多项式步骤(如枚举所有子集)

检查方法:归约的每步必须是多项式时间,输出大小多项式界。

7.4 摊还分析误用平均情况

陷阱:将摊还分析与平均情况分析混淆。

澄清

  • 摊还分析对任意操作序列给保证,不涉及概率
  • 平均情况需要假设输入分布(如均匀分布)
  • 摊还分析的结果更强——它给最坏序列下的平均代价

7.5 竞争比下界忽略 Yao 原理

陷阱:证明在线算法竞争比下界时,直接构造特定输入。

澄清:确定性算法的下界可直接构造最坏输入;但随机化算法的下界需要使用 Yao 原理——构造输入分布,使任何确定性算法期望代价高。

7.6 PCP 定理应用过度

陷阱:将 PCP 定理应用于非 NP 优化问题。

澄清:PCP 定理直接蕴含的是 NP 优化问题(如 MAX-3SAT)的不可近似性。对 P 中的优化问题,PCP 无直接帮助;对 PSPACE 或更强类的优化问题,需要不同的 PCP 变体。

7.7 误认为 BQP 包含 NP

陷阱:因 Shor 算法可解因式分解,误以为量子计算机可解所有 NP 问题。

澄清

  • 因式分解未证明 NP 完全(可能是 NP-intermediate)
  • Shor 算法是 BQP 算法,但 BQP 与 NP 的关系开放
  • 多数理论学家相信 NP ⊄ BQP,即量子计算机不能多项式时间解 NPC 问题

8. 工程实践

8.1 SAT 求解器在工业界的应用

8.1.1 硬件形式化验证

芯片设计使用 SAT 求解器验证:

  • 等价性检查(Equivalence Checking):验证 RTL 与门级网表功能等价
  • 模型检测(Model Checking):验证芯片满足时序性质(如无死锁、无冲突)
  • 测试生成(Test Generation):自动生成测试向量覆盖故障

工业级 SAT 求解器如 ABC(Berkeley)、miniSATCaDiCaL 能处理千万变量级实例。

8.1.2 软件验证

  • 程序分析:CBMC(C Bounded Model Checker)将 C 程序归约为 SAT
  • 定理证明:Coq、Lean 的 tactic 使用 SAT 求解器自动消解
  • SMT 求解:Z3、CVC4 在 SAT 之上添加理论求解(线性算术、数组、位向量)

8.2 CP-SAT 在组合优化中的应用

Google OR-Tools 的 CP-SAT 求解器是工业级组合优化利器:

# 员工排班问题(OR-Tools CP-SAT 示例)
from ortools.sat.python import cp_model


def employee_scheduling():
    """员工排班:5 名员工、7 天、每天 3 班,约束:
        - 每班至少 1 人
        - 每人每周最多 5 天
        - 每人连续工作不超过 3 天
    """
    model = cp_model.CpModel()
    num_emp = 5
    num_days = 7
    num_shifts = 3

    # 决策变量:work[e, d, s] = 1 表示员工 e 在第 d 天第 s 班
    work = {}
    for e in range(num_emp):
        for d in range(num_days):
            for s in range(num_shifts):
                work[e, d, s] = model.NewBoolVar(f'work_{e}_{d}_{s}')

    # 约束 1:每班至少 1 人
    for d in range(num_days):
        for s in range(num_shifts):
            model.Add(sum(work[e, d, s] for e in range(num_emp)) >= 1)

    # 约束 2:每人每周最多 5 天
    for e in range(num_emp):
        days_worked = []
        for d in range(num_days):
            day_var = model.NewBoolVar(f'day_{e}_{d}')
            model.AddMaxEquality(day_var, [work[e, d, s] for s in range(num_shifts)])
            days_worked.append(day_var)
        model.Add(sum(days_worked) <= 5)

    # 约束 3:每人每天至多 1 班
    for e in range(num_emp):
        for d in range(num_days):
            model.Add(sum(work[e, d, s] for s in range(num_shifts)) <= 1)

    # 求解
    solver = cp_model.CpSolver()
    status = solver.Solve(model)
    if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):
        for e in range(num_emp):
            schedule = []
            for d in range(num_days):
                for s in range(num_shifts):
                    if solver.Value(work[e, d, s]):
                        schedule.append(f'D{d}S{s}')
            print(f'员工 {e}: {schedule}')
    else:
        print('无可行解')


employee_scheduling()

8.3 近似算法在物流路径优化中的应用

  • VRP(Vehicle Routing Problem):使用 Clarke-Wright 节约算法(1.5-近似)
  • TSP:使用 Christofides 1.5-近似或 Lin-Kernighan 启发式
  • 集合覆盖:贪心 lnn\ln n-近似(Feige 1998 证明不可改进)

8.4 数据流算法在实时监控中的应用

  • HyperLogLog:Redis、BigQuery 用于基数估计(如独立访客数)
  • Count-Min Sketch:用于热门查询统计、异常检测
  • Bloom Filter:CDN 缓存、垃圾邮件过滤

9. 案例研究

9.1 LeetCode 493. 翻转对(分治 + BIT)

问题:给定数组 nums,返回翻转对数量,翻转对定义为 i<ji < jnums[i]>2nums[j]nums[i] > 2 \cdot nums[j]

算法:归并排序式分治,时间 O(nlogn)O(n \log n)

def reversePairs(nums):
    """归并分治统计翻转对"""
    if len(nums) <= 1:
        return 0

    mid = len(nums) // 2
    left = nums[:mid]
    right = nums[mid:]

    count = reversePairs(left) + reversePairs(right)

    # 统计跨左右翻转对
    j = 0
    for i in range(len(left)):
        while j < len(right) and left[i] > 2 * right[j]:
            j += 1
        count += j

    # 归并排序
    left.sort()
    right.sort()
    nums[:] = merge(left, right)
    return count


def merge(a, b):
    result = []
    i = j = 0
    while i < len(a) and j < len(b):
        if a[i] <= b[j]:
            result.append(a[i])
            i += 1
        else:
            result.append(b[j])
            j += 1
    result.extend(a[i:])
    result.extend(b[j:])
    return result

9.2 LeetCode 207. 课程表(拓扑排序)

问题:判断有向图是否存在拓扑序(即无环)。

算法:Kahn 算法(BFS 入度法),时间 O(V+E)O(V + E)

from collections import deque, defaultdict


def canFinish(numCourses, prerequisites):
    """Kahn 算法判定有向图无环"""
    graph = defaultdict(list)
    indegree = [0] * numCourses
    for v, u in prerequisites:
        graph[u].append(v)
        indegree[v] += 1

    queue = deque([i for i in range(numCourses) if indegree[i] == 0])
    visited = 0
    while queue:
        u = queue.popleft()
        visited += 1
        for v in graph[u]:
            indegree[v] -= 1
            if indegree[v] == 0:
                queue.append(v)
    return visited == numCourses

9.3 LeetCode 312. 戳气球(区间 DP)

问题:n 个气球排成一行,戳破气球 i 得到 nums[i1]nums[i]nums[i+1]nums[i-1] \cdot nums[i] \cdot nums[i+1] 金币,求最大金币数。

算法:区间 DP,O(n3)O(n^3)

def maxCoins(nums):
    """区间 DP:dp[i][j] 表示戳破 (i, j) 开区间内气球的最高得分"""
    nums = [1] + nums + [1]
    n = len(nums)
    dp = [[0] * n for _ in range(n)]

    for length in range(2, n):
        for i in range(n - length):
            j = i + length
            for k in range(i + 1, j):
                dp[i][j] = max(
                    dp[i][j],
                    nums[i] * nums[k] * nums[j] + dp[i][k] + dp[k][j]
                )

    return dp[0][n - 1]

9.4 LeetCode 514. 自由之路(BFS + 记忆化)

问题:在环形转盘上拼写目标字符串,求最少操作次数。

算法:BFS + 记忆化搜索,避免重复状态。

from collections import deque


def findRotateSteps(ring, key):
    """BFS + 记忆化求最少操作次数"""
    from functools import lru_cache
    n = len(ring)
    pos = {}
    for i, c in enumerate(ring):
        pos.setdefault(c, []).append(i)

    @lru_cache(maxsize=None)
    def dfs(i, j):
        """i: ring 当前位置, j: key 待拼写位置"""
        if j == len(key):
            return 0
        res = float('inf')
        for p in pos[key[j]]:
            d = min((p - i) % n, (i - p) % n)
            res = min(res, d + 1 + dfs(p, j + 1))
        return res

    return dfs(0, 0)

9.5 Concorde TSP 求解器世界记录

Concorde TSP 求解器由 Applegate、Bixby、Chvátal、Cook 开发,使用分支割平面法(branch-and-cut)精确求解 TSP:

  • 1987:318 城市 TSP(VLSI 印刷电路板)精确求解
  • 1998:13509 城市 TSP(美国 13509 个县)精确求解,耗时 3 个月
  • 2006:85900 城市 TSP(世界 TSP 挑战)精确求解,证明最优解

Concorde 证明 NP-Hard 问题在大规模下也可精确求解(虽然最坏复杂度指数级)。这印证”理论复杂性 ≠ 实际不可解”。


10. 习题与参考答案

10.1 选择题

题目 1 下列哪个陈述是 Baker-Gill-Solovay 1975 定理的内容?

A. P ≠ NP B. 存在 oracle A 使 P^A = NP^A,存在 oracle B 使 P^B ≠ NP^B C. NP ≠ PSPACE D. NP = co-NP

答案:B

解析:Baker-Gill-Solovay 1975《Relativizations of the P=?NP Question》SICOMP 4(4):431-442 证明了对 oracle 的相对化结果不一致,因此相对化技术无法解决 P vs NP。


题目 2 Savitch 定理的内容是?

A. NSPACE(f(n)) = DSPACE(f(n)) B. NSPACE(f(n)) ⊆ DSPACE(f(n)^2) C. NTIME(f(n)) ⊆ DTIME(2^f(n)) D. PSPACE = NPSPACE

答案:B

解析:Savitch 1970 证明 NSPACE(f(n)) ⊆ DSPACE(f(n)^2),蕴含 PSPACE = NPSPACE。


题目 3 PCP 定理的标准形式是?

A. NP = PCP[O(n), O(1)] B. NP = PCP[O(log n), O(1)] C. NP = PCP[O(1), O(log n)] D. NP = PCP[poly(n), O(1)]

答案:B

解析:PCP 定理(Arora-Safra 1998)证明 NP = PCP[O(log n), O(1)],即 NP 证明可用 O(logn)O(\log n) 随机位与 O(1)O(1) 次证明查询验证。


题目 4 关于 Ladner 定理,下列哪个正确?

A. P = NP 蕴含 NP-intermediate 存在 B. P ≠ NP 蕴含 NP-intermediate 存在 C. NP-intermediate 是 NP 完全 D. NP-intermediate 不存在

答案:B

解析:Ladner 1975 证明若 P ≠ NP,则存在 NP-intermediate 问题(在 NP 中但既非 P 也非 NPC)。


题目 5 Christofides 算法的近似比是?

A. 1 B. 1.5 C. 2 D. lnn\ln n

答案:B

解析:Christofides 1976 给出度量 TSP 的 1.5-近似算法,使用 MST + 最小权重完美匹配 + Euler 回路。


题目 6 Sleator-Tarjan 1985 证明 LRU 的竞争比是?

A. 1 B. O(logk)O(\log k) C. kk D. k2k^2

答案:C

解析:Sleator-Tarjan 1985 证明对容量 k 的缓存,LRU 是 k-竞争的。


题目 7 哪种证明技术被 Baker-Gill-Solovay 屏障阻拦?

A. 对角线法 B. 概率方法 C. 摊还分析 D. 归约法

答案:A

解析:对角线法是相对化技术(relativizing technique),被 Baker-Gill-Solovay 1975 屏障阻拦。


10.2 填空题

题目 1 Turing 1936 证明的不可判定问题是 ____。

答案:停机问题(Halting Problem)


题目 2 Cook 1971 证明 NP 完全的具体问题是 ____。

答案:SAT(布尔可满足性问题)


题目 3 Karp 1972 论文证明的 NP 完全问题数量是 ____。

答案:21


题目 4 摊还分析的三种方法是 、____。

答案:聚合分析、核算法、势能法


题目 5 PCP 定理的原始证明由 ____ 和 ____ 于 1992 年完成(1998 年发表)。

答案:Arora-Safra、Arora-Lund-Motwani-Sudan-Szegedy


题目 6 Savitch 定理蕴含 PSPACE = ____。

答案:NPSPACE


题目 7 千禧年大奖问题中唯一的计算机科学问题是 ____。

答案:P vs NP


10.3 代码修正题

题目 1 下列 DPLL 实现有错误,请修正:

def dpll_wrong(formula, assignment):
    # 单元传播:遗漏冲突检查
    for clause in formula:
        if len(clause) == 1:
            lit = next(iter(clause))
            assignment[abs(lit)] = lit > 0
    # 选择变量并分支
    var = abs(next(iter(next(iter(formula)))))
    return (dpll_wrong(simplify(formula, var, True), {**assignment, var: True})
            or dpll_wrong(simplify(formula, var, False), {**assignment, var: False}))

问题:单元传播未删除已满足子句,未处理冲突,未在满足后停止。

修正后

def dpll_correct(formula, assignment):
    # 修正:完整单元传播 + 冲突处理 + 满足检查
    while True:
        # 检查所有子句是否满足
        all_satisfied = True
        for clause in formula:
            unassigned = [l for l in clause if abs(l) not in assignment]
            if len(unassigned) == 0:
                if not any(assignment[abs(l)] == (l > 0) for l in clause):
                    return None  # 冲突:子句无法满足
            else:
                all_satisfied = False
        if all_satisfied:
            return assignment

        # 单元传播
        unit = None
        for clause in formula:
            unassigned = [l for l in clause if abs(l) not in assignment]
            if len(unassigned) == 1:
                unit = unassigned[0]
                break
        if unit is None:
            break
        assignment[abs(unit)] = unit > 0
        formula = simplify(formula, abs(unit), unit > 0)
        if formula is None:
            return None

    # 分支
    if not formula:
        return assignment
    var = abs(next(iter(next(iter(formula)))))
    result = dpll_correct(simplify(formula, var, True), {**assignment, var: True})
    if result is not None:
        return result
    return dpll_correct(simplify(formula, var, False), {**assignment, var: False})

10.4 开放论述题

题目 1 论述 P vs NP 千禧年大奖问题为何至今未解,列出至少三个已知屏障。

参考答案

P vs NP 是 Clay 数学研究所 2000 年设立的千禧年大奖问题之一,悬赏 $1,000,000。问题陈述简单:P = NP?但至今未解,主要原因有三:

  1. 相对化屏障(Baker-Gill-Solovay 1975): 存在 oracle A 使 P^A = NP^A(如 A = QBF),也存在 oracle B 使 P^B ≠ NP^B(如随机 oracle)。这意味着任何”对 oracle 保持不变”的证明技术(如对角线法、模拟论证)都无法解决 P vs NP。

  2. 自然证明屏障(Razborov-Rudich 1997): 一大类”自然”的组合证明技术(满足可构造性 + 宽广性)无法分离 P 与 NP。若存在自然证明,则可破解基于单向函数的密码学。鉴于密码学假设的可信度,自然证明技术不能解决问题。

  3. 代数化屏障(Aaronson-Wigderson 2008): 在 oracle 基础上添加代数扩展(algebraic extension),证明任何”代数化”技术也无法解决 P vs NP。

  4. 困难性根源: 上述三个屏障涵盖了对角线、组合、代数三大主流证明范式。突破需要全新数学工具,可能涉及非经典逻辑、拓扑、几何等高深工具。

题目 2 论述摊还分析三方法的等价性与适用场景。

参考答案

摊还分析三方法:

  1. 聚合分析(Aggregate Method):直接计算 n 次操作总代价 T(n),平均到每次。最直观但计算复杂。
  2. 核算法(Accounting Method):为不同操作分配不同摊还代价,存款支付未来昂贵操作。灵活性高但需谨慎设计。
  3. 势能法(Potential Method):定义势能函数 Φ\Phi,摊还代价 = 实际代价 + ΔΦ。最严谨、最强大。

等价性

三方法在表达能力上等价——任何能用一种方法分析的,都可用其他两种。但难度不同:

  • 聚合分析适合简单的总和计算(如动态数组 append)
  • 核算法适合多操作类型且代价差异大(如二项堆)
  • 势能法适合复杂状态依赖(如 Splay 树、Fibonacci 堆)

适用场景

  • 聚合分析:总代价易求的场景。如动态数组、二进制计数器
  • 核算法:操作类型多且代价模式清晰。如二项堆、Fibonacci 堆
  • 势能法:状态变化复杂,势能可清晰刻画。如 Splay 树、斜堆、Union-Find

实际中势能法最常用,因其数学严谨性便于严格证明。


11. 参考文献

11.1 经典论文

  1. Turing, Alan M. (1936). “On Computable Numbers, with an Application to the Entscheidungsproblem.” Proceedings of the London Mathematical Society s2-42(1):230-265. DOI:10.1112/plms/s2-42.1.230

  2. Church, Alonzo (1936). “An Unsolvable Problem of Elementary Number Theory.” American Journal of Mathematics 58(2):345-363. DOI:10.2307/2371045

  3. Gödel, Kurt (1931). “Über formal unentscheidbare Sätze der Principia Mathematica und verwandter Systeme I.” Monatshefte für Mathematik und Physik 38(1):173-198. DOI:10.1007/BF01700692

  4. Rice, Henry G. (1953). “Classes of Recursively Enumerable Sets and Their Decision Problems.” Transactions of the American Mathematical Society 74(2):358-366. DOI:10.1090/S0002-9947-1953-0053041-6

  5. Hartmanis, Juris; Stearns, Richard E. (1965). “On the Computational Complexity of Algorithms.” Transactions of the American Mathematical Society 117:285-306. DOI:10.1090/S0002-9947-1965-0170805-7

  6. Cobham, Alan (1965). “The intrinsic computational difficulty of functions.” Proc. 1964 Int. Congr. Logic Method. Phil. Sci., North-Holland, pp. 24-30

  7. Edmonds, Jack (1965). “Paths, trees, and flowers.” Canadian Journal of Mathematics 17:449-467. DOI:10.4153/CJM-1965-045-4

  8. Cook, Stephen A. (1971). “The Complexity of Theorem-Proving Procedures.” Proc. 3rd Annual ACM Symposium on Theory of Computing (STOC 71), pp. 151-158. DOI:10.1145/800157.805047

  9. Karp, Richard M. (1972). “Reducibility Among Combinatorial Problems.” Proc. Symp. Complexity of Computer Computations, Plenum Press, pp. 85-103

  10. Levin, Leonid A. (1973). “Universal Search Problems.” Problems of Information Transmission 9(3):115-116

  11. Savitch, Walter J. (1970). “Relationships Between Nondeterministic and Deterministic Tape Complexities.” Journal of Computer and System Sciences 4(2):177-192. DOI:10.1016/S0022-0000(70)80006-X

  12. Baker, Theodore; Gill, John; Solovay, Robert (1975). “Relativizations of the P=?NP Question.” SIAM Journal on Computing 4(4):431-442. DOI:10.1137/0204037

  13. Ladner, Richard E. (1975). “On the Structure of Polynomial Time Reducibility.” Journal of the ACM 22(1):155-171. DOI:10.1145/321864.321877

  14. Razborov, Alexander A.; Rudich, Steven (1997). “Natural Proofs.” Journal of Computer and System Sciences 55(1):24-35. DOI:10.1006/jcss.1997.1494

  15. Arora, Sanjeev; Safra, Shmuel (1998). “Probabilistic Checking of Proofs: A New Characterization of NP.” Journal of the ACM 45(1):70-122. DOI:10.1145/273865.273901

  16. Arora, Sanjeev; Lund, Carsten; Motwani, Rajeev; Sudan, Madhu; Szegedy, Mario (1998). “Proof Verification and the Hardness of Approximation Problems.” Journal of the ACM 45(3):501-555. DOI:10.1145/278298.278306

  17. Dinur, Irit (2007). “The PCP Theorem by Gap Amplification.” Journal of the ACM 54(3):Article 12. DOI:10.1145/1132516.1132534

  18. Sleator, Daniel D.; Tarjan, Robert E. (1985). “Amortized efficiency of list update and paging rules.” Communications of the ACM 28(2):202-208. DOI:10.1145/2786.2793

  19. Tarjan, Robert E. (1985). “Amortized computational complexity.” SIAM Journal on Algebraic and Discrete Methods 6(2):306-318. DOI:10.1137/0606031

  20. Håstad, Johan (2001). “Some optimal inapproximability results.” Journal of the ACM 48(4):798-859. DOI:10.1145/502090.502098

11.2 教材与专著

  1. Cormen, Thomas H.; Leiserson, Charles E.; Rivest, Ronald L.; Stein, Clifford (2022). Introduction to Algorithms. MIT Press, 4th edition. ISBN 978-0262046305

  2. Sipser, Michael (2013). Introduction to the Theory of Computation. Cengage Learning, 3rd edition. ISBN 978-1133187790

  3. Papadimitriou, Christos H. (1994). Computational Complexity. Addison-Wesley. ISBN 978-0201530827

  4. Arora, Sanjeev; Barak, Boaz (2009). Computational Complexity: A Modern Approach. Cambridge University Press. ISBN 978-0521424264

  5. Garey, Michael R.; Johnson, David S. (1979). Computers and Intractability: A Guide to the Theory of NP-Completeness. W. H. Freeman. ISBN 978-0716710455

  6. Kleinberg, Jon; Tardos, Eva (2006). Algorithm Design. Pearson. ISBN 978-0321295354

  7. Borodin, Allan; El-Yaniv, Ran (1998). Online Computation and Competitive Analysis. Cambridge University Press. ISBN 978-0521563925

  8. Knuth, Donald E. (1968). The Art of Computer Programming, Volume 1: Fundamental Algorithms. Addison-Wesley, 3rd edition (1997). ISBN 978-0201896831

  9. Motwani, Rajeev; Raghavan, Prabhakar (1995). Randomized Algorithms. Cambridge University Press. ISBN 978-0521474658

  10. Nisan, Noam; Ronen, Amir (2001). “Algorithmic Mechanism Design.” Games and Economic Behavior 35(1-2):166-196. DOI:10.1006/game.1999.0790

11.3 在线资源与数据库

  1. Complexity Zoo (2026). A Comprehensive Database of Computational Complexity Classes. https://complexityzoo.net/Complexity_Zoo. Accessed 2026-07-20. 由 Scott Aaronson 创建,整合 500+ 复杂性类的形式化定义与已知关系

  2. Clay Mathematics Institute (2000). Millennium Prize Problems: P vs NP. https://www.claymath.org/millennium/p-vs-np/. Accessed 2026-07-20. 七大千禧年大奖问题之一,奖金 100 万美元

  3. SAT Competition (2026). Annual SAT Solver Competition. https://satcompetition.org/. Accessed 2026-07-20. 自 2002 年起年度举办,推动 CDCL 求解器工业级发展

  4. DIMACS (1992-2026). Center for Discrete Mathematics and Theoretical Computer Science. https://dimacs.rutgers.edu/. Accessed 2026-07-20. 算法理论研究的核心机构,NP 完全性研讨会的发起方

  5. Stanford CS254 (2026). Computational Complexity. https://web.stanford.edu/class/cs254/. Accessed 2026-07-20. Stanford 研究生复杂性理论课程

  6. MIT 6.045J (2026). Automata, Computability, and Complexity. https://ocw.mit.edu/courses/6-045j-automata-computability-and-complexity-spring-2011/. Accessed 2026-07-20. MIT 可计算性与复杂性经典课程

12. 延伸阅读

12.1 在线课程

  • MIT 6.045J: Automata, Computability, and Complexity — Sipser 教材的官方配套课程,深入可计算性理论、停机问题、Rice 定理与归约方法。Lecture notes、习题与解答均通过 MIT OpenCourseWare 开放获取。是入门可计算性理论的最佳起点。

  • MIT 6.046J: Design and Analysis of Algorithms — CLRS 教材的进阶配套,覆盖动态规划、摊还分析、NP 完全性、近似算法、线性规划等核心主题。课程作业强调归约证明与算法设计实战。

  • Stanford CS254: Computational Complexity — 研究生级复杂性理论,覆盖 P vs NP、相对化屏障、自然证明、PCP 定理、量子复杂性(BQP)、元复杂性等前沿主题。由 Ryan Williams 等讲授,是博士生的必修基础。

  • CMU 15-451: Algorithms — Avrim Blum 与 Anupam Gupta 主讲,强调算法设计的统一视角(贪心、分治、DP、流、线性规划)与竞争分析、在线算法、近似算法的工程权衡。

  • Berkeley CS 170: Efficient Algorithms and Intractable Problems — Christos Papadimitriou 风格的代表性课程,融合算法与复杂性理论,强调算法的”可处理性边界”思想。

12.2 视频讲座

  • Ryan Williams (MIT) — “The P vs NP Problem” — Clay Mathematics Institute 官方邀请讲座,深入浅出介绍 P vs NP 千禧年大奖问题、相对化屏障与为何此问题如此困难。约 60 分钟,适合作为入门后的进阶理解材料。

  • Avi Wigderson (IAS) — “The Nature of Computation” — 普林斯顿高等研究院系列讲座,从复杂性理论视角审视计算的本质,涵盖随机性、伪随机性、零知识证明、自然证明屏障。Wigderson 2024 年因复杂性理论贡献获 Abel Prize。

  • Sanjeev Arora (Princeton) — “PCP Theorem and Hardness of Approximation” — PCP 定理主要证明者之一的亲授讲座,从证明系统到不可近似性的完整逻辑链。配套教材为 Arora-Barak《Computational Complexity: A Modern Approach》。

  • Tim Roughgarden (Stanford) — “Beyond Worst-Case Analysis” — 突破传统最坏情况分析的局限,介绍平滑分析(Spielman-Teng 2004)、参数化复杂性、半随机模型等现代分析范式。

  • Erik Demaine (MIT) — “Algorithmic Lower Bounds: Fun with Hardness Proofs” — MIT 6.890 课程,以游戏与谜题为载体讲解 NP 完全性归约的艺术。Demaine 的教学风格极具感染力,适合强化归约直觉。

12.3 开源项目与工具

  • CryptoMiniSathttps://github.com/msoos/cryptominisat。开源 CDCL SAT 求解器,长期参与 SAT Competition 并多次获奖。支持 XOR 子句、Gaussian 消元,特别适合密码学应用中的 SAT 求解。代码结构清晰,是学习工业级 SAT 求解器实现的优秀范本。

  • Google OR-Tools CP-SAThttps://developers.google.com/optimization/cp/cp_solver。Google 开源的约束规划求解器,底层基于 CDCL SAT + LP relaxation + lazy clause generation。支持 Python/C++/Java/C# 多语言绑定,广泛用于车辆路径、员工排班、装箱等工业级组合优化问题。

  • Concorde TSP Solverhttps://github.com/matthias-micheler/concorde-code。Applegate-Bixby-Chvátal-Cook 开发的 TSP 精确求解器,基于 branch-and-cut 方法,曾求解出 pla85900(85,900 个城市)的世界记录实例。配套著作《The Traveling Solver Problem: A Computational Study》是 TSP 研究的权威参考。

  • Pycosathttps://github.com/ContinuumIO/pycosat。Python 接口的 MiniSAT 求解器,轻量易用,适合教学场景与原型开发。结合 Python 的迭代器与生成器可实现简洁的 SAT 应用。

  • NetworkXhttps://networkx.org/。Python 图论算法库,内置 CLIQUE、VERTEX-COVER、HAMILTONIAN 等经典 NP 完全问题的求解接口。适合验证小规模实例的归约正确性。

  • Dimacs CNF Format — DIMACS CNF 是 SAT 求解器的标准输入格式,理解该格式有助于跨工具迁移与基准测试。规范见 https://fairmut3x.github.io/cnfgen/.

12.4 博客文章与教程

  • Scott Aaronson — “Why Philosophers Should Care About Computational Complexity” (2011, Bulletin of Symbolic Logic) — 从哲学视角审视复杂性理论,论证 P vs NP 不仅是一个数学问题,更关乎知识的本质。Aaronson 的写作风格独特,兼顾学术严谨与可读性。

  • Lance Fortnow — “The Status of the P versus NP Problem” (2009, Communications of the ACM) — P vs NP 问题权威综述,梳理了 1971 年 Cook-Levin 定理以来近 40 年的进展与屏障。Fortnow 同时维护复杂性理论博客 Computational Complexity — The Blog,是社区重要阵地。

  • Ryan O’Donnell — “PCP Theorem Primer” 系列 — CMU 教授 Ryan O’Donnell 的 PCP 定理入门教程,从证明系统、概率可检验性、不可近似性的逻辑链逐步展开。配套教材《Analysis of Boolean Functions》是 PCP 理论的现代化教材。

  • ** Luca Trevisan — “In Praise of Hardness”** — Berkeley 教授 Trevisan 关于不可近似性与 PCP 定理的博客系列,强调” hardness is a resource” 的反直觉观点:正是 NP-hardness 使得密码学、伪随机生成器、去随机化成为可能。

  • William Gasarch — “The P vs NP Problem” Poll Results (2002, 2012, 2019) — 每十年一次的复杂性理论社区民意调查,统计研究者对 P vs NP 的预期。2019 年调查中约 84% 受访者认为 P ≠ NP,与 Ladner 定理的蕴含一致。

  • PolyMath Blog — “Deterministic Sensitivity Conjecture” 系列 — 多位数学家协作讨论敏感度猜想(Huang 2019 解决),展示了现代复杂性理论社区的协作研究模式。

12.5 进阶论文方向

  • Geometric Complexity Theory (GCT) — Mulmuley 等提出的代数几何程序,旨在通过代数几何与表示论工具攻克 P vs NP。GCT 是当前少有的”非相对化、非自然证明”的候选程序,但其复杂度极高,至今未有突破。

  • Algebraic Natural Proofs Barrier — Forbes-Shpilka 2018 与 Grochow et al. 2017 提出的代数自然证明屏障,对应 Razborov-Rudich 1997 在代数复杂性理论中的版本,阻拦了大多数电路下界证明技术。

  • Meta-complexity — 研究复杂性理论的”复杂性”,即”计算一个问题的难度有多难”。Hirahara 2018、Oliveira-Santhanam 2017 等最近成果表明,元复杂性方法可能突破传统屏障。

  • Fine-grained Complexity — 在 P ≠ NP 假设之上更强的假设,如 SETH (Strong Exponential Time Hypothesis)、OVH (Orthogonal Vectors Hypothesis)、APSP Hypothesis。Williams 2014 证明 SETH 蕴含多个问题的精确下界,是近年最重要的精细复杂性成果。

  • Quantum Supremacy and BQP — Google 2019 量子优越性实验、IBM 2020 反驳、Pan Jian-Wei 团队 2020 Jiuzhang 光量子计算等实验进展,推动 BQP 量子复杂性类的实证研究。理解 BQP 与 NP 的关系对未来量子时代至关重要。

  • Proof Complexity — 研究证明长度下界,对应 SAT 求解器的指数下界。Razborov 2016 等工作建立了 proof complexity 与 communication complexity 的深刻联系。

13. 总结

13.1 核心知识图谱回顾

算法理论作为计算机科学的数学基础,由两大支柱构成:

  • 可计算性理论(Computability Theory):研究”什么是可计算的”。以 Turing 1936 图灵机与 Church 1936 λ-演算为根基,由 Church-Turing 论题统一。Gödel 1931 不完备性定理、Turing 停机问题、Rice 1953 定理共同确立了不可判定性的普遍性。

  • 计算复杂性理论(Computational Complexity Theory):研究”什么是高效可计算的”。以 Hartmanis-Stearns 1965 复杂性类奠基,Cobham 1964 / Edmonds 1965 P 类形式化为起点,Cook 1971 / Karp 1972 / Levin 1973 NP 完全性发现为里程碑,PCP 定理(Arora-Safra 1998 / Dinur 2007)将不可近似性纳入统一框架。

13.2 三大屏障与未解之谜

P vs NP 千禧年大奖问题之所以悬而未决,源于三大已知屏障:

  1. 相对化屏障(Baker-Gill-Solovay 1975):存在 oracle A 使 PA=NPAP^A = NP^A,也存在 oracle B 使 PBNPBP^B \neq NP^B。任何仅基于”黑盒模拟”的证明技术都无法解决 P vs NP。

  2. 自然证明屏障(Razborov-Rudich 1997):在强伪随机函数存在的前提下,任何满足”构造性”与”广度性”的电路下界证明方法都不可能攻克 NC¹ 以上的下界。

  3. 代数自然证明屏障(Forbes-Shpilka 2018 / Grochow et al. 2017):代数版本的屏障阻拦了 VP vs VNP 的多数已知技术。

突破这三大屏障是当代理论计算机科学最核心的开放问题。Mulmuley 的 GCT 程序、Hirahara 的元复杂性方法、Williams 的算法-下界对应(algorithmic lower bound technique)是当前少数候选的突破方向。

13.3 工程实践的三条准则

理论结果对工程实践有三条直接指导:

  1. 遇到 NP-Hard 问题,先找近似算法:Christofides 1.5-近似 TSP、集合覆盖 ln(n)\ln(n)-近似、PCP 定理给出的不可近似下界,构成了近似算法的工程边界。Google OR-Tools CP-SAT 在工业组合优化中已能处理数万变量的实例。

  2. 数据结构效率看摊还,不看最坏单次:Sleator-Tarjan 1985 的势能法证明了 Splay 树、动态数组、并查集等结构在序列操作下达到最优摊还代价。C++ STL vector::push_back、Python list.append 均依赖此原理。

  3. 在线场景的竞争分析优先于最坏情况:LRU 缓存的 kk-竞争比、Ski Rental 的 2-竞争确定性 / e/(e1)e/(e-1)-竞争随机化、k-Server 问题的 2k12k-1 竞争上界,是缓存、调度、推荐系统的理论基础。

13.4 学习路径建议

按 Bloom 认知层次递进学习:

  • 记忆层:复述 Turing / Church / Gödel / Cook / Karp / Levin 等核心论文的历史脉络与关键贡献;背诵 P / NP / NP-Complete / NP-Hard 形式化定义与归约 4 步法。

  • 理解层:贯通 Cook-Levin 证明思路、Savitch 定理、Baker-Gill-Solovay 相对化、Ladner 定理、PCP 定理的逻辑链;解释为何 NP-Hard 不等于不可解。

  • 应用层:使用聚合 / 核算 / 势能三方法分析动态数组、Splay 树;设计 2-近似 TSP、Christofides 1.5-近似、集合覆盖贪心;编写 DPLL SAT 求解器、Count-Min Sketch、LRU 缓存。

  • 分析层:证明经典归约链 SAT → 3-SAT → CLIQUE → VERTEX-COVER → HAMILTONIAN → TSP 的正确性;识别”归约方向错误”、“摊还误用平均情况”、“误认 BQP 包含 NP”等常见陷阱。

  • 评估层:在精确算法、近似算法、启发式算法之间做工程权衡;识别 P vs NP 为何难解;判断何时使用 SAT 求解器、何时使用 CP-SAT、何时使用 ILP。

  • 创造层:设计基于复杂性理论的工业解决方案(芯片形式化验证、密码学协议设计、推荐系统约束求解);为未来量子时代预留扩展接口(BQP 类、Shor 算法对 RSA 的威胁)。

13.5 未来留白与扩展方向

本文档聚焦经典复杂性理论的核心体系,但以下方向值得未来扩展:

  • 量子复杂性理论:BQP / QMA / BPP / NP 的关系、Shor 算法对密码学的影响、Google 量子优越性实验的理论意义。

  • 参数化复杂性:Downey-Fellows 理论,以参数 kk 为变量的 FPT 算法与 W-hierarchy,是突破 NP-hard 实践困境的现代方法。

  • 算法博弈论:Nisan-Ronen 2001 算法机制设计、TSP 机制、Sperner 引理与 PPAD 完全性问题,连接计算机科学与经济学。

  • 可计算性理论与数理逻辑:Gödel 不完备性定理的更深层次、Cohen 力迫法与连续统假设独立性、Chaitin 不完备性与算法信息论。

  • 元复杂性与电路下界:Hirahara 2018、Chen-Lydot-Wang 2020 等元复杂性突破,以及 Williams 的算法-下界对应方法,是当前 P vs NP 研究的最前沿。


“If P = NP, then the world would be a profoundly different place than we usually assume it to be. There would be no special value in ‘creative leaps,’ no fundamental gap between solving a problem and recognizing the solution once it’s found.” — Scott Aaronson, Why Philosophers Should Care About Computational Complexity (2011)

“We seem to be missing some fundamental insight into the nature of computation. … Any proof technique that resolves P vs NP must be non-relativizing and non-naturalizing.” — Lance Fortnow, The Status of the P versus NP Problem (CACM 2009)

复杂性的故事远未结束——每一位学习者都可能成为下一段历史的执笔人。

返回入门指南