知识焦点
先建立概念地图
- asymptotic notation
- Big-O
- Big-Omega
- Big-Theta
- limit
- L'Hôpital's rule
- Master Theorem
- recurrence relation
CS170 · 讨论课 1
本讨论课讲义涵盖渐近记号(O、Θ、Ω)的定义与极限证明、渐近复杂度比较、利用主定理与展开法求解递推关系、基于二分查找的「寻找谷底」算法,以及选做的 Hadamard 矩阵与其 O(n log n) 矩阵-向量乘法。注意:助教可能无法在课堂上覆盖所有题目,讲义故意设计得较长,旨在作为课后练习与巩固课堂、阅读和作业内容的资源。
知识焦点
建议顺序
先完整作答,再按需展开提示;最后才展开官方参考解答并逐步核对证明与计算。
本地来源
题面与解答均由本地一手资料整理:dis01.pdf、dis01-sol.pdf
本地一手资料完整提取 · 讨论课
本节按本地一手资料完整整理,共 5 道大题。本地官方解答已完整收录并默认折叠;建议先独立完成,再逐题核对推导。
dis01.pdf、dis01-sol.pdf本门课程非常关注算法的运行时间,但我们不太在意小输入规模上的具体表现(大多数算法在小输入上表现良好),而是希望比较运行时间的长期(渐近)增长。
渐近记号(Asymptotic Notation):以下是 \(O(\cdot)\)、\(\Theta(\cdot)\)、\(\Omega(\cdot)\) 的定义。
\(f(n) = O(g(n))\):若存在常数 \(c > 0\),使得当 \(n\) 充分大时,\(f(n) \le c \cdot g(n)\)。(渐近意义下,\(f\) 的增长至多与 \(g\) 相当)
\(f(n) = \Omega(g(n))\):若 \(g(n) = O(f(n))\)。(渐近意义下,\(f\) 的增长至少与 \(g\) 相当)
\(f(n) = \Theta(g(n))\):若 \(f(n) = O(g(n))\) 且 \(g(n) = O(f(n))\)。(渐近意义下,\(f\) 与 \(g\) 增长相同)
若将这些定义与数的序关系比较,\(O\) 类似于 \(\le\),\(\Omega\) 类似于 \(\ge\),\(\Theta\) 类似于 \(=\)(不过皆针对渐近行为而言)。
渐近极限法则(Asymptotic Limit Rules):设 \(f(n), g(n) \ge 0\)。
若 \(\displaystyle\lim_{n \to \infty} \frac{f(n)}{g(n)} < \infty\),则 \(f(n) = O(g(n))\)。
若 \(\displaystyle\lim_{n \to \infty} \frac{f(n)}{g(n)} = c\),其中常数 \(c > 0\),则 \(f(n) = \Theta(g(n))\)。
若 \(\displaystyle\lim_{n \to \infty} \frac{f(n)}{g(n)} > 0\),则 \(f(n) = \Omega(g(n))\)。
注意:以上皆是涉及极限的充分条件,并非 \(O\)、\(\Theta\)、\(\Omega\) 的真正定义。(你应该自行验证这些命题的正确性!)
(a) 证明 \(n^3 = O(n^4)\)。
(b) 找出满足 \(f(n), g(n) \ge 0\) 的函数 \(f(n), g(n)\),使得 \(f(n) = O(g(n))\),但 \(\displaystyle\lim_{n \to \infty} \frac{f(n)}{g(n)} \ne 0\)。
(c) 证明:对任意常数 \(c > 0\),都有 \(\log n = O(n^c)\)。
提示:使用洛必达法则(L'Hôpital's rule):若 \(\lim_{n \to \infty} f(n) = \lim_{n \to \infty} g(n) = \infty\),则 \(\lim_{n \to \infty} \frac{f(n)}{g(n)} = \lim_{n \to \infty} \frac{f'(n)}{g'(n)}\)(若右端极限存在)。
(d) 找出满足 \(f(n), g(n) \ge 0\) 的函数 \(f(n), g(n)\),使得 \(f(n) = O(g(n))\),但 \(\displaystyle\lim_{n \to \infty} \frac{f(n)}{g(n)}\) 不存在。在这种情况下,你将无法使用极限来证明 \(f(n) = O(g(n))\)。
提示:考虑振荡函数!
对于涉及极限的题目,先尝试直接计算 \(\lim_{n\to\infty} f(n)/g(n)\);若极限为 0、正常数、无穷或不存在,分别对应不同的渐近关系。对于无法用极限处理的情形,回到 \(O\) 的定义:寻找常数 \(c\) 使得 \(f(n) \le c\cdot g(n)\) 在 \(n\) 充分大时成立。
对于振荡函数类题目(如 (d)),考虑一个被常数上界压制、但比值的极限因持续振荡而不存在的函数,例如让 \(f(n)\) 在两个固定倍数之间来回摆动。
(a) \[ \lim_{n \to \infty} \frac{n^3}{n^4} = \lim_{n \to \infty} \frac{1}{n} = 0 \] 因此 \(f(n) = O(g(n))\)。
(b) 令 \(f(n) = 3n\),\(g(n) = 5n\)。则 \(\lim_{n \to \infty} \frac{f(n)}{g(n)} = \frac{3}{5}\),即 \(f(n) = \Theta(g(n))\)。由 \(\Theta\) 的定义,此时仍有 \(f(n) = O(g(n))\) 成立。
(c) 由洛必达法则: \[ \lim_{n \to \infty} \frac{\log n}{n^c} = \lim_{n \to \infty} \frac{n^{-1}}{c n^{c-1}} = \lim_{n \to \infty} \frac{1}{c n^c} = 0 \] 因此 \(\log n = O(n^c)\)。
(d) 令 \(f(x) = x(\sin x + 1)\),\(g(x) = x\)。由于 \(\sin x + 1 \le 2\),故对所有 \(x \ge 0\) 有 \(f(x) \le 2 \cdot g(x)\),所以 \(f(x) = O(g(x))\)。
然而,若尝试计算极限: \[ \lim_{x \to \infty} \frac{x(\sin x + 1)}{x} = \lim_{x \to \infty} (\sin x + 1) \] 该极限不存在(正弦函数永远振荡)。
解答来源:dis01-sol.pdf · 1-2 · confidence: high
题面来源:dis01.pdf · 1-2 · confidence: high
(a) 将下列函数排序,使得对所有 \(i, j\),若 \(f_i\) 排在 \(f_j\) 之前,则 \(f_i = O(f_j)\)。无需证明你的答案。
\(f_1(n) = 3n\)
\(f_2(n) = n^{1/3}\)
\(f_3(n) = 12\)
\(f_4(n) = 2^{\log_2 n}\)
\(f_5(n) = \sqrt{n}\)
\(f_6(n) = 2^n\)
\(f_7(n) = \log_2 n\)
\(f_8(n) = 2^{\sqrt{n}}\)
\(f_9(n) = n^3\)
答案可以只写成函数列表,例如 \(f_8, f_9, f_1, \ldots\)
(b) 在以下各小题中,判断 \(f = O(g)\)、\(f = \Omega(g)\),还是两者都成立(此时 \(f = \Theta(g)\))。对每个答案给出简要证明。回顾:按渐近增长率,对数 < 多项式 < 指数。
| \(f(n)\) | \(g(n)\) | |
|---|---|---|
| (i) | \(\log_3 n\) | \(\log_4 n\) |
| (ii) | \(n \log(n^4)\) | \(n^2 \log(n^3)\) |
| (iii) | \(\sqrt{n}\) | \((\log n)^3\) |
| (iv) | \(n + \log n\) | \(n + (\log n)^2\) |
| f(n) | g(n) | |
|---|---|---|
| (i) | log_3 n | log_4 n |
| (ii) | n log(n^4) | n^2 log(n^3) |
| (iii) | \(\sqrt{n}\) | (log n)^3 |
| (iv) | n + log n | n + (log n)^2 |
来源:dis01.pdf · 3
对于 (a),先化简每个函数:\(2^{\log_2 n} = n\)。再按「常数 < 对数 < 幂次根号 < 线性 < 多项式 < 根号指数 < 指数」的框架排序。对于 (b),常用手段包括换底公式、提取主项、以及洛必达法则。
自检方法:排序后取相邻两项验证 \(f_i = O(f_{i+1})\) 是否成立;对 (b) 每个小题检查是否忽略了低阶项或常数因子的影响。
(a) \(f_3, f_7, f_2, f_5, f_4, f_9, f_8, f_6, f_1\)
(b) (i) \(f = \Theta(g)\);利用对数换底公式,\(\frac{\log n}{\log 3}\) 与 \(\frac{\log n}{\log 4}\) 仅差一个常数因子。
(ii) \(f = O(g)\);\(f(n) = 4n \log n\),\(g(n) = 3n^2 \log n\),\(g\) 中多项式次数更高。
(iii) \(f = \Omega(g)\);任何多项式都支配对数乘积。亦可通过以下极限证明: \[ \lim_{n \to \infty} \frac{f(n)}{g(n)} = \lim_{n \to \infty} \frac{\sqrt{n}}{(\log n)^3} = \lim_{n \to \infty} \frac{\frac{1}{2\sqrt{n}}}{3(\log n)^2 \cdot \frac{1}{n}} \text{ [洛必达法则]} = \lim_{n \to \infty} \frac{\sqrt{n}}{6(\log n)^2} = \lim_{n \to \infty} \frac{\sqrt{n}}{24 \log n} \text{ [洛必达法则]} = \lim_{n \to \infty} \frac{\sqrt{n}}{48} \text{ [洛必达法则]} = \infty \]
(iv) \(f = \Theta(g)\);\(f\) 和 \(g\) 都呈 \(\Theta(n)\) 增长,因为线性项主导了其他项。亦可通过以下极限证明: \[ \lim_{n \to \infty} \frac{f(n)}{g(n)} = \lim_{n \to \infty} \frac{n + \log n}{n + (\log n)^2} = \lim_{n \to \infty} \frac{1 + \frac{1}{n}}{1 + \frac{2 \log n}{n}} \text{ [洛必达法则]} = 1 \]
解答来源:dis01-sol.pdf · 3-4 · confidence: high
题面来源:dis01.pdf · 3 · confidence: high
主定理(Master Theorem):若递推关系形如 \(T(n) = aT\left(\frac{n}{b}\right) + O(n^d)\),其中常数 \(a > 0\),\(b > 1\),\(d \ge 0\),则 \[ T(n) = \begin{cases} O(n^d) & \text{若 } d > \log_b a \\ O(n^d \log n) & \text{若 } d = \log_b a \\ O(n^{\log_b a}) & \text{若 } d < \log_b a \end{cases} \]
注意,若递推关系不在 \(T(n) = aT\left(\frac{n}{b}\right) + O(n^d)\) 的形式,就不能使用主定理!此时可以尝试以下策略:
还有更多本课程范围之外的策略,感兴趣的读者推荐阅读:http://www.iiitdm.ac.in/old/Faculty_Teaching/Sadagopan/pdf/DAA/recurrence-relations-V3.pdf
求解以下递推关系,假设基本情况 \(T(0) = T(1) = 1\):
(a) \(T(n) = 2 \cdot T(n/2) + O(n)\)
(b) \(T(n) = T(n - 1) + n\)
(c) \(T(n) = 3 \cdot T(n - 2) + 5\)
(d) \(T(n) = 2 \cdot T(n/2) + O(n \log n)\)
递归树层级结构:
第 0 层:1 个节点,工作量 O(n log n)
第 1 层:2 个节点,每个 O((n/2) log(n/2))
第 2 层:4 个节点,每个 O((n/4) log(n/4))
...
第 i 层:2^i 个节点,每个 O((n/2^i) log(n/2^i))
叶节点层:共 log n 层,叶节点工作量 O(1)
来源:dis01-sol.pdf · 7
对于 (a),识别 \(a, b, d\) 并套用主定理;注意 \(\log_b a\) 与 \(d\) 的大小关系。对于 (b) 和 (c),它们不是 \(aT(n/b)\) 形式,需要逐层展开寻找规律,注意等差/等比数列求和。对于 (d),主定理不直接适用,尝试递归树方法,逐层求和。
自检方法:展开后检查基本情况的系数是否正确;对 (d) 的级数求和确认是否化简为 \(\sum n(\log n - i)\) 的形式。
(a) 可以使用主定理!注意到 \(a = 2\),\(b = 2\),\(d = 1\),有 \(\log_b a = \log_2 2 = 1 = d\)。因此由主定理: \[ T(n) = O(n^d \log n) = O(n \log n) \]
(b) 由于无法使用主定理,采用「展开」策略: \[ \begin{aligned} T(n) &= T(n-1) + n \\ &= (T(n-2) + (n-1)) + n \\ &= ((T(n-3) + (n-2)) + (n-1)) + n \\ &= \cdots \text{(继续展开)} \\ &= T(1) + 2 + 3 + \cdots + (n-2) + (n-1) + n \\ &= 1 + 2 + 3 + \cdots + (n-2) + (n-1) + n \\ &= \sum_{i=1}^{n} i \\ &= \frac{n(n+1)}{2} \\ &= O(n^2) \end{aligned} \]
(c) 继续展开! \[ \begin{aligned} T(n) &= 3T(n-2) + 5 \\ &= 3^2 T(n-4) + 5 \cdot 3 + 5 \\ &= 3^3 T(n-6) + 5 \cdot 3^2 + 5 \cdot 3 + 5 \\ &= 3^4 T(n-8) + 5 \cdot 3^3 + 5 \cdot 3^2 + 5 \cdot 3 + 5 \\ &= \cdots \\ &= 3^{\lfloor n/2 \rfloor} T(n \bmod 2) + 5 \cdot 3^{\lfloor n/2 \rfloor - 1} + 5 \cdot 3^{\lfloor n/2 \rfloor - 2} + \cdots + 5 \cdot 3^2 + 5 \cdot 3 + 5 \\ &= 1 \cdot 3^{\lfloor n/2 \rfloor} + 5 \cdot 3^{\lfloor n/2 \rfloor - 1} + 5 \cdot 3^{\lfloor n/2 \rfloor - 2} + \cdots + 5 \cdot 3^2 + 5 \cdot 3 + 5 \\ &= 3^{\lfloor n/2 \rfloor} + 5 \cdot \frac{3^{\lfloor n/2 \rfloor} - 1}{3 - 1} \\ &= \frac{7}{2} \cdot 3^{\lfloor n/2 \rfloor} - \frac{5}{2} \\ &= O(3^{n/2}) \end{aligned} \]
(d) 使用递归树方法。节点表示一次递归调用所做的工作:
第 0 层:\(O(n \log n)\) 第 1 层:\(2\) 个节点,每个 \(O\left(\frac{n}{2} \log \frac{n}{2}\right)\),合计 \(O\left(n \log \frac{n}{2}\right)\) 第 2 层:\(4\) 个节点,每个 \(O\left(\frac{n}{4} \log \frac{n}{4}\right)\),合计 \(O\left(n \log \frac{n}{4}\right)\) \(\ldots\)
将各层相加: \[ \begin{aligned} T(n) &= O(n \log n) + O\left(n \log \frac{n}{2}\right) + O\left(n \log \frac{n}{4}\right) + \cdots + O(1) \\ &= O\left(\sum_{i=0}^{\lfloor \log n \rfloor} n \log \frac{n}{2^i}\right) \\ &= O\left(\sum_{i=0}^{\lfloor \log n \rfloor} n (\log n - i)\right) \\ &= O\left(n \left(\log^2 n - \sum_{i=0}^{\lfloor \log n \rfloor} i\right)\right) \\ &= O\left(n \left(\log^2 n - \frac{1}{2} \log^2 n\right)\right) \\ &= O(n \log^2 n) \end{aligned} \]
解答来源:dis01-sol.pdf · 5-7 · confidence: high
题面来源:dis01.pdf · 4 · confidence: high
给定一个长度为 \(N\) 的整数数组 \(A\)。\(A\) 具有以下性质:它在元素 \(j\) 之前单调递减,从 \(j\) 开始单调递增。换句话说,存在某个 \(j\),使得当 \(i < j\) 时有 \(A[i] > A[i+1]\),当 \(i \ge j\) 时有 \(A[i] < A[i+1]\)。在事先不知道 \(j\) 的前提下,给出一个高效算法找出 \(j\)。
为简单起见,可以假设 \(N\) 是 2 的幂。
这本质上是一个在「先减后增」单峰序列上的二分查找问题。关键观察:比较中间两个元素 \(A[N/2]\) 与 \(A[N/2 + 1]\) 的大小关系,就能判断谷底 \(j\) 落在左半还是右半。
自检方法:考虑边界情况——当数组只剩一个元素时,它是否一定是谷底?验证递归后下标的偏移是否正确。
(i) 主要思路:若 \(A\) 只有 1 个元素,直接输出 1。否则,检查 \(A[N/2] < A[N/2 + 1]\) 是否成立。若成立,则递归调用算法于子数组 \(A[1 : N/2]\)。否则,递归调用算法于子数组 \(A[N/2 + 1 : N]\)(并将 \(N/2\) 加到答案上)。
(ii) 正确性证明:当 \(A\) 的大小为 1 时,算法正确,因为 \(j\) 唯一可能的取值就是 1。若 \(A[N/2] < A[N/2 + 1]\),则我们知道 \(j \le N/2\),因此 \(j\) 包含在我们递归调用的子数组中,可以归纳地假设递归调用找到了正确的下标。否则,我们知道 \(j > N/2\),同样有 \(j\) 包含在递归调用的子数组中,并且可以归纳地假设递归调用找到了正确的下标(在加上 \(N/2\) 之前)。
(iii) 运行时间分析:算法花费 \(O(1)\) 时间比较 \(A[N/2]\) 与 \(A[N/2 + 1]\)。每次比较后我们将问题规模减半,因此从长度为 \(N\) 的数组到长度为 1 的数组需要 \(\log N\) 次递归调用。故总运行时间为 \(O(\log N)\)。
解答来源:dis01-sol.pdf · 8 · confidence: high
题面来源:dis01.pdf · 5 · confidence: high
Hadamard 矩阵(Hadamard matrices)\(H_0, H_1, H_2, \ldots\) 定义如下:
\(H_0\) 是 \(1 \times 1\) 矩阵 \([1]\)
对 \(k > 0\),\(H_k\) 是 \(2^k \times 2^k\) 矩阵: \[ H_k = \begin{bmatrix} H_{k-1} & H_{k-1} \\ H_{k-1} & -H_{k-1} \end{bmatrix} \]
(a) 写出 Hadamard 矩阵 \(H_0\)、\(H_1\) 和 \(H_2\)。
(b) 计算矩阵-向量乘积 \(H_2 \cdot v\),其中 \(H_2\) 是你在上面求得的 Hadamard 矩阵,且 \[ v = \begin{bmatrix} 1 \\ -1 \\ -1 \\ 1 \end{bmatrix} \] 注意由于 \(H_2\) 是 \(4 \times 4\) 矩阵,向量长度为 4,结果将是一个长度为 4 的向量。
(c) 现在我们计算另一个量。取 \(v_1\) 和 \(v_2\) 分别为 \(v\) 的上半部分和下半部分,即 \[ v_1 = \begin{bmatrix} 1 \\ -1 \end{bmatrix}, \quad v_2 = \begin{bmatrix} -1 \\ 1 \end{bmatrix}, \quad v = \begin{bmatrix} v_1 \\ v_2 \end{bmatrix} \] 计算 \(u_1 = H_1(v_1 + v_2)\) 和 \(u_2 = H_1(v_1 - v_2)\),得到两个长度为 2 的向量。将 \(u_1\) 叠在 \(u_2\) 上方得到长度为 4 的向量 \(u\)。你观察到 \(u\) 有什么特点?
(d) 设 \[ v = \begin{bmatrix} v_1 \\ v_2 \end{bmatrix} \] 是长度为 \(n = 2^k\) 的列向量。\(v_1\) 和 \(v_2\) 分别是该向量的上半部分和下半部分,因此它们各自是长度为 \(n/2 = 2^{k-1}\) 的向量。用 \(H_{k-1}\)、\(v_1\)、\(v_2\) 表示矩阵-向量乘积 \(H_k v\)(注意 \(H_{k-1}\) 是维度为 \(n/2 \times n/2\),即 \(2^{k-1} \times 2^{k-1}\) 的矩阵)。由于 \(H_k\) 是 \(n \times n\) 矩阵,\(v\) 是长度为 \(n\) 的向量,结果将是长度为 \(n\) 的向量。
(e) 利用 (c) 的结果,给出一个计算矩阵-向量乘积 \(H_k v\) 的分治算法,并证明它可以在 \(O(n \log n)\) 次操作内完成。假设涉及的数都足够小,基本的算术运算(如加法和乘法)都花费单位时间。你不需要证明正确性。
H0 = [1]
H1 = [[1, 1], [1, -1]]
H2 = [[1, 1, 1, 1], [1, -1, 1, -1], [1, 1, -1, -1], [1, -1, -1, 1]]
来源:dis01-sol.pdf · 9
H_k v = [H_{k-1} v_1 + H_{k-1} v_2; H_{k-1} v_1 - H_{k-1} v_2] = [H_{k-1}(v_1+v_2); H_{k-1}(v_1-v_2)]
来源:dis01-sol.pdf · 10
对于 (a)-(c),从定义出发逐步计算,观察分块矩阵与向量上半/下半部分的关系;对于 (d),将 \(v\) 按上半 \(v_1\)、下半 \(v_2\) 分块,写出 \(H_k v\) 的分块形式;对于 (e),利用 (d) 将问题归约为两个规模为 \(n/2\) 的子问题,加上 \(O(n)\) 的合并代价。
自检方法:验证 (c) 得到的 \(u\) 是否等于 (b) 的 \(H_2 v\);对 (e) 检查递推式 \(T(n) = 2T(n/2) + O(n)\) 是否符合主定理情形二。
(a) \[ H_0 = [1] \] \[ H_1 = \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} \] \[ H_2 = \begin{bmatrix} 1 & 1 & 1 & 1 \\ 1 & -1 & 1 & -1 \\ 1 & 1 & -1 & -1 \\ 1 & -1 & -1 & 1 \end{bmatrix} \]
(b) \[ H_2 v = \begin{bmatrix} 1 & 1 & 1 & 1 \\ 1 & -1 & 1 & -1 \\ 1 & 1 & -1 & -1 \\ 1 & -1 & -1 & 1 \end{bmatrix} \begin{bmatrix} 1 \\ -1 \\ -1 \\ 1 \end{bmatrix} = \begin{bmatrix} 0 \\ 0 \\ 0 \\ 4 \end{bmatrix} \]
(c) \[ H_1(v_1 + v_2) = \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} \begin{bmatrix} 0 \\ 0 \end{bmatrix} = \begin{bmatrix} 0 \\ 0 \end{bmatrix} \] \[ H_1(v_1 - v_2) = \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} \begin{bmatrix} 2 \\ -2 \end{bmatrix} = \begin{bmatrix} 0 \\ 4 \end{bmatrix} \] 我们观察到 \[ u = \begin{bmatrix} 0 \\ 0 \\ 0 \\ 4 \end{bmatrix} = H_2 v \]
(d) \[ H_k v = \begin{bmatrix} H_{k-1} v_1 + H_{k-1} v_2 \\ H_{k-1} v_1 - H_{k-1} v_2 \end{bmatrix} = \begin{bmatrix} H_{k-1}(v_1 + v_2) \\ H_{k-1}(v_1 - v_2) \end{bmatrix} \]
(e) 计算 (d) 中描述的两个子问题,并按 (c) 的方式合并。设 \(T(n)\) 表示计算 \(H_k v\) 所需的操作数。我们需要求出向量 \(v_1 + v_2\) 和 \(v_1 - v_2\),这需要 \(O(n)\) 次操作。我们还需要计算矩阵-向量乘积 \(H_{k-1}(v_1 + v_2)\) 和 \(H_{k-1}(v_1 - v_2)\),各需要 \(T(n/2)\) 次操作。因此运行时间的递推关系为: \[ T(n) = 2T\left(\frac{n}{2}\right) + O(n) \] 使用主定理,得到 \(T(n) = O(n \log n)\)。
解答来源:dis01-sol.pdf · 9-11 · confidence: high
题面来源:dis01.pdf · 6-7 · confidence: high
dis01.pdfdis01-sol.pdf