SICI · 天机门 · 第五章

效率的度量 · 炼气五层

给我一个循环,什么算不了——这句话,停机之渊记下了。

门楣刻字:「效率为尺」
序章

观澜台上 · 三令牌下注

挑一块「最好的」算法

观澜台悬在万法之上。台下,无数算法如光河流淌——有的湍急,有的缓滞,有的在暗处打转,永不前行。石碑刻着四个字:「效率为尺。」

指令之渊,指令集架构

你嗤笑。算法不就是代码写得快不快?给你一个 for 循环,什么算不了?台角站着一个人影,通体由打孔纸带编织而成——诺依曼老祖。他递过来三块令牌。

诺依曼老祖 诺依曼老祖

「挑一块最好的。」他的声音像机器咬合,没有任何温度。「你敢把直觉押上去吗?」

停机之渊 停机之渊

「最快的最好。」深渊在台下低语。「我替你记账。每一笔,S9 清算。」

三令牌下注 · 逐一执行,看各自的代价

三块令牌各刻一段伪代码。逐一执行——有的快但错,有的对但慢,有的对且快但吃内存。👆 点击「下注·执行」

一个返回错误答案的算法不是算法——它是一个带着热情的错误。「好」不是一个字,是三把尺:正确性、时间、空间。你刚才不是在挑令牌,你是在发现度量的维度。
第一幕 · 自信

渐近之尺 · 大O的诞生

常数不算数,增长率才是永恒判决

尺子入手冰凉。刻度不是数字,是函数。台下光河中浮出九个函数球——1、log₂n、√n、n、n log₂n、n²、n³、2ⁿ、n!——在无标度的虚空中乱作一团。

诺依曼老祖 诺依曼老祖

「把它们从小到大排好。」

停机之渊 停机之渊

「100n² 比 n³ 大得多,不是应该排前面?」

九函数排序 · 按增长率从小到大排到渐近之尺上

点击函数球,按增长率从小到大依次排入尺槽。排错会震动提示。

渐近之尺

1.
2.
3.
4.
5.
6.
7.
8.
9.
九球排完后,三符号槽解锁——拖 n² 到三个槽,发现 O / Ω / Θ。

三符号槽 · O / Ω / Θ 的定义

O(上界)
Ω(下界)
Θ(紧界)
Big-O 不问你今天跑多快——它问当问题长到无穷大时你会以什么姿势死。常数是今天的幻觉,增长率是永恒的判决。
第一幕 · 巅峰

量劫仪 · 三种天命

同一把快排,三种命运

量劫仪是一座三面棱镜,内部悬浮着一段快排伪代码。

诺依曼老祖 诺依曼老祖

「快排。你给它什么,它就是什么命。」

你不信。快排不就是 O(n log n)?尺子上写着的。棱镜第一面亮了——你喂进去一个已排好序的数组。光河猛地滞住,像撞上一堵墙。

三面棱镜 · 喂不同输入,看快排的三种命运

点击下方按钮,逐一喂入三种输入,点亮三面棱镜。

最坏情形
平均情形
最好情形
逐一喂入三种输入,看三条曲线分叉。
你不分析算法——你分析它最倒霉的那天。因为用户不会承诺永远给你好输入,但你可以承诺,无论输入多糟,你的算法不会比「最坏」更差。
第一幕 · 崩塌

分形塔 · 递归的代价

T(n)=aT(n/b)+f(n)——合并代价才是账单

分形塔的每一层都是上一层的缩影——递归的物理化身。塔壁刻着一行递推方程:T(n) = aT(n/b) + f(n)。

诺依曼老祖 诺依曼老祖

「搭一棵递归树,看看每层花多少。」他递来一面镜子——递归之镜,镜框铭刻「映树」。

你心想:分治嘛,二分两半再合并,不就 O(n log n)?你在镜面上写下 T(n) = 2T(n/2) + n²——合并那一步是 n²,你以为无所谓。镜中的递归树长出来了。你数了数每层的总和——n² + n²/2 + n²/4 + …… = 2n²。不是 n log n。是 n²。塔壁裂了一道缝。

递归树 + Master 定理 · 四个递推归类

切换四个预设递推,看递归树每层总和,自动归类到 Master 三区域。

Master 定理三区域

