Algorithm Design · Lecture 4-1

先猜,再证:代入法解递推Solve Recurrences by Substitution

上一课收尾时,MergeSort 的账单 T(n) = T(⌈n/2⌉) + T(⌊n/2⌋) + Θ(n) 摊在桌上没结;更早在 3-1,教授承诺过「怎么解递归算法的时间复杂度,周四见」。今天兑现。方法一简单得近乎无赖:先猜出答案,再用数学归纳法验收。猜是艺术(试错 + 经验),证是手艺——而这门手艺的全部秘密,藏在两个长得几乎一样的常数里:你能挑的 c,和早被定死的 c′

✓ 第1–2周:尺子 O·Ω·Θ ✓ 第3周:递归 · 两大排序 ▶ 第4周:拆雷——解递推(代入法) ○ 接下来:展开法 · 递归树
Ke Chen · May 28, 2026 承接 Lecture 3-3 可交互 中英双语
向下滚动开始 · scroll to begin
01

递推式:T(n) 里住着 T 自己 / Recurrence relations

递归算法的正确性,3-1 已经用归纳法收编;可一问「多快」,写出来的式子就开始自我引用。先把这种式子看清楚,再谈怎么解。

教授开场先回了一遍老账:归约 (reduction) 是全学期最重要的设计原则,递归是它的特例——自我归约:干一点活把问题变小,交给「会解同款问题」的下家,拿到结果再拼装。正确性证明顺着算法结构走归纳法,归纳假设就是那句「你信任下家把活干对了」。这些都顺。卡壳的是第二问:这算法多快?设递归算法的时间复杂度为 T(n),照着算法结构一记账,得到的是——

General Form · 递归算法的时间账单(讲义原文)
T(n) = T(n₁) + T(n₂) + ⋯ + T(nk) + f(n)
前一半:k 个递归调用的时间——每个 nᵢ 严格小于 n(3-1 的红字铁律,否则 infinite loop);后一半:非递归部分 f(n)——「把问题变小」的功夫 + 「拼装结果」的功夫,大小往往也随 n 变。

这种定义 T(n) 时右边又用到 T 自己的式子,叫recurrence relation(递推式)。它把算法的运行时间描述得一点不漏——但也一点没解:我们想要的是 L1-3 那种明码标价的「T(n) = O(某个具体函数)」,而不是一句「T 等于几个小号的 T 加点杂费」。三位老朋友的账单都长这样:

Insert 与 Merge(3-2 / 3-3 的子程序): S(n) = S(n−1) + O(1)
InsertionSort: T(n) = T(n−1) + S(n)
MergeSort: T(n) = T(⌈n/2⌉) + T(⌊n/2⌋) + S(n)
两级引爆:先解 S,再解 T 注意 InsertionSort 的账单里嵌着另一个未知函数 S(n)。教授:"in order to solve T(n)… we better first figure out what S(n) is."(要解 T,得先把 S 是 O(什么)搞清楚,代回去,才有一条全已知的递推。)所以今天的战役顺序是钦定的:先打 S(n),再打 T(n)。MergeSort 更棘手——两个递归调用,⌈n/2⌉ 和 ⌊n/2⌋ 一大一小("one may be slightly larger than the other"),先挂着。
本课任务(教授原话) "The entire purpose of today's lectures are to show you some ways to solve this kind of recurrence relations."——给你递推式,怎么答出 T(n) = O(某物)?那个「某物」怎么找?方法一,现在开始。
02

方法一:先猜,再证 / Guess, then verify

教授:"The formal name is solve by substitution, but I'd like to call it solve by guess."(正式名叫代入法,但我更想叫它「猜猜法」。)

第①步 · 猜一个解 Guess
对着递推式,猜 T(n) = O(某个具体函数)。猜从哪来?本页往下看——答案坦率得可爱。
第②步 · 归纳法验收 Verify
猜想不是证明。把猜写成 O/Ω 定义里的证书不等式,用数学归纳法证它。归纳步里要把假设代入 (substitute) 递推式——方法名就是这么来的。

