Back to notebook

计算复杂度、渐近记号与计算复杂性理论基础

This post is not yet available in English. Showing the original version.
Table of Contents
Table of Contents

本篇系统梳理算法分析的基础理论体系,包括计算模型与输入规模度量、渐近符号的数学定义与性质、常见程序结构的复杂度推导、主定理(Master Theorem)的应用,以及计算复杂性理论(P、NP、NP-Hard 与 NP-Complete)的核心概念与归约方法。


一、计算模型与输入规模度量

在理论计算机科学中,评估算法效率必须建立在明确的抽象计算模型基础之上。通常采用随机存取机(RAM, Random Access Machine)模型:

1.1 输入规模与位级复杂度

算法的运行时间 T(n)T(n) 是输入规模 nn 的函数。度量输入规模时需严格区分元素个数与数值大小:

  1. 元素集合处理:对含有 nn 个元素的数组执行排序或搜索,输入规模即为元素总数 nn;
  2. 大整数运算与数值问题:当输入为一个大整数 NN(例如素数判定、RSA 模幂计算)时,输入在计算机内部以二进制比特流存储,其真实的输入规模是二进制比特长度:
    ℓ=⌊log⁡2N⌋+1=Θ(log⁡N).\ell = \lfloor \log_2 N \rfloor + 1 = \Theta(\log N).

1.2 伪多项式时间(Pseudo-polynomial Time)

设某算法对于输入实例的处理时间为关于某个输入数值参数 WW 的多项式(例如 O(nW)O(nW))。

这种关于输入数据的“数值大小”呈多项式增长、但关于“输入比特长度”呈指数级增长的算法,被称为伪多项式时间算法。0-1 背包问题的动态规划解法复杂度为 O(nC)O(nC)(CC 为背包容量数值),即属于典型的伪多项式时间。


二、渐近记号体系:O,Ω,ΘO, \Omega, \Theta 的数学严密定义

为了摆脱具体硬件平台、编译器优化及微架构状态(如缓存命中、分支预测)的干扰,算法分析采用渐近分析(Asymptotic Analysis),重点关注输入规模 n→∞n \to \infty 时计算代价的增长阶数。

设 f(n)f(n) 与 g(n)g(n) 为定义在自然数集上的最终非负函数:

2.1 形式化数学定义

  1. 渐近上界 OO(Big-O):
    f(n)=O(g(n))  ⟺  ∃c>0,n0∈N, ∀n≥n0: 0≤f(n)≤c⋅g(n).f(n) = O(g(n)) \iff \exists c > 0, n_0 \in \mathbb{N},\ \forall n \ge n_0:\ 0 \le f(n) \le c \cdot g(n).
    表明当 nn 充分大时,f(n)f(n) 的增长速率不超过 g(n)g(n) 的常数倍。
  2. 渐近下界 Ω\Omega(Big-Omega):
    f(n)=Ω(g(n))  ⟺  ∃c>0,n0∈N, ∀n≥n0: 0≤c⋅g(n)≤f(n).f(n) = \Omega(g(n)) \iff \exists c > 0, n_0 \in \mathbb{N},\ \forall n \ge n_0:\ 0 \le c \cdot g(n) \le f(n).
    表明当 nn 充分大时,f(n)f(n) 的增长速率不低于 g(n)g(n) 的常数倍。
  3. 渐近紧确界 Θ\Theta(Big-Theta):
    f(n)=Θ(g(n))  ⟺  ∃c1,c2>0,n0∈N, ∀n≥n0: 0≤c1⋅g(n)≤f(n)≤c2⋅g(n).f(n) = \Theta(g(n)) \iff \exists c_1, c_2 > 0, n_0 \in \mathbb{N},\ \forall n \ge n_0:\ 0 \le c_1 \cdot g(n) \le f(n) \le c_2 \cdot g(n).
    数学上,当且仅当 f(n)=O(g(n))f(n) = O(g(n)) 且 f(n)=Ω(g(n))f(n) = \Omega(g(n)) 同时成立时,f(n)=Θ(g(n))f(n) = \Theta(g(n))。
flowchart LR
    A["渐近下界: Ω(g(n))<br>(至少为该阶数)"] --- B["渐近紧确界: Θ(g(n))<br>(上下界同阶夹逼)"] --- C["渐近上界: O(g(n))<br>(至多为该阶数)"]

2.2 渐近等价性与常数因子的消除

在渐近分析中,所有正乘法常数和低阶加法项均可被渐近记号吸收:

2.3 常见渐近复杂度的增长率偏序关系