Case 1 · 叶主导
f(n) < n^(log_b a)
Θ(n^(log_b a))
Case 2 · 均衡
f(n) = Θ(n^(log_b a))
Θ(n^(log_b a)·log n)
Case 3 · 根主导
f(n) > n^(log_b a)
Θ(f(n))
切换四个递推,全部归类后,切到「陷阱」做代入法验证。
Master 定理是一把判官尺:合并代价小,叶子说了算;合并代价大,根节点说了算;两者相等,每一层都得交税。你以为分治是免费的午餐——不,合并那一步才是账单。
第二幕 · 震惊

排序刑场 · 不可逾越的下界

有些墙不是技术砌的,是数学砌的

排序刑场是一片开阔的石台。诺依曼老祖:「排好它们。比 O(n log n) 更快。」你不信邪。冒泡太慢?换快排。快排最坏 O(n²)?换堆排。堆排也是 O(n log n)?换归并——还是 O(n log n)。你试了五种排序,没有一种低于 n log n。

停机之渊 停机之渊

「换更聪明的排序,总能比 n log n 快——是吗?」

刑场中央升起一棵巨树——不是递归树,是判定树。每个节点是一次比较,每条路径是一种排列结果。

五排序竞速 + 判定树下界 · Ω(n log n)

拖 n 滑块看五条曲线全部卡在 n log n 以上;展开判定树数叶子;Stirling 展开确认下界。

10
冒泡 O(n²) 插入 O(n²) 归并 O(n log n) 快排 O(n log n) 堆排 O(n log n)

判定树论证

叶子数: 树高:
三步全做:竞速 + 判定树 + Stirling。
这不是在考验你的聪明。n! 种排列需要 log(n!) 次比较才能分辨,而 log(n!) = Ω(n log n)。你找不到更快的比较排序——不是因为你不够能干,是因为它不存在。
第二幕 · 希望→裂痕

散列阁 · O(1)的幻象

O(1) 平均 ≠ O(1) 最坏

散列阁里悬着无数小格,每格有自己的编号。诺依曼老祖递给你一个哈希函数:「把数据丢进去,算个地址,直接放。不比较,不排序。」

你试了——O(1)。丢一个,查一个,都是 O(1)。你笑了:「这就是答案。排序下界?那是比较排序的下界。我不比较。」然后你丢进了第一百个数据。两个数据算出了同一个地址。格子里撞了。

哈希表模拟 · 冲突 + 摊还分析

点击插入数据,看冲突如何发生;拖入恶意输入看退化;展开摊还分析。

插入≥3次 + 触发冲突/恶意输入 + 展开摊还分析。
O(1) 平均不是 O(1) 最坏。摊还是对许多次操作的承诺,不是对任何一次操作的保证。代价没有消失——它被推迟了,攒在一次 rehash 里爆发。
第二幕 · 再崩塌

贪心迷阵 · 局部最优的陷阱

贪心每步都对,结果全错

贪心迷阵的每条岔路口都写着「取最优者行」。第一关活动选择——每步选最早结束的,完美。第二关 Huffman 编码——每步合并频率最低的,完美。你笑了:「每步取最优,全局就最优。」

第三关——0-1 背包。容量 10,三件宝物:重 6 值 12、重 5 值 10、重 5 值 10。你按性价比选了最贵的——重 6 值 12。剩 4 的空间,什么都装不下了。总计值 12。

诺依曼老祖 诺依曼老祖

「另一条路:选两件重 5 值 10——总重 10,总值 20。你每步都选了最优。你的结果比最优差了 40%。」

贪心三关 · 活动选择 → Huffman → 0-1 背包

关卡1:点击活动条选最早结束的;关卡2:构建 Huffman 树;关卡3:看贪心翻车。

关卡1 · 活动选择(选最早结束)

逐一通过三关,看贪心在 0-1 背包上翻车。
贪心不是错的。贪心是瞎的。它在贪心选择性质成立时完美——那是问题的属性,不是算法的功劳。当问题没有这个属性时,贪心每步都对,结果全错。
第二幕 · 再崩塌

记忆之殿 · 重叠的代价

DP 治重复,不治规模

记忆之殿的墙壁上刻满了格子——每一个格子是一个子问题的答案。你在墙上写下递推:OPT(i, w) = max{ OPT(i-1, w), OPT(i-1, w-wᵢ) + vᵢ }。格子逐个填满,最优解 20 浮现。你笑了:「DP。天下无敌。」