第②步没有任何新数学:把「S(n) = O(n)」按 L1-3 的定义翻译回来,就是「找到某个常数 c,使 S(n) ≤ c·n(对一切 n ≥ n₀)」。第二周肉搏 (c, n₀) 证书的手艺满血回归——只是这次的对手不是显式函数,而是一条递推式,所以证明工具从「代数放缩」换成「归纳法」。这也是递归↔归纳这枚硬币的第三面:3-1 用归纳证递归算法「对不对」,今天用归纳证递归算法「多快」——同一枚硬币,换个刻面。

学生"Is there any systematic way to come up with this guess?"(猜有没有系统方法?)
教授"Unfortunately, not always."(很遗憾,不总有。)这个方法的精神就是:你猜,然后试着证明猜没猜对。不过——"later, like in the next two or three topics, I'm going to show you some kind of systematic or semi-systematic way to figure out a reliable guess for some special form of recurrence."(接下来两三讲会给出半系统的方法,对付特殊形状的递推——就是 4-3 的展开法和 4-4 的递归树。)
教授一般情况呢?讲义最后一页只有六个字的答案:Trial & error + experience(试错 + 经验)。练得够多,看到递推就会条件反射:"ah, I know this one, that must be n squared."
教授的坦白 · 关于「我怎么一猜就中」 "Where is that guess coming from? Well, probably because I know the solution."(这猜哪来的?呃,大概因为我知道答案。)课堂上的每次「幸运一猜」都是排练过的。别慌——猜错了会发生什么,他也专门演示了一遍,就在 §06。
03

两个常数的故事:c 是你的,c′ 是给定的 / Your c vs. their c′

动笔之前先立规矩。这一节是全课的命门——后面每一条推导、每一个陷阱、每一道挑战题,都会回到这两个常数的身份差异上。

归纳证明一开工,场上会出现两个常数。长得像双胞胎,身份天差地别:

c · 你的常数(自由)
来自你要证的目标:S(n) ≤ c·n。O 的定义说「存在常数 c」——存在性命题,挑哪只由你定,而且不急着定:先推着走,推到最后一步看看 c 需要满足什么条件,再回头挑一个够用的。挑法也不唯一:c′ 行,2c′ 行,c′² 也行——"anything that makes the whole inequality work would be a valid choice."
c′ · 给定的常数(固定)
来自递推式里的 O(1) / O(n)。当年某人(通常就是你自己)证明「非递归部分是 O(1)」时,按定义已经选定了一个 c′。教授:"Once it's chosen, it's fixed. You do not have the freedom to choose it anymore."(选完就钉死了,你没有再挑的自由。)讲义里它永远标红,就是在喊:别碰。
为什么这事致命 · 一组具体数字 教授给的体感:"If c is 5, c′ is 10, then you have 15n. It's definitely larger than 5n."——你想证 ≤ 5n,推出来却是 15n,想把 c′ 偷偷调小?没门,它是别人交过的证书,动它等于伪造证据。另一条同样刻死的规矩:c′ 必须为正(O 的定义规定,函数不能被「负倍数」压住)——§06 的失败案例正是死在这个「必须为正」上,无可抢救。
本课配色 = 讲义配色 下面所有推导沿用讲义的颜色语言:绿色 = 归纳假设换进来的部分(可信的旧知识);红色 = 固定常数 c′(碰不得);蓝色 = 要证的目标不等式。盯颜色,比盯字母快。
04

第一战:S(n) = S(n−1) + O(1) / First battle

最小的递推,完整走一遍全流程。之后所有战役——n² 也好、下界也好、期末考卷上的也好——全是这套动作的变奏。

猜:S(n) = O(n)。(每层干常数的活、总共剥 n 层,直觉上是条直线。)按定义翻译成目标:要证 S(n) ≤ c·n,c 由我们挑。归纳三件套,逐件核对:

Base case:居然「不重要」?

