上一篇:(五)验证很快,找到很难——讲有些事能算但算不动。 这一篇:讲世界是不确定的,而概率是唯一能驾驭不确定性的工具。
耗时:读完约 50 分钟,动手约 2 小时。
开头:一个检测,准确率 99%,测出阳性,你慌不慌
假设你去做一项体检,医生说:
这个检测准确率 99%。
结果出来:阳性。
你觉得自己有病的概率是多少?99%?90%?
正确答案:可能不到 10%。
这不是抬杠。这是一个真实的、反复被证实的数学事实。而它之所以反复骗到人,是因为它踩中了人类直觉的一个盲区——我们把"检测准确率"和"我有病"混为一谈了。
这一篇就从这里讲起。前面五篇都在讲"确定的东西"——格点、离散、证明、可计算、复杂度。但真实世界里,大多数判断是在不确定下做的:体检、风控、垃圾邮件、天气预报、抽卡、推荐系统、AI 的每一步。
概率论,是计算机科学里唯一能处理"不确定"的数学。 而且它有一个反直觉到骨子里的特性:用随机,反而能算得更准、更快。
一、贝叶斯定理:把"直觉"翻译成"公式"
先说那个检测。关键是要区分三个概念,大多数人只听过一个:
| 概念 | 问什么 | 例子 |
|---|---|---|
| 敏感性(真阳性率) | 真有病的人,测出阳性的概率 | 99% |
| 特异性(真阴性率) | 没病的人,测出阴性的概率 | 99% |
| 患病率(先验) | 随机一个人有病的概率 | 0.1% |
“准确率 99%“这句话,什么都没说清楚。 它可能是敏感性 99%、特异性 99%,也可能只是其中一个。而结论完全取决于这三个数怎么组合。
贝叶斯定理一句话
阳性后真有病的概率(PPV)= 真阳性 ÷ 阳性总数
写成公式(这就是贝叶斯定理):
PPV = 患病率 × 敏感性 ÷ [患病率 × 敏感性 + (1−患病率) × (1−特异性)]
实测:10 万人模拟
我模拟了 10 万人(患病率 0.1%,敏感性 99%,特异性 99%):
真病人 100 人
健康人 99,900 人
真阳性 TP = 99 人 ← 100 个病人,99 个被查出
假阳性 FP = 999 人 ← 99,900 个健康人,1% 被误判 = 999 人
阳性总数 = 1,098 人
阳性中真有病 = 99 ÷ 1,098 = 9.02%
你测出阳性,真有病的概率是 9.02%。
为什么这么低?因为假阳性的人数(999)是真阳性人数(99)的 10 倍。
- 病很罕见(0.1%),所以绝大多数受检者是健康人
- 健康人里 1% 被误判 → 数量巨大
- 病人里 99% 被查出 → 但病人本来就少
“准确率 99%“掩盖了"健康人太多了"这个事实。
患病率一换,结论天翻地覆
同一个检测(敏感性 99%、特异性 99%),换不同的患病率:
| 患病率 | 阳性后真有病 |
|---|---|
| 0.1% | 9.02% |
| 0.5% | 33.22% |
| 1.0% | 50.00% |
| 5.0% | 83.90% |
| 10.0% | 91.67% |
| 30.0% | 97.70% |
同样的检测,同样的 99%,在罕见病上是 9%,在常见病上是 97%。
这就是贝叶斯定理最核心的一课:结论不只取决于"检测多准”,更取决于"这件事本来就多常见”。
特异性动一点点,结论翻五倍
再看一个反直觉的:
特异性 99% → PPV = 9.02%
特异性 99.9% → PPV = 49.77%
特异性只提高 0.9 个百分点,PPV 从 9% 跳到 50%。
因为假阳性的人数直接除以 10,而假阳性正是压垮 PPV 的那部分。
为什么"准确率"这个词在医学和风控里几乎没用? 因为它把敏感性和特异性混成一个数。而这两个数对结论的影响,一个在分子、一个在分母,方向完全不同。
二、条件概率的陷阱:先验比你想的重要
贝叶斯定理逼你面对一个不舒服的事实:你的判断,取决于你先相信什么(先验)。
这不是"主观”,这是数学结构。换一种说法就更刺人:
说法 A:"这个检测准确率 99%"
说法 B:"这个检测的假阳性率是 1%"
这两句话是同一件事。 但 A 让你觉得"很可靠”,B 让你开始警惕。
而真正该问的是第三个:“这件事本身有多常见?” —— 先验。没有它,前两句话都不完整。
计算机里的贝叶斯无处不在:
- 垃圾邮件过滤器(贝叶斯分类器)—— 一个词出现在垃圾邮件里的概率,要乘以"垃圾邮件本身的占比"
- 推荐系统 —— “你喜欢这个” 要乘以 “你本来就喜欢这类东西”
- 异常检测 —— “这个行为像攻击” 要乘以 “攻击本来就很罕见”(否则整天误报)
三、用随机,反而更快更准:随机化算法
前面是"用概率理解世界"。现在反过来:用概率当工具。
这是概率论最反直觉的应用——故意引入随机性,让算法变得更好。
随机快排:治"最坏情况"
快速排序有个臭名昭著的问题:输入已经有序时,它会退化。
实测(输入已经是升序排列):
| n | 确定性快排(取第一个为基准) | 随机化快排(随机选基准) |
|---|---|---|
| 300 | 5.5 ms | 0.7 ms |
| 600 | 19.5 ms | 1.7 ms |
| 1000 | 41.8 ms | 1.7 ms |
同样的问题,随机选基准,快了 20 倍以上。
为什么?确定性快排选固定位置当基准,敌人(或者巧合)可以构造出最坏输入。随机化快排随机选基准——不存在一个"最坏输入",因为输入不知道你会选谁。
这是随机化的第一课:它把"最坏情况"从"输入能控制的"变成"纯运气"。 运气可以被分析(期望值),敌意不能。
Miller-Rabin:用"可能"换"肯定"
判断一个大数是不是素数。确定性方法(试除法)要试到 √n 次;n 一大就完蛋。
Miller-Rabin 是一个概率算法:它不保证 100% 正确,但错误概率可以压到任意小(比如 10⁻³⁰)。
实测:
| 数 | 试除法 | Miller-Rabin |
|---|---|---|
| 1,000,003 | 0.15 ms | 0.04 ms |
| 1,000,000,007 | 4.61 ms | 0.05 ms |
| 1,000,000,000,039 | 189.33 ms | 0.12 ms |
| 2¹²⁷−1(39 位) | 不可行(要试 1.3×10¹⁹ 次) | 0.7 ms |
2¹²⁷−1 这个数,试除法宇宙终结都试不完,Miller-Rabin 半毫秒给出"素数",且错误概率小于十亿分之一。
这是随机化的第二课:用一点点"可能错"的代价,换取"肯定算不动"变成"瞬间算完"。 而"可能错"这个代价,可以压到比你被雷劈还小的概率。
蒙特卡洛:用随机算 π
最纯粹的例子。π 是个确定的东西,但用随机能算它:
往正方形里撒点,落在内切圆里的比例,乘 4 就是 π 的估计。
| 点数 | π 估计 | 误差 |
|---|---|---|
| 1,000 | 3.140000 | 0.0016 |
| 10,000 | 3.152800 | 0.0112 |
| 100,000 | 3.145200 | 0.0036 |
| 1,000,000 | 3.138636 | 0.0030 |
| 10,000,000 | 3.141839 | 0.0002 |
撒点撒得越多,越准。 但注意收敛速度:
误差按 1/√n 缩小。想精确 10 倍,需要 100 倍的点数。
这就是蒙特卡洛方法的代价:它永远到不了"精确",但能到"足够好",而且对任何问题都通用。 高维积分、粒子物理、金融风险、AI 里的采样——全是它。
四、三个反直觉概率题(全都实测过)
1. 三门问题:换门,赢率翻倍
你面前三扇门,一扇后面是车,两扇是羊。你选一扇,主持人打开另外两扇中的一扇(一定是羊),问你:换不换?
直觉:剩下两扇,一半一半,换不换都行。错。
实测 20 万次:
不换:赢 33.26%
换: 赢 66.74%
换门,赢率翻倍。
为什么?你第一次选对的概率是 1/3。主持人开一扇羊门,没有改变"你最初那扇门是 1/3"这个事实——他开门这件事,把剩下的 2/3 概率集中到了那一扇没开的门上。
关键:主持人的动作不是随机的,他"必须开一扇羊门"。这个信息改变了概率。
2. 生日悖论:23 人就有一半概率撞
10 人 → 11.8%
20 人 → 41.0%
23 人 → 51.4% ← 过半
30 人 → 70.5%
50 人 → 97.0%
23 个人里,有两个人同一天生日的概率超过一半。 直觉上觉得要 100 多人才行。
原因是第二篇讲过的:你要比的是所有配对(C(23,2)=253 对),不是"某个人和我同一天"。
3. 抽卡:1% 出率,抽 100 次,出卡率只有 63%
抽 10 次 → 9.5%
抽 50 次 → 39.4%
抽 100 次 → 63.5% ← 不是 100%!
抽 300 次 → 95.0%
1% 出率,抽 100 次,至少出一次的概率只有 63%,不是 100%。
因为 0.99¹⁰⁰ ≈ 0.366 —— 一次都不出的概率是 36.6%。
这里藏着两个最常见的错觉:
- “期望 100 次出” ≠ “100 次必出”。期望值是平均,不是保证。
- 抽了很多次没出,不代表"下次概率变大"。每一次都是独立的 1%。
五、大数定律:为什么"手气"是错觉
大数定律
试验次数越多,观测频率越接近真实概率。
实测抛硬币(20 组取平均和最大偏离):
| n | 最偏离 |
|---|---|
| 10 | 0.40 |
| 100 | 0.08 |
| 1,000 | 0.019 |
| 10,000 | 0.013 |
| 100,000 | 0.0046 |
| 1,000,000 | 0.0007 |
n 越大,偏离越小。 这是数学保证的,不是运气。
但注意:这不是"补偿"
很多人误解成"抛了 5 次正面,反面该来了"。这是赌徒谬误。
实测:连出 5 次正面后,第 6 次正面的概率:
实测 50.44%(理论 50.00%)
硬币没有记忆。 “该出反面了"是错觉。
大数定律说的不是"偏差会补回来”,是"偏差会被稀释"。 抛了 5 次正面,你已经偏离了 5 次。但接下来抛 10 万次,这 5 次的偏离在 10 万次里被稀释到可以忽略。 过去的偏离不会被"纠正",只会被"淹没"。
六、期望值:理性决策的唯一依据
概率论给你一个判断标准:期望值。
期望 = 每个结果的价值 × 它的概率,然后加起来。
彩票为什么是坑?因为期望值恒为负——你花的钱,大于你平均赢回来的钱。
抽卡为什么让人上瘾?因为稀有度×概率的期望值,被设计成"看起来值"但实际亏——而人脑对"小概率大回报"有系统性的高估。
在计算机里,期望值是无处不在的决策工具:
- 缓存策略:存这个还是存那个?看哪个的"命中概率 × 命中收益"高
- A/B 测试:哪个版本好?看哪个的期望指标高
- 负载均衡:请求发给谁?期望让响应时间最短
- 机器学习:损失函数本质就是"期望损失",训练就是在最小化它
用期望值做决策,是把"我觉得"换成"平均来看"。 单次可能错,长期必然对。
七、把这一篇放回体系
六篇讲完,八层目录已经走完前六层:
| 层 | 状态 |
|---|---|
| 一 数怎么装进机器 | ✅ |
| 二 离散结构 | ✅ |
| 三 怎么保证对 | ✅ |
| 四 什么能算 | ✅ |
| 五 算得多快 | ✅ |
| 六 不确定的世界 | ✅ 本篇 |
| 七 连续的近似(微积分) | 待写 |
| 八 高维的表示(线代) | 待写 |
这一篇和前五篇的关系
前面五篇都在讲"确定":
- 格点是确定的(一)
- 离散结构是确定的(二)
- 证明是确定的(三)
- 可计算性是确定的(四)
- 复杂度是确定的(五)
这一篇第一次引入"不确定"。 但它不是"放弃精确",而是给不确定装上数学框架:
- 贝叶斯:不确定下怎么更新信念
- 随机化:用不确定当工具,反而更快更准
- 大数定律:不确定下的长期规律
- 期望值:不确定下的决策标准
这是从"计算机是精确的"到"计算机活在不确定世界里"的转折。
八、动手:六个实验
实验 1:那个 99% 的检测
import random
random.seed(1)
def 模拟(患病率, 敏感性, 特异性, N=100000):
病人 = int(N*患病率); 健康 = N - 病人
TP = int(病人*敏感性); FP = int(健康*(1-特异性))
pos = TP + FP
return TP/(pos) if pos else 0
print(f"患病率 0.1%,99%/99%:阳性后真有病 {模拟(0.001,0.99,0.99)*100:.2f}%")
for prev in [0.001, 0.005, 0.01, 0.05, 0.1, 0.3]:
print(f" 患病率 {prev*100:4.1f}% → {模拟(prev,0.99,0.99)*100:6.2f}%")
亲手看到"同样 99%,罕见病 9%、常见病 97%"。
实验 2:三门问题
import random
random.seed(4)
N = 200000; 换赢 = 不换赢 = 0
for _ in range(N):
车 = random.randrange(3); 选 = random.randrange(3)
可开 = [i for i in range(3) if i != 选 and i != 车]
主持开 = random.choice(可开)
换 = [i for i in range(3) if i != 选 and i != 主持开][0]
if 换 == 车: 换赢 += 1
if 选 == 车: 不换赢 += 1
print(f"不换 {不换赢/N*100:.1f}% 换 {换赢/N*100:.1f}%")
实验 3:生日悖论
import random
random.seed(4)
def 撞(m, trials=20000):
hit = 0
for _ in range(trials):
b = [random.randrange(365) for _ in range(m)]
if len(set(b)) < m: hit += 1
return hit/trials
for m in [10, 20, 23, 30, 50]:
print(f"{m:2d} 人 → {撞(m)*100:.1f}%")
实验 4:随机快排 vs 确定性快排(有序输入)
import random, sys, time
sys.setrecursionlimit(100000)
def q_det(a):
if len(a) <= 1: return a
return q_det([x for x in a[1:] if x < a[0]]) + [a[0]] + q_det([x for x in a[1:] if x >= a[0]])
def q_rand(a):
if len(a) <= 1: return a
i = random.randrange(len(a)); a = a[:]; a[0], a[i] = a[i], a[0]
return q_rand([x for x in a[1:] if x < a[0]]) + [a[0]] + q_rand([x for x in a[1:] if x >= a[0]])
s = list(range(1000))
t = time.time(); q_det(s); print(f"确定性 {time.time()-t:.3f}s")
t = time.time(); q_rand(s); print(f"随机化 {time.time()-t:.3f}s")
实验 5:蒙特卡洛求 π
import random, math
random.seed(9)
def pi(n):
inside = sum(1 for _ in range(n)
if (lambda x, y: x*x+y*y <= 1)(random.random(), random.random()))
return 4*inside/n
for n in [1000, 100000, 10000000]:
print(f"{n:>9,} 点 → {pi(n):.6f} (π={math.pi})")
实验 6:赌徒谬误
import random
random.seed(42)
cnt = tot = 0
for _ in range(500000):
seq = [random.random() < 0.5 for _ in range(6)]
if all(seq[:5]):
tot += 1
if seq[5]: cnt += 1
print(f"连出 5 次正面后,第 6 次正面概率 {cnt/tot*100:.2f}%")
九、带走的东西
一句话:
世界是不确定的,概率是唯一能驾驭它的数学——而它反直觉到极致:用随机,反而能算得更快更准。
三句话:
- 贝叶斯定理:结论取决于"先验",不只取决于"证据"。 99% 准确的检测,罕见病上阳性后只有 9% 真有病——因为健康人太多,假阳性淹没了真阳性。
- 随机化算法:故意引入随机,消除最坏情况。 随机快排让"最坏输入"不存在;Miller-Rabin 用十亿分之一的可错概率,换"试除法永远算不完"变成"半毫秒"。
- 大数定律不是"补偿",是"稀释"。 硬币没有记忆,过去的偏离不会被纠正,只会被淹没。
一个最该记住的认知升级
前面五篇讲"确定",这一篇讲"不确定"。 但概率不是"放弃精确",是给不确定装上数学框架: 贝叶斯更新信念、随机化当工具、大数定律看长期、期望值做决策。
AI 的每一步——训练、推理、采样、决策——本质都是概率。 不懂这一篇,就看不懂 AI 在干什么。
下一篇
(七)连续的近似:微积分。为什么"梯度下降"能让 AI 学会东西、为什么"导数"是"变化率"、为什么计算机处理连续的世界要靠"逼近"而不是"精确"。
这一篇会把前面所有"离散"的东西,接到"连续"上——因为 AI 的底层,就是在用离散的机器逼近连续的数学。
深水区
- 概率论主干:任意一本《概率论》教材,或 Sheldon Ross《A First Course in Probability》。
- 贝叶斯:E.T. Jaynes《Probability Theory: The Logic of Science》——把贝叶斯讲到哲学高度的名著。
- 随机化算法:Mitzenmacher & Upfal《Probability and Computing》——概率和算法的结合,正好接这一篇。
- 蒙特卡洛:任何一本《Monte Carlo Methods》教材。
这些我不做页码引用——不同版本页码不同,写了就是骗你。按书名和章节找。
附:本篇所有数字的来源
全部本机跑出来:Ubuntu / Python 3.8 / x86-64。
贝叶斯:患病率 0.1%、敏感性 99%、特异性 99%,10 万人模拟 → 真阳 99、假阳 999、PPV=9.02%;不同患病率 0.1%/0.5%/1%/5%/10%/30% → 9.02%/33.22%/50.00%/83.90%/91.67%/97.70%。
随机快排(有序输入):n=300/600/1000,确定性 5.5/19.5/41.8 ms,随机化 0.7/1.7/1.7 ms。
Miller-Rabin vs 试除:1,000,003 → 0.15 vs 0.04 ms;1,000,000,007 → 4.61 vs 0.05 ms;1,000,000,000,039 → 189.33 vs 0.12 ms;2¹²⁷−1 → 试除不可行 vs 0.7 ms。
蒙特卡洛 π:1k/10k/100k/1M/10M 点 → 3.140000/3.152800/3.145200/3.138636/3.141839(误差 1/√n 收敛)。
三门问题:20 万次,不换 33.26%、换 66.74%(理论 33.33%/66.67%)。
生日悖论:10/20/23/30/50 人 → 11.8%/41.0%/51.4%/70.5%/97.0%。
抽卡 1% 出率:10/50/100/200/300 次 → 9.5%/39.4%/63.5%/86.7%/95.0%。
大数定律:抛硬币 n=10~100 万,最大偏离 0.40→0.0007。
赌徒谬误:连出 5 次正面后第 6 次 50.44%(理论 50%)。
你自己机器上跑出来的数字会略有不同。差一点不影响结论——结论是结构性的。