当 n→∞n \to \infty 时,常见复杂度阶数的增长速率由慢到快排列如下:

O(1)≺O(log⁡n)≺O(n)≺O(nlog⁡n)≺O(n2)≺O(n3)≺O(2n)≺O(n!).O(1) \prec O(\log n) \prec O(n) \prec O(n\log n) \prec O(n^2) \prec O(n^3) \prec O(2^n) \prec O(n!).

需注意的重要极限性质:


三、常见程序结构的时间复杂度推导

3.1 双层嵌套循环与算术级数

for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= i; ++j) {
        do_primitive_operation(); // O(1)
    }
}

基本操作的总执行次数为等差数列求和:

T(n)=∑i=1n∑j=1i1=∑i=1ni=n(n+1)2=12n2+12n=Θ(n2).T(n) = \sum_{i=1}^n \sum_{j=1}^i 1 = \sum_{i=1}^n i = \frac{n(n+1)}{2} = \frac{1}{2}n^2 + \frac{1}{2}n = \Theta(n^2).

严谨证明应给出级数的代数闭式,而非仅凭“两层循环嵌套”的主观直觉。

3.2 变量倍增循环与对数阶

for (long long i = 1; i <= n; i *= 2) {
    do_primitive_operation();
}

设循环执行了 kk 次,循环变量 ii 的取值序列为 1,2,4,8,…,2k1, 2, 4, 8, \ldots, 2^k。循环终止条件为 2k>n2^k > n,解得迭代次数为 k=⌊log⁡2n⌋+1=Θ(log⁡n)k = \lfloor \log_2 n \rfloor + 1 = \Theta(\log n)。

3.3 收敛几何级数与线性总代价

考虑外层处理规模逐层减半、内层操作代价与当前规模成正比的结构(例如堆排序中的自底向上建堆过程):

T(n)=∑k=0⌊log⁡2n⌋n2k=n∑k=0⌊log⁡2n⌋(12)k<n∑k=0∞(12)k=n⋅11−1/2=2n=Θ(n).T(n) = \sum_{k=0}^{\lfloor \log_2 n \rfloor} \frac{n}{2^k} = n \sum_{k=0}^{\lfloor \log_2 n \rfloor} \left(\frac{1}{2}\right)^k < n \sum_{k=0}^{\infty} \left(\frac{1}{2}\right)^k = n \cdot \frac{1}{1 - 1/2} = 2n = \Theta(n).

由于公比为 1/21/2 的几何级数强敛,其累加和被常数 2 严格限制,总计算量为线性的 Θ(n)\Theta(n)。与此相对,若内层项为调和级数:
∑i=1nni=n∑i=1n1i=n⋅(ln⁡n+γ+O(1/n))=Θ(nlog⁡n).\sum_{i=1}^n \frac{n}{i} = n \sum_{i=1}^n \frac{1}{i} = n \cdot (\ln n + \gamma + O(1/n)) = \Theta(n\log n).


四、最好、最坏与平均情况复杂度

设 InI_n 为规模为 nn 的合法输入实例的全集,T(x)T(x) 为算法在特定输入 x∈Inx \in I_n 上执行的基本操作步数:

  1. 最坏情况时间复杂度(Worst-case):
    Tmax⁡(n)=max⁡x∈InT(x).T_{\max}(n) = \max_{x \in I_n} T(x).
    给出算法在任意输入下的绝对性能上限保证,是工程与理论分析中最核心的考查指标。
  2. 最好情况时间复杂度(Best-case):
    Tmin⁡(n)=min⁡x∈InT(x).T_{\min}(n) = \min_{x \in I_n} T(x).
    刻画算法在最理想输入下的表现。
  3. 平均情况时间复杂度(Average-case):
    E[T](n)=∑x∈InP(x)⋅T(x).E[T](n) = \sum_{x \in I_n} P(x) \cdot T(x).
    其中 P(x)P(x) 为输入 xx 出现的先验概率分布(通常假设各合法排列等概率独立出现)。

案例:快速排序(Quick Sort)

[!IMPORTANT]
概念辨析:渐近记号(O,Ω,ΘO, \Omega, \Theta)描述的是数学函数的增长阶数,与“最好/最坏/平均情况”属于两个不同正交维度的概念。最坏情况复杂度既可以写出渐近紧确界 Θ(n2)\Theta(n^2),也可以在数学上宽松地写为其上界 O(n3)O(n^3)。


五、递归递推方程与主定理(Master Theorem)

对于分治算法的标准递推关系式:

T(n)=aT(n/b)+f(n),(a≥1,b>1).T(n) = a T(n/b) + f(n),\qquad (a \ge 1, b > 1).

该式表达:将规模为 nn 的问题拆分为 aa 个规模为 n/bn/b 的独立子问题,分解与合并子问题的额外计算代价为 f(n)f(n)。

5.1 递归树结构与能量博弈

递归树的树高为 log⁡bn\log_b n。第 kk 层(k=0,1,…,log⁡bnk = 0, 1, \ldots, \log_b n)的子问题总数为 aka^k,每个子问题规模为 n/bkn/b^k:

flowchart TD
    Comp{"比较 f(n) 与 n^(log_b a)"}
    Comp -->|"情况 1: 叶子节点主导<br>f(n) = O(n^(log_b a - ε))"| C1["T(n) = Θ(n^(log_b a))"]
    Comp -->|"情况 2: 各层工作量平衡<br>f(n) = Θ(n^(log_b a) log^k n)"| C2["T(n) = Θ(n^(log_b a) log^(k+1) n)"]
    Comp -->|"情况 3: 顶层根节点主导<br>f(n) = Ω(n^(log_b a + ε)) 且满足正则条件"| C3["T(n) = Θ(f(n))"]

5.2 主定理三类情形的判定与应用

  1. 情况 1(叶子主导):若存在常数 ε>0\varepsilon > 0,使得 f(n)=O(nlog⁡ba−ε)f(n) = O(n^{\log_b a - \varepsilon}),则:
    T(n)=Θ(nlog⁡ba).T(n) = \Theta(n^{\log_b a}).
    例:T(n)=9T(n/3)+nT(n) = 9T(n/3) + n。此处 a=9,b=3a=9, b=3,nlog⁡39=n2n^{\log_3 9} = n^2。因 f(n)=n=O(n2−1)f(n) = n = O(n^{2 - 1}),故 T(n)=Θ(n2)T(n) = \Theta(n^2)。
  2. 情况 2(各层均衡):若存在 k≥0k \ge 0,使得 f(n)=Θ(nlog⁡balog⁡kn)f(n) = \Theta(n^{\log_b a} \log^k n),则:
    T(n)=Θ(nlog⁡balog⁡k+1n).T(n) = \Theta(n^{\log_b a} \log^{k+1} n).
    例:归并排序 T(n)=2T(n/2)+Θ(n)T(n) = 2T(n/2) + \Theta(n)。a=2,b=2a=2, b=2,nlog⁡22=n1n^{\log_2 2} = n^1。f(n)=Θ(n)f(n) = \Theta(n)(k=0k=0),故 T(n)=Θ(nlog⁡n)T(n) = \Theta(n\log n)。
    例:二分查找 T(n)=T(n/2)+Θ(1)T(n) = T(n/2) + \Theta(1)。a=1,b=2a=1, b=2,nlog⁡21=n0=1n^{\log_2 1} = n^0 = 1。f(n)=Θ(1)f(n) = \Theta(1),故 T(n)=Θ(log⁡n)T(n) = \Theta(\log n)。
  3. 情况 3(根节点主导):若存在常数 ε>0\varepsilon > 0,使得 f(n)=Ω(nlog⁡ba+ε)f(n) = \Omega(n^{\log_b a + \varepsilon}),且满足正则条件 af(n/b)≤cf(n)a f(n/b) \le c f(n)(常数 c<1c < 1),则:
    T(n)=Θ(f(n)).T(n) = \Theta(f(n)).
    例:T(n)=3T(n/4)+nlog⁡nT(n) = 3T(n/4) + n\log n。nlog⁡43≈n0.793n^{\log_4 3} \approx n^{0.793}。f(n)=nlog⁡n=Ω(n0.793+0.2)f(n) = n\log n = \Omega(n^{0.793 + 0.2}),正则性满足,故 T(n)=Θ(nlog⁡n)T(n) = \Theta(n\log n)。

六、计算复杂性理论:P、NP、NP-Hard 与 NP-Complete

计算复杂性理论依据问题在特定计算模型下的内在求解难度对问题进行分类。通常聚焦于判定问题(Decision Problem),即输出仅为“是(Yes)”或“否(No)”的问题(优化问题通常可通过引入目标阈值转化为等价的判定问题)。

flowchart TD
    subgraph NP 类问题全集
        P["P 类问题: 确定性图灵机多项式时间内可判定"]
        NPC["NP-Complete (NPC): NP 中最难的判定问题"]
    end
    NPH["NP-Hard (NP 难): 难度不低于任意 NP 问题 (不要求自身属于 NP)"]
    
    P -->|"P ⊆ NP"| NP
    NPC -->|"所有 NP 问题多项式归约至此"| NPH

