1.1 - 算法分析的数学基础

数据结构与算法复习

一篇围绕算法分析的数学基础与渐进复杂度展开的学习笔记。


算法分析需要两套前置知识:用于推导求和、比较增长率的数学工具,以及用于量化资源消耗的渐进记号体系。本篇先整理前者(指数、对数、级数、模运算、证明方法),再展开后者(OO、Ω\Omega、Θ\Theta、oo 的定义、运算法则与运行时间计算方法)。

1. 数学基础

1.1 指数

XAXB=XA+BX^A X^B = X^{A+B} XAXB=XA−B\frac{X^A}{X^B} = X^{A-B} (XA)B=XAB(X^A)^B = X^{AB} XN+XN=2XN≠X2NX^N + X^N = 2X^N \neq X^{2N} 2N+2N=2N+12^N + 2^N = 2^{N+1}

1.2 对数

在计算机科学中,若无特别声明,所有对数均以 2 为底。

定义:XA=BX^A = B 当且仅当 log⁡XB=A\log_X B = A。

定理 1.1(换底公式):对任意 C>0C > 0,

log⁡AB=log⁡CBlog⁡CA\log_A B = \frac{\log_C B}{\log_C A}

定理 1.2:

log⁡AB=log⁡A+log⁡B\log AB = \log A + \log B

常用等式:

公式说明
log⁡A/B=log⁡A−log⁡B\log A/B = \log A - \log B
log⁡(AB)=Blog⁡A\log(A^B) = B \log A
log⁡X<X\log X < X对所有 X>0X > 0 成立
log⁡1=0, log⁡2=1\log 1 = 0,\ \log 2 = 1
log⁡1024=10, log⁡1,048,576=20\log 1024 = 10,\ \log 1,048,576 = 20

1.3 级数

几何级数:

∑i=0NAi=AN+1−1A−1(A≠1)\sum_{i=0}^{N} A^i = \frac{A^{N+1} - 1}{A - 1} \quad (A \neq 1)

当 0<A<10 < A < 1 且 N→∞N \to \infty 时,∑i=0∞Ai=11−A\displaystyle \sum_{i=0}^{\infty} A^i = \frac{1}{1 - A}。

常用算术级数:

∑i=1Ni=N(N+1)2≈N22\sum_{i=1}^{N} i = \frac{N(N+1)}{2} \approx \frac{N^2}{2} ∑i=1Ni2=N(N+1)(2N+1)6≈N33\sum_{i=1}^{N} i^2 = \frac{N(N+1)(2N+1)}{6} \approx \frac{N^3}{3} ∑i=1Nik≈Nk+1∣k+1∣(k≠−1)\sum_{i=1}^{N} i^k \approx \frac{N^{k+1}}{|k+1|} \quad (k \neq -1)

调和级数:

HN=∑i=1N1i≈ln⁡N+γH_N = \sum_{i=1}^{N} \frac{1}{i} \approx \ln N + \gamma

其中 γ≈0.57721566\gamma \approx 0.57721566(欧拉常数)。HN=Θ(log⁡N)H_N = \Theta(\log N)。

级数运算常用恒等式:

∑i=1Nf(N)=Nf(N)\sum_{i=1}^{N} f(N) = N f(N) ∑i=n0Nf(i)=∑i=1Nf(i)−∑i=1n0−1f(i)\sum_{i=n_0}^{N} f(i) = \sum_{i=1}^{N} f(i) - \sum_{i=1}^{n_0-1} f(i)

1.4 模运算

若 NN 整除 A−BA - B,则称 AA 与 BB 模 NN 同余,记为 A≡B(modN)A \equiv B \pmod{N}。直观上即 AA 和 BB 除以 NN 的余数相同。

性质示例
若 A≡B(modN)A \equiv B \pmod{N},则 A+C≡B+C(modN)A+C \equiv B+C \pmod{N}81≡61≡1(mod10)81 \equiv 61 \equiv 1 \pmod{10}
若 A≡B(modN)A \equiv B \pmod{N},则 AD≡BD(modN)AD \equiv BD \pmod{N}

1.5 证明方法

算法分析中最常使用的三种证明方法:

方法思路结构
归纳法证明最小情形(基准)成立,再假设 kk 成立来证 k+1k+1 成立基准情形 → 归纳假设 → 归纳步骤
反证法假设结论为假,推导出矛盾假设 ¬P → 推导 → 矛盾 → 故 P 成立
反例法举出一个不满足结论的具体实例直接给出反例即可推翻命题

归纳法示例——证明斐波那契数 Fi=Fi−1+Fi−2F_i = F_{i-1} + F_{i-2}(F0=1,F1=1F_0 = 1, F_1 = 1)满足 Fi<(5/3)iF_i < (5/3)^i:

  • 基准:F1=1<5/3F_1 = 1 < 5/3,F2=2<25/9F_2 = 2 < 25/9
  • 归纳:假设对 i=1,2,…,ki = 1,2,\ldots,k 成立,则