诺依曼老祖 诺依曼老祖

递过递归之镜——第二次了。镜面铭刻已变:「映图」。「现在解这个。」他在镜上写下:TSP。

S 是已访问城市的子集。子集有多少?2ⁿ。10 个城市,1024。15 个,32768。20 个——一百万。墙壁裂了。

DP 表格填充 + TSP 状态爆炸

0-1 背包 DP(3 件物品,W=10)

TSP 状态爆炸 · 2ⁿ·n 计数器

5 城
状态数 2ⁿ·n = 160
递归之镜 · 映图——子问题 DAG 在镜中膨胀
填完 DP 表 + 引爆 TSP 状态爆炸。
DP 没有消灭指数——它只是把重叠的指数折成多项式。当子问题不重叠、状态本身指数膨胀时,DP 就是带了一张更大备忘录的暴力搜索。记忆只治「重复」,不治「规模」。
第三幕 · 重燃→逼近

迷图深渊 · 路径与遍历

BFS 水波,DFS 藤蔓,Dijkstra 贪心,Bellman-Ford 补救

迷图深渊是一张巨网——节点如星辰,边如暗河。你吐出一口气。BFS——队列推进,一层一层扫开。DFS——深入到底再回溯。两种走法,O(V+E)。掌控感回来了。

你开始找最短路径——Dijkstra,贪心地取最近节点。干净利落。然后,一条边亮起了暗红色。它的权重是负的。Dijkstra 的光河在那条边前停住了。

诺依曼老祖 诺依曼老祖

「慢一点。Bellman-Ford。什么路都走得通。」

你以 O(VE) 的代价走完了负权图。但当你走到最深的那个节点——脚下的网碎了。下面是深渊。深渊里传来一个声音:「它会停吗?」

图遍历 + 最短路径 · BFS/DFS/Dijkstra/Bellman-Ford

试 BFS/DFS + Dijkstra(非负图)+ 注入负权后试 Dijkstra 失败 + Bellman-Ford 补救。
每个算法都有自己的领地。Dijkstra 在非负图上又快又准——因为它的贪心假设在非负权下成立。Bellman-Ford 在负权图上也能走——但代价是 O(VE)。没有免费的午餐,只有不同的权衡。
终幕 · 对决

停机之渊 · 三问排除法收网

有些问题不是慢——是不存在答案

深渊没有底。它不是黑暗——它是「无法判断」本身。它开口了,声音像无数程序同时在执行又同时卡死:

停机之渊 停机之渊

「你以为更快的算法能解决一切。我来告诉你——有些问题,不是慢。是不存在答案。三问。答得出来,我退。答不出来——你留在渊底。」

诺依曼老祖 诺依曼老祖

递出递归之镜——第三次,铭文已变:「映己」。「你的刻痕,够用了。」

渊账累计 0 笔 · 三问全对后清零

尾声

算法观澜 · 计算的边界

炼气五层 · 算法观澜 · 圆满

观澜台回到脚下。你低头看——石板上的五道刻痕亮了。五道刻痕拼成一幅地图。不是迷路的图——是边境的图。

诺依曼老祖 诺依曼老祖

「你来了的时候说:给我一个循环,什么算不了。现在你知道了。有些东西,算不了。不是因为你不够能干——是因为它们不在计算的范围里。这不是退步。这是你第一次看见了世界的形状。」

知识图谱点亮 · 十二碎片排认知链

点击下方碎片,按因果顺序点亮认知链。

排错处会闪烁红光,提示该知识点依赖的前置知识。

五刻痕回顾 · 触碰刻痕,回闪对应场景

刻痕① 递归无代价 → Master 定理
刻痕② 比较排序破 n log n → 判定树下界
刻痕③ 贪心万能 → 0-1 背包反例
刻痕④ DP 万能 → TSP 状态爆炸
刻痕⑤ 计算万能 → 停机不可判定

递归之镜三阶段

映树(S3)· 递归树
映图(S7)· 子问题图
映己(S9)· 自指悖论

三层计算边界地图

衡量一个计算机科学家的,不是他解决了多少问题——而是他多精确地知道,哪些问题可以被解决。算法是地图——武器有极限,地图告诉你极限在哪。这就是观澜。知止而后有定——边界不是牢笼,是定义。
ch06 计算理论的边界 🔒(点亮全部图谱后开启)