讲义原话:"doesn't matter for asymptotic bounds, can assume S(1) = 1."(对渐近界不重要,设 S(1)=1 即可。)教授的解释是一则等比数列的寓言:一列数每次翻倍,从 2 出发变 4,和从 100 万出发变 200 万——增长率一模一样,都是「翻倍」。初始值决定每一项的具体数值,但渐近记号根本不审数值,只审增长率 (growth rate)。所以 base case 随便设:S(1)=1 行,S(1)=10 行,嫌麻烦设 S(5)=15 也行——"whatever convenient for you to complete the proof."(怎么方便怎么来。)这份自由后面会反复用到,先记住:base case 不重要 ≠ 可以没有,而是「具体取值随便」

Inductive hypothesis:故意假设得更强

假设 S(m) ≤ c·m 对一切 m < n 成立——特别地,S(n−1) ≤ c(n−1)。教授特意指出这比「只假设 n−1」更强 ("actually this is something stronger… we're making the hypothesis stronger than what we need")。为什么模板一律写强形式?3-1 的挑战题 C3 已经埋过答案:递推每次剥几层是算法说了算(MergeSort 一刀剥一半!),强形式一步到位、永不翻车。

Inductive step:代入,整理,收口

互动 · 推导链生长机:S(n) = O(n) 的归纳步,一行一行长出来
这一步在干嘛?点「下一步」开始。
收口的手筋 · 「目标 − 正数」必 ≤ 目标 整理出 cn − (c − c′) 之后,要证它 ≤ cn,只需 c − c′ ≥ 0。而 c′ 是死的、c 是活的——挑 c ≥ c′,残差立刻变号。这就是「c 的自由」存在的意义:定义把挑常数的权力交给证明者,证明者就该在最后一刻兑现它。
完整证明:S(n) = O(n)(含 base case 的细账) · Full write-up
命题 存在常数 c 与 n₀,使 S(n) ≤ c·n 对一切 n ≥ n₀ 成立(即 S(n) = O(n))。以下取 n₀ = 1。
拆包 递推里的 O(1) 按定义拆开:存在固定常数 c′ > 0,使非递归部分 ≤ c′(2-2 的「拆包」动作)。以下 c′ 视为已知定数。
Base case 设 S(1) = 1(渐近界不挑初值,取最方便的)。要求 S(1) ≤ c·1,即 c ≥ 1。
Inductive hypothesis 设 n ≥ 2,假设 S(m) ≤ c·m 对一切 1 ≤ m < n 成立;特别地 S(n−1) ≤ c(n−1)。
Inductive step S(n) = S(n−1) + O(1) ≤ c(n−1) + c′ = cn − c + c′ = cn − (c − c′) ≤ cn,最后一步成立当且仅当 c ≥ c′。
交证书 取 c = max{c′, 1}(同时满足 base case 与归纳步;讲义因「base 不重要」直接写 c = c′,精神一致)。于是 S(n) ≤ cn 对一切 n ≥ 1 成立,S(n) = O(n)。∎
品一品 全程只动了一次「自由」:最后一步挑 c。其余每一步都是机械动作——写递推、代假设、拆 O、移项。教授:"The inductive step is really quite straightforward, almost mechanically."(几乎是机械的。)难的从来不是证,是猜。
05

升级战:InsertionSort → O(n²) / Level up

S(n) = O(n) 到手,代回 InsertionSort 的账单:T(n) = T(n−1) + S(n) 变成 T(n) = T(n−1) + O(n)——两级引爆的第二级,点火。

猜:T(n) = O(n²)。(3-2 数循环数出过 Θ(n²),这猜不算冒险——课堂上教授的说法是 "think of it as I'm lucky"。)目标:T(n) ≤ c·n²。假设:T(m) ≤ cm² 对一切 m < n;特别地 T(n−1) ≤ c(n−1)²。代入递推:

T(n) = T(n−1) + O(n)
   ≤ c(n−1)² + c′n
   = cn² − 2cn + c + c′n  (展开 (n−1)² = n² − 2n + 1)
   = cn² + (c′ − 2c)n + c  (按「目标 + 残差」整理)
   ≤ cn²  ⟸ 需要 (c′ − 2c)n + c ≤ 0 对一切 n ≥ n₀
  可取 c = c′、n₀ = 1:残差 = −c′n + c′ = c′(1 − n) ≤ 0 ✓ ∎