Fk+1=Fk+Fk−1<(5/3)k+(5/3)k−1=(5/3)k−1⋅(5/3+1)<(5/3)k−1⋅(5/3)2=(5/3)k+1F_{k+1} = F_k + F_{k-1} < (5/3)^k + (5/3)^{k-1} = (5/3)^{k-1} \cdot (5/3 + 1) < (5/3)^{k-1} \cdot (5/3)^2 = (5/3)^{k+1}

2. 复杂度分析

2.1 渐进记号

评估算法资源消耗时,比较的是函数的相对增长率(relative rate of growth),而非具体数值。渐进记号体系提供了一套正式框架。

定义:设 T(N)T(N) 和 f(N)f(N) 为定义在正整数上的函数。

记号读法定义含义
T(N)=O(f(N))T(N) = O(f(N))大 O存在 c>0c > 0 和 n0n_0,使得 N≥n0N \ge n_0 时 T(N)≤cf(N)T(N) \le c f(N)T(N)T(N) 增长率 ≤ f(N)f(N)(上界)
T(N)=Ω(g(N))T(N) = \Omega(g(N))Omega存在 c>0c > 0 和 n0n_0,使得 N≥n0N \ge n_0 时 T(N)≥cg(N)T(N) \ge c g(N)T(N)T(N) 增长率 ≥ g(N)g(N)(下界)
T(N)=Θ(h(N))T(N) = \Theta(h(N))ThetaT(N)=O(h(N))T(N) = O(h(N)) 且 T(N)=Ω(h(N))T(N) = \Omega(h(N))T(N)T(N) 增长率 = h(N)h(N)(紧界)
T(N)=o(p(N))T(N) = o(p(N))小 olim⁡N→∞T(N)p(N)=0\displaystyle \lim_{N \to \infty} \frac{T(N)}{p(N)} = 0T(N)T(N) 增长率 < p(N)p(N)(严格上界)

注意:f(N)≤O(g(N))f(N) \le O(g(N)) 是错误的写法——不等式已隐含在 OO 的定义中。同时,不要在 OO 中保留常数和低阶项:O(2N2)O(2N^2) 和 O(N2+N)O(N^2+N) 都应写为 O(N2)O(N^2)

2.2 增长率比较

极限比较法:对于两个函数 f(N)f(N) 和 g(N)g(N),计算 lim⁡N→∞f(N)g(N)\displaystyle \lim_{N \to \infty} \frac{f(N)}{g(N)}:

极限值结论
00f(N)=o(g(N))f(N) = o(g(N))
有限非零常数 ccf(N)=Θ(g(N))f(N) = \Theta(g(N))
∞\inftyg(N)=o(f(N))g(N) = o(f(N))

查表法:

函数名称典型场景
cc常数简单语句
log⁡N\log N对数二分查找
log⁡2N\log^2 N对数的平方
N\sqrt{N}平方根
NN线性顺序扫描
Nlog⁡NN \log N线性对数归并排序、堆排序
N2N^2平方冒泡排序、选择排序
N3N^3立方矩阵乘法(朴素)
2N2^N指数穷举搜索

增长率由慢到快:

c<log⁡N<log⁡2N<N<N<Nlog⁡N<N2<N3<2Nc < \log N < \log^2 N < \sqrt{N} < N < N \log N < N^2 < N^3 < 2^N

值得注意的是:log⁡N\log N 增长极其缓慢——log⁡kN=O(N)\log^k N = O(N) 对任意常数 kk 成立。

2.3 运算法则

法则 1:若 T1(N)=O(f(N))T_1(N) = O(f(N)) 且 T2(N)=O(g(N))T_2(N) = O(g(N)),则

T1(N)+T2(N)=max⁡(O(f(N)), O(g(N)))T1(N)⋅T2(N)=O(f(N)⋅g(N))\begin{aligned}T_1(N) + T_2(N) &= \max\bigl(O(f(N)),\, O(g(N))\bigr) \\T_1(N) \cdot T_2(N) &= O\bigl(f(N) \cdot g(N)\bigr)\end{aligned}

法则 2:若 T(N)T(N) 是 kk 次多项式,则 T(N)=Θ(Nk)T(N) = \Theta(N^k)。

法则 3:对任意常数 kk,log⁡kN=O(N)\log^k N = O(N)。

推论——复杂度分析中的简化原则:

原则说明
忽略常数因子O(1000N)=O(N)O(1000N) = O(N)
忽略低阶项O(N2+N)=O(N2)O(N^2 + N) = O(N^2)
加法取最大循环并列时复杂度取各段最大者
乘法取乘积嵌套循环时复杂度取各层乘积