6.1 核心概念严格界定

  1. P 类问题(Polynomial-time):
    能够在确定性图灵机(Deterministic Turing Machine)上,在关于输入规模的多项式时间 O(nk)O(n^k) 内被判定的问题集合。
  2. NP 类问题(Nondeterministic Polynomial-time):
    能够在非确定性图灵机上多项式时间内判定的问题集合;或者等价定义为:对于任意回答为“是”的实例,存在一个多项式长度的证书(Certificate),使得确定性图灵机能够在多项式时间内验证该证书的正确性。
    • 注意:NP 的全称为“非确定性多项式”,绝非“非多项式(Non-Polynomial)”。
    • 显然有 P⊆NPP \subseteq NP(若一个问题能在多项式时间内直接求解,则该解自身便可充当多项式时间可验证的证书)。
  3. 多项式时间归约(Polynomial-time Reduction):
    设 AA 和 BB 为两个判定问题。若存在一个多项式时间可计算的映射函数 ff,使得对于问题 AA 的任意实例 xx:
    x∈A  ⟺  f(x)∈B,x \in A \iff f(x) \in B,
    则称问题 AA 可以多项式时间归约到问题 BB,记作 A≤pBA \le_p B。
    • 理论含义:问题 BB 的内在计算难度不低于问题 AA。因为一旦拥有了在多项式时间内求解问题 BB 的算法,便可先花多项式时间将实例 xx 变换为 f(x)f(x),再调用 BB 的求解器间接高效解决 AA。
  4. NP 难问题(NP-Hard):
    若对于所有问题 L∈NPL \in \mathrm{NP},均满足 L≤pHL \le_p H,则称问题 HH 为 NP-Hard 问题(HH 自身不必属于 NP 类,也可以不是判定问题)。
  5. NP 完全问题(NP-Complete, NPC):
    若问题 CC 同时满足以下两个条件:
    1. C∈NPC \in \mathrm{NP};
    2. C∈NP-HardC \in \text{NP-Hard}(即 ∀L∈NP, L≤pC\forall L \in \mathrm{NP},\ L \le_p C)。
      则称 CC 为 NP 完全问题。

6.2 证明新问题为 NP 完全问题的规范证明方向

若欲证明一个新提出的判定问题 YY 是 NP 完全问题,必须严格执行两步:

  1. 证明 Y∈NPY \in \mathrm{NP}:说明如何构造多项式长度的证书,并给出一个在多项式时间内验证证书正确性的确定性算法;
  2. 证明 Y∈NP-HardY \in \text{NP-Hard}:选取一个已知的 NP 完全问题 XX,构造从已知问题 XX 到新问题 YY 的多项式时间归约(即证明 X≤pYX \le_p Y)。

[!WARNING]
常见归约方向错误:
很多初学者常将归约方向颠倒,错误地构造了 Y≤pXY \le_p X。Y≤pXY \le_p X 仅说明“新问题 YY 不比已知难题 XX 更难”,完全无法证明 YY 具备 NP-Hard 的难度等级。必须严格遵循“已知 NPC ≤p\le_p 待证新问题”的推导方向。


七、典型例题与常见易错辨析

7.1 硬件性能提升对不同复杂度问题规模的影响

问题:某算法在当前计算机上 1 小时内可处理规模为 nn 的数据。现将计算机运行速度提升 64 倍,求新计算机在 1 小时内能处理的问题规模 NN。

推导范式:设原运算步数预算为 T(n)T(n),新规模 NN 满足方程式 T(N)≤64⋅T(n)T(N) \le 64 \cdot T(n):

  1. 若 T(n)=nT(n) = n:N≤64nN \le 64n(规模扩大 64 倍);
  2. 若 T(n)=n2T(n) = n^2:N2≤64n2  ⟹  N≤64n=8nN^2 \le 64n^2 \implies N \le \sqrt{64}n = 8n(规模扩大 8 倍);
  3. 若 T(n)=2nT(n) = 2^n:
    2N≤64⋅2n=26⋅2n=2n+6  ⟹  N≤n+6.2^N \le 64 \cdot 2^n = 2^6 \cdot 2^n = 2^{n+6} \implies N \le n + 6.
    由此可见:面对指数阶算法,即使算力发生巨大翻倍,输入规模也仅能获得常数级加法增加(+6+6),这从根本上解释了为何算法设计必须极力追求多项式时间算法。

7.2 概念辨析自查清单



0 / 2000
Loading comments...