和第一战唯一的实质区别:猜想升到二次,展开 (n−1)² 后多出一项 −2cn——正是这笔「负资产」给了我们吃掉 +c′n 的本钱。残差 (c′−2c)n + c 是一条直线:只要斜率 c′−2c 为负,它迟早跌到 0 以下;c 挑得越大,跌得越早。亲手拨一拨:

互动 · 常数调音台:c′ 已锁定,残差归你压

场景:上面的证明推进到「需要残差 R(n) = (c′ − 2c)n + c ≤ 0」。演示中 c′ = 2(固定,碰不得),你只有一个旋钮:自由常数 c。目标:让格子全绿(允许贴线 R = 0——≤ 不怕取等)。

c =0.6
试试 c = 1 / 1.2 / 2 / 4,盯住变号点。
调音台的三个观察c ≤ 1(= c′/2)时永远压不住:斜率 c′−2c ≥ 0,残差不降反升——自由不是万能的,c 得跨过门槛。② c = 1.2 时 n = 3 恰好 R = 0:贴线通过——定义里写的是 ≤,取等不犯规(2-1 的边界精确性,还记得吗)。③ n₀ 前面漏掉的那几个小 n 怎么办?base case 的自由度就是干这个的:有限几个值,全部划进 base case,挨个验掉即可。
完整证明:InsertionSort 的 T(n) = O(n²)(讲义 3/6 全文 + 注解) · Full write-up
已知 T(n) = T(n−1) + O(n),其中 O(n) 部分依定义有固定常数 c′ > 0 使其 ≤ c′n。
目标 找常数 c 使 T(n) ≤ cn² 对一切 n ≥ n₀。
Base 渐近界不挑初值:设 T(1) = 1,则 c ≥ 1 即满足 T(1) ≤ c·1²。
IH 对一切 m < n:T(m) ≤ cm²;特别地 T(n−1) ≤ c(n−1)²。
Step T(n) ≤ c(n−1)² + c′n = cn² − 2cn + c + c′n = cn² + [(c′−2c)n + c]。取 c = c′:方括号 = −c′n + c′ = c′(1−n) ≤ 0 对一切 n ≥ 1,故 T(n) ≤ cn²。
交证书 (c, n₀) = (max{c′, 1}, 1)。T(n) = O(n²)。∎ ——与 3-2 逐行数循环得到的 Θ(n²) 的上半边完全吻合;下半边(Ω)等到 §07。
06

实验室:猜-证机 / Lab: the guess-and-verify machine

同一条递推 T(n) = T(n−1) + O(n),三种猜法,三种命运。教授专门演示了猜错的下场——"later, I'll show you what happens if we guess wrong." 兑现时刻。

互动 · 猜-证机:选一个猜想,逐步验收,看链条在哪一行断裂
猜 T(n) = O(n) 猜 T(n) = O(n²) 猜 T(n) = O(n³)
这一步在干嘛?点「下一步」开始。
断链解剖 · O(n) 死在哪 整理到最后得 (c + c′)n − c。c′ 是正数(定义钉死),所以 c + c′ 严格大于 c——当 n > c/c′ 后,这个量必然超过 cn。于是链条第 3 行写着 ≤、末行写着 >:教授:"that's a contradicting sign. You cannot conclude anything from this chain."(方向矛盾,这条链什么结论也吐不出来。)注意死因:不是算错,是方向拼不上——归纳证明失败了。
重要细节 · 证明失败 ≠ 猜错了 教授特意补刀:"just because the proof doesn't work didn't mean the guess was wrong. It's possible there's some issue with the proof technique."(链断了只说明这条路不通——可能猜错,也可能是证法笨。)逻辑上它只是单向的:猜错 ⇒ 证明必失败;证明失败 ⇏ 猜错。但实践口诀照旧:"the first response would be: maybe I'm guessing something wrong."(第一反应先换猜。)本例我们恰好知道真相——T(n) 真是 Θ(n²)(§07 补下界),O(n) 是货真价实的猜错,链断毫不冤枉。
O(n³) 的启示 · 上界不唯一,越紧越值钱 O(n³) 也能证过!毫不奇怪——"if it's big O of n², it can also be big O of n³, it can also be big O of 2ⁿ."(O 只是上界,松的猜同样为真,只是信息更弱。)那怎么知道手里的上界贴不贴真相?教授透露研究圈的玩法:不断试更小的猜,压到压不动为止——"you just keep doing this to reduce your upper bound gradually until you hit a wall."(撞墙了,大概率就贴着真相了。)墙的另一面是什么?§07 揭晓:下界。
07