2.4 运行时间计算

分析运行时间的基本方法:逐语句累加运行时间,忽略常数,关注最内层循环的迭代次数。

a. 单层循环

c
int sum(int n) {    int total = 0;               // O(1)    for (int i = 0; i < n; i++) // n 次迭代        total += i;              // O(1) 每轮    return total;                // O(1)}

总时间:O(1)+n⋅O(1)+O(1)=O(N)O(1) + n \cdot O(1) + O(1) = O(N)。

b. 嵌套循环

c
for (int i = 0; i < n; i++)         // n 次    for (int j = 0; j < n; j++)     // n 次/每轮        count++;                     // O(1)

总时间:n⋅n⋅O(1)=O(N2)n \cdot n \cdot O(1) = O(N^2)。

c. 依赖外层的循环

c
for (int i = 0; i < n; i++)         // n 次    for (int j = 0; j < i; j++)     // i 次/每轮        count++;
∑i=0n−1i=n(n−1)2=O(N2)\sum_{i=0}^{n-1} i = \frac{n(n-1)}{2} = O(N^2)

d. 对数时间

c
while (n > 1) {    n /= 2;          // 每轮将 n 减半    /* O(1) 操作 */}

循环次数为 ⌈log⁡2n⌉\lceil \log_2 n \rceil,总时间 O(log⁡N)O(\log N)。

e. 递归函数

递归函数的时间复杂度通过递推关系式描述,判断复杂度的关键在于确定

  1. 递归层数 NrN_{r}
  2. 第 ii 层子任务数 nin_{i} (n1=1),i∈[1,Nr](n_{1} = 1), i \in [1,N_{r}]
  3. 每层每个子任务的规模 TiT_{i} (往往并非常数,与层级有关)

则总复杂度可以表示如下:

T(N)=∑i=1Nr(ni⋅Ti)T(N) = \sum_{i=1}^{N_{r}} (n_{i} \cdot T_{i})

也就是将每层的总规模计算后,逐层累加。

情形 1:T(N)=T(N−1)+O(1)T(N) = T(N-1) + O(1)

递归深度为 NN,每层一个子问题,每个子问题规模为 O(1)O(1),因此

T(N)=N⋅O(1)=O(N)T(N) = N \cdot O(1)=O(N)

情形 2:T(N)=2T(N−1)+O(1)T(N) = 2T(N-1) + O(1)

每个问题分为两个子问题,第 ii 层有 2i2^i 个子问题,每个子问题规模为 O(1)O(1),该层总工作量为 2i⋅O(1)2^i \cdot O(1) ,共 NN 层,因此累加可得

∑i=1N2i⋅O(1)=O(2N)\sum_{i=1}^{N} 2^i \cdot O(1) = O(2^N)

情形 3:T(N)=T(N/2)+O(1)T(N) = T(N/2) + O(1)

递归深度为 log⁡N\log N,每层一个子问题,每个子问题规模为 O(1)O(1),因此

T(N)=log⁡N⋅O(1)=O(log⁡N)T(N) = \log N \cdot O(1)=O(\log N)

情形 4:T(N)=2T(N/2)+O(N)T(N) = 2T(N/2) + O(N)

递归深度为 log⁡N\log N,第 ii 层有 2i2^i 个子问题,每个子问题规模为 O(N/2i)O(N/2^i),该层总工作量为 2i⋅O(N/2i)=O(N)2^i \cdot O(N/2^i) = O(N) ,因此

T(N)=log⁡N⋅O(N)=O(Nlog⁡N)T(N) = \log N \cdot O(N)=O(N \log N)
递推式含义复杂度典型算法
T(N)=T(N−1)+O(1)T(N) = T(N-1) + O(1)逐次减一 + 常数时间O(N)O(N)线性递归(如阶乘)
T(N)=2T(N−1)+O(1)T(N) = 2T(N-1) + O(1)指数分支 + 常数时间O(2N)O(2^N)斐波那契朴素递归
T(N)=T(N/2)+O(1)T(N) = T(N/2) + O(1)减半 + 常数时间O(log⁡N)O(\log N)二分查找
T(N)=2T(N/2)+O(N)T(N) = 2T(N/2) + O(N)两分 + 线性时间合并O(Nlog⁡N)O(N \log N)归并排序

小结

主题核心内容
数学基础指数恒等式、对数换底公式、几何/算术/调和级数、模运算同余性质、归纳法与反证法
渐进记号OO(上界)、Ω\Omega(下界)、Θ\Theta(紧界)、oo(严格上界);用极限比较增长率的快慢
运算法则加法取最大、乘法取乘积、忽略常数和低阶项、多项式取最高次
运行时间计算逐句累加 → 关注最内层循环 → 查级数公式求和 → 递归问题检查每层工作量与分支数