下界与三明治:Ω(n²),然后 Θ / Lower bound & the sandwich

上界只承诺「不会更糟」,却不说自己离真相多远。想把真相钉死,得从下面再顶一块板子。这一段,课堂上有个同学替所有人问了两个好问题。

学生为什么要猜一个下界?这跟 Θ 有什么关系?
教授只有上界,你不知道它紧不紧——"we don't know how far away that upper bound is from the ground truth."(不知道离真值有多远:也许真相是 n log n?也许是 n?)办法是上下夹击:"you want to use the upper bound and lower bound to sandwich the range for the true complexity."(用上界和下界做三明治,夹住真相。)两片板子一相遇,就是 Θ——增长率彻底钉死。
学生那为什么递推式里得写 Θ(n)?之前不都是 O(n) 吗?
教授好问题。要证的结论是 T(n) cn²——链条得一路 ≥ 下去。可 O 只提供 ≤,"it's in the wrong direction of the conclusion you want to prove."(方向反了,接不上。)所以非递归部分必须自带下界:Θ(n) 的 Ω 半边保证它 ≥ c′n。想证多紧的结论,递推里就得有多紧的原料。
教授顺带一句现实:"oftentimes you just see people do the upper bound"——简单算法上界明显贴紧,没人费劲写下界;"sometimes you just cannot close the gap."(有时上下界就是合不拢。)但理想情况,我们两头都要,并盼它们相遇。

于是递推升级为 T(n) = T(n−1) + Θ(n)(InsertionSort 的非递归部分确实是 Θ(n):worst case 恰好比较 n−1 次,3-2 数过)。猜:T(n) = Ω(n²)。目标:T(n) ≥ c·n²——注意不等号已调头。整条链条跟着调头:

互动 · 下界推导链:所有箭头调头,c 往「小」挑
这一步在干嘛?点「下一步」开始。
方向感总结 · c 的两副面孔 上界战役里,残差要 ≤ 0,c 得挑够大(c ≥ c′);下界战役里,残差要 ≥ 0,c 得挑够小(c = c′/2,一挑,n 的系数干脆归零,只剩 c′/2 > 0,对一切 n 成立)。自由度永远是单向的:上界嫌不够就加大 c,下界嫌不够就调小 c——反正 O 和 Ω 的定义只要求「存在」,不问大小体面。
合龙 · Θ(n²) 到手 §05 证了 T(n) = O(n²),本节证了 T(n) = Ω(n²)——上下板子相遇,按 2-3 的定义:T(n) = Θ(n²)。InsertionSort 的档位从「数循环数出来的经验值」升格为「递推式解出来的定理」。三明治合龙,一口吃掉。
08

战果清点与真正的悬念 / Scoreboard & the cliffhanger

讲义最后一页(6/6)清点战果——三张账单,两张已结,一张高挂问号。

算法递推式出处
Insert / MergeS(n) = S(n−1) + Θ(1)Θ(n)§04 上界 · 挑战题 C1 下界
InsertionSortT(n) = T(n−1) + Θ(n)Θ(n²)§05 + §07 三明治
MergeSortT(n) = T(⌈n/2⌉) + T(⌊n/2⌋) + Θ(n)?高挂——见下

那个红色问号,教授留了话:"We haven't done anything with merge sort… but it turns out if you make the correct guess, you can also solve it using this method. I will record a separate video for the merge sort case—it's really repetitive."(方法完全够用,只要猜对;单独录一支视频演示。)也就是说,4-1 交给你的武器已经能斩 MergeSort,缺的只是那一发正确的猜。挑战题 C3 会让你先用今天的工具给它箍一个不紧的上界,亲手感受「猜」的份量。

How to guess correctly? · 讲义收官之问 答案就六个字:Trial & error + experience。但别灰心——这门课马上要把「猜」也工程化:4-3 展开法 (unrolling) 把递推一层层摊开直接算出答案;4-4 递归树 (recursion tree) 把账单画成一棵树逐层结算。它们负责「生产靠谱的猜」,代入法负责「盖章验收」——流水线就齐了。
09

配方与陷阱 / Recipe & pitfalls

把整套动作压缩成可以带进考场的清单,再把最容易栽的四个坑插上旗。

代入法七步配方

备料:递推全已知

账单里若嵌着别的未知函数(如 S(n)),先解它、代回,得到一条只含 T 和显式函数的递推。

猜一个解

试错 + 经验(以后有 4-3 / 4-4 帮你生产猜想)。宁可先猜松点——松的能证过,再逐步压紧。

按定义翻译成证书

O:要证 T(n) ≤ c·g(n);Ω:要证 T(n) ≥ c·g(n)。写明:c 由我们挑,暂不定值。

强归纳假设

对一切 m < n 成立;点名递推里实际出现的那几个 m(n−1、⌊n/2⌋……)。base case 的取值随便,先欠着。

代入 + 拆包

递归项用假设换掉(绿);O/Θ 项换成固定常数 c′(红)。从这行起,式子里没有任何渐近记号——全是显式常数。

整理成「目标 ± 残差」,挑 c 收口

上界:让残差 ≤ 0(c 往大挑);下界:让残差 ≥ 0(c 往小挑)。残差在有限几个小 n 上不听话?划进 base case。

交证书

报出 (c, n₀),宣读:"因为能找到这样的常数,按定义,猜想成立。" ∎

四个经典陷阱

坑一 · 把 c′ 当自由变量
"c′ 太大不方便,我取小点" ✗

c′ 是别人(往往是昨天的你)证 O(1)/O(n) 时交过的证书,选定即钉死。你的自由只在 c 上。讲义把 c′ 标红,就是防这只手。

坑二 · 在归纳里携带 O(·)
"≤ cn + n = (c+1)n = O(n),证毕" ✗

归纳步必须收口到同一个 c 的 cn 上;(c+1)n > cn,链根本没接上。写 "= O(n)" 等于每层偷换一次 c——递归 log n 层,c 膨胀成非常数。归纳内部禁用渐近记号,全程显式常数。(案发现场:挑战题 C4。)

坑三 · 证了 O 就宣布 Θ
"T(n) ≤ cn² 证完,故 T(n) = Θ(n²)" ✗

O 只是上界,离真相可能差十万八千里(O(2ⁿ) 也「对」)。要 Θ,必须再打下界——而且递推里得有 Θ 级的原料(§07:O 的方向接不上 ≥ 的链)。

坑四 · 链一断就烧掉猜想
"归纳失败 ⇒ 猜错了" ✗

单向逻辑:猜错 ⇒ 必失败;失败 ⇏ 猜错(可能是证法问题,比如假设形式太弱)。实践上先换猜没错,但下结论要嘴严——教授原话立此存照。

10

挑战题 / Harder problems

四道,难度递增。C1 补上讲义总结页欠的半边账;C3 提前挑战 MergeSort;C4 是一份精心伪造的「证明」,等你验尸。先动笔再看解。

★★ · 把 Θ(n) 的下半边补上
C1. 讲义总结页写 S(n) = S(n−1) + Θ(1) 解得 S(n) = Θ(n),但课上只证了 O(n) 半边。请用代入法证明 S(n) = Ω(n),完成三明治。

提示:照抄 §07 的动作——所有箭头调头。收口时 c 该往哪个方向挑?

题解 · Solution
拆包 Θ(1) 的 Ω 半边:存在固定常数 c′ > 0,使非递归部分 ≥ c′。
目标 找常数 c 使 S(n) ≥ c·n。IH:S(m) ≥ cm 对一切 m < n;特别地 S(n−1) ≥ c(n−1)。
Step S(n) = S(n−1) + Θ(1) ≥ c(n−1) + c′ = cn − (c − c′) ≥ cn ⟸ c − c′ ≤ 0,即 c ≤ c′。取 c = c′ 即可(往小挑也行:c′/2 同样合法)。
Base 设 S(1) = 1,要求 S(1) ≥ c·1,即 c ≤ 1;最终取 c = min{c′, 1}。交证书 (c, n₀) = (min{c′, 1}, 1)。∎
品一品 与 §04 完全镜像:上界要 c ≥ c′,下界要 c ≤ c′。两个方向的自由度各自单向——这正是 O 与 Ω 定义对偶性的肌肉记忆版。O(n) ∧ Ω(n) ⇒ Θ(n),账单结清。
★★★ · 每次砍一半:一条新形状的递推
C2. 某递归算法每层干 O(1) 的活,然后把问题砍到一半:T(n) = T(⌊n/2⌋) + O(1)。猜 T(n) = O(log₂ n),用代入法证明。(小心两件事:log₂ 1 = 0 会闹什么脾气?⌊n/2⌋ 落在归纳假设的覆盖范围内吗?)

提示:n₀ 的自由度不是摆设;⌊n/2⌋ ≤ n/2 且 log 单调。这条递推的形状,几周后你会在 BinarySearch 身上重逢。

题解 · Solution
先排雷 n = 1 时 c·log₂ 1 = 0,而 T(1) 是正常数——目标不等式在 n = 1 处不可能成立。所以把起点定在 n₀ = 2:渐近记号允许我们放弃有限个小 n,这正是 n₀ 存在的意义。
目标 找 c 使 T(n) ≤ c·log₂ n 对一切 n ≥ 2。
Base n = 2 和 n = 3 都当 base case(理由见下):要求 T(2) ≤ c·log₂ 2 = c 及 T(3) ≤ c·log₂ 3,取 c ≥ max{T(2), T(3)/log₂ 3} 即可满足——base case 的自由度再次兜底。
IH 对一切 2 ≤ m < n:T(m) ≤ c·log₂ m。
Step(n ≥ 4) 此时 ⌊n/2⌋ ≥ 2,落在 IH 覆盖范围内(n = 3 会递归到 ⌊3/2⌋ = 1,假设够不着——这就是为什么 3 也划进 base case)。于是 T(n) ≤ c·log₂⌊n/2⌋ + c′ ≤ c·log₂(n/2) + c′(log 单调递增)= c(log₂ n − 1) + c′ = c·log₂ n − (c − c′) ≤ c·log₂ n ⟸ c ≥ c′。
交证书 c = max{c′, T(2), T(3)/log₂ 3},n₀ = 2。∎
品一品 「砍一半」的账单解出来是 log——每次砍半,砍 log₂ n 刀见底,每刀常数。这是全课程最重要的直觉之一,BinarySearch、堆、平衡树全靠它吃饭。
★★★ · 提前挑战 MergeSort:先箍一个不紧的上界
C3. 对 MergeSort 的递推 T(n) = T(⌈n/2⌉) + T(⌊n/2⌋) + O(n),用代入法证明 T(n) = O(n²)。(先在「n 是 2 的幂」的舒适区里做,再挑战一般情形。)想一想:这个上界可信吗?贴得紧吗?

提示:两个递归项都要代入假设——强归纳形式终于显出全部威力。一般情形可用恒等式 a² + b² = ((a+b)² + (a−b)²)/2,其中 a = ⌈n/2⌉、b = ⌊n/2⌋,a + b = n,|a − b| ≤ 1。

题解 · Solution
舒适区(n 是 2 的幂) 两个孩子都是 n/2。T(n) ≤ 2·c(n/2)² + c′n = cn²/2 + c′n ≤ cn² ⟺ c′n ≤ cn²/2 ⟸ c ≥ 2c′/n,对 n ≥ 1 只需 c ≥ 2c′。取 c = 2c′、n₀ = 1,链条闭合。✓
一般情形 设 a = ⌈n/2⌉、b = ⌊n/2⌋,则 a + b = n、|a − b| ≤ 1,于是 a² + b² = (n² + (a−b)²)/2 ≤ (n² + 1)/2。代入:T(n) ≤ c(n²+1)/2 + c′n = cn² − [cn²/2 − c′n − c/2]。取 c = 2c′,方括号 = c′(n² − n − 1),当 n ≥ 2 时 n² − n − 1 ≥ 1 > 0 ✓。交证书 (2c′, 2),n = 1 划入 base case。∎
可信吗? 完全可信——证明无懈可击,MergeSort 确实 = O(n²)。贴得紧吗?直觉说不:InsertionSort 每层砍 1、也才 n²;MergeSort 每刀砍一半,凭什么同档?按 §06 的研究玩法,该继续往下压猜想。压到哪撞墙?2-2 的食物链在 n 和 n² 之间还站着一位:n log n——这正是教授「单独一支视频」要引爆的答案。
★★★★ · 验尸:一份伪造得很体面的「证明」
C4. 有同学对 T(n) = 2T(⌊n/2⌋) + n 宣布 T(n) = O(n),并出示「证明」:
IH:对一切 m < n,T(m) ≤ c·m。
T(n) = 2T(⌊n/2⌋) + n ≤ 2·c·⌊n/2⌋ + n
   ≤ cn + n = (c+1)n = O(n) ∎ ??
每一步算术都对,但结论是错的(这条递推正是 MergeSort 的形状,真解比 n 大)。指出「证明」里唯一但致命的犯规。

提示:重读坑二。归纳到底要求把链条收口到哪个式子上?"= O(n)" 三个字符在归纳内部意味着什么?

题解 · Solution
犯规点 最后一步的 "= O(n)"。归纳要证的命题是一张固定证书:存在一个常数 c,使 T(n) ≤ c·n 对所有 n ≥ n₀ 成立。归纳步必须推出 T(n) ≤ cn——同一个 c。这里推出的是 (c+1)n,而 (c+1)n > cn 恒成立:链条根本没有接回目标,证明在最后一厘米处断裂。
为什么"看起来对" 因为 (c+1)n「确实是 O(n) 的样子」。但在归纳内部写 O(·),等价于每层归纳偷偷把 c 换成 c+1:第 1 层 c+1,第 2 层 c+2……递归深度约 log₂ n 层,「常数」膨胀成 c + log₂ n——随 n 增长,根本不是常数。渐近记号是对「单个函数」的判词,不能在归纳的流水线上逐层引用。
残差验尸 按配方第⑥步整理:cn + n − cn = n,残差 = +n——不管 c 挑成多大的常数,+n 都会长过它,永远压不到 ≤ 0。和 §06 猜 O(n) 的死法一模一样,只是这具「证明」用 O(n) 三个字符把伤口盖住了。
教训(全课最锋利的一条) 归纳证明内部禁用渐近记号:从「代入 + 拆包」那一行起,全程显式常数,每一步都落在同一张证书 (c, n₀) 上。顺带:此递推的真解正是 MergeSort 问号的答案——4-3 / 4-4 的方法会把它算出来,再由代入法盖章。
11

随堂小测 / Quick Check

无限次尝试,零压力。七道题,把「猜—证—两个常数—两个方向」各敲一遍。

Q1. 代入法 (solve by substitution) 的两步是?
Q2. 归纳证明里出现的 c(目标不等式)与 c′(递推里 O(1) 拆包所得)——谁能挑,谁不能?
Q3. 为什么 base case 的具体取值「对渐近界不重要」,可以随手设 S(1) = 1 甚至 S(5) = 15?
Q4. 对 T(n) = T(n−1) + O(n) 猜 T(n) = O(n),归纳链条断在哪?
Q5. 归纳证明失败了。下列哪个说法最准确?
Q6. 想用代入法证 T(n) = Ω(n²),为什么递推的非递归项必须是 Θ(n)(或至少 Ω(n)),只有 O(n) 不行?
Q7. 计算题:S(n) = S(n−1) + O(1),已知拆包后 c′ = 3。归纳步整理为 S(n) ≤ cn − (c − 3)。下列哪个 c 能收口(使 S(n) ≤ cn)?