这是 数学重学路线图 阶段三的子页面
📌 相关笔记:概率与统计
本页核心:概率论的基本语言和计算法则,从”数数”到”更新信念”
一、概率是什么?——从直觉开始 直觉比喻:概率就是”惊讶程度的反面”
太阳明天升起?概率≈1,你不惊讶
连续中两次彩票?概率极小,你非常惊讶
概率高 = 预期之中;概率低 = 出乎意料
正式定义(古典概型):
$$P(A) = \frac{\text{事件A的有利结果数}}{\text{所有等可能结果总数}}$$
前提条件:每个结果等可能 出现
例子:掷一个公平骰子
总结果数 = 6
P(掷出偶数) = {2,4,6} / {1,2,3,4,5,6} = 3/6 = 0.5
概率的三条公理(柯尔莫哥洛夫):
公理1:P(A) ≥ 0(概率非负)
公理2:P(Ω) = 1(必然事件概率为1)
公理3:互斥事件可加 P(A∪B) = P(A) + P(B)(当A∩B=∅)
Python验证:掷骰子频率趋近概率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import randomdef dice_experiment (n_rolls ):"""掷n次骰子,观察偶数出现的频率""" even_count = 0 for _ in range (n_rolls): if random.randint(1 , 6 ) % 2 == 0 : even_count += 1 return even_count / n_rollsfor n in [100 , 1000 , 10000 , 100000 ]:freq = dice_experiment(n) print (f"掷{n:>7 } 次: 偶数频率 = {freq:.4 f} " )
二、概率基本运算 2.1 加法规则(或事件) 一般形式:P(A∪B) = P(A) + P(B) - P(A∩B)
直觉:两个圆的面积之和,要减去重叠部分,否则算了两次
特例——互斥事件(A∩B=∅):P(A∪B) = P(A) + P(B)
例子:一副52张牌,抽到红心或国王的概率
P(红心) = 13/52, P(国王) = 4/52, P(红心国王) = 1/52
P(红心∪国王) = 13/52 + 4/52 - 1/52 = 16/52 ≈ 0.308
2.2 补事件 P(Ā) = 1 - P(A)
直觉:不想直接算”至少一个”,就算”一个都没有”再用1减
经典技巧:求”至少一次”的概率
P(至少一次) = 1 - P(一次都没有)
例子:掷3次骰子,至少出现一次6的概率
P(一次都不出6) = (5/6)³ ≈ 0.579
P(至少一次6) = 1 - 0.579 ≈ 0.421
2.3 乘法规则(且事件) 一般形式:P(A∩B) = P(A) × P(B|A)
独立事件特例:P(A∩B) = P(A) × P(B)
独立的直觉:A发不发生对B没有任何影响
Python验证加法规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 deck = [(suit, rank) for suit in ['红心' ,'黑桃' ,'方块' ,'梅花' ] for rank in range (1 , 14 )] hearts = {c for c in deck if c[0 ] == '红心' } kings = {c for c in deck if c[1 ] == 13 } both = hearts & kings either = hearts | kings print (f"P(红心) = {len (hearts)} /52 = {len (hearts)/52 :.4 f} " )print (f"P(国王) = {len (kings)} /52 = {len (kings)/52 :.4 f} " )print (f"P(红心∩国王)= {len (both)} /52 = {len (both)/52 :.4 f} " )print (f"P(红心∪国王)= {len (either)} /52 = {len (either)/52 :.4 f} " )
三、条件概率 3.1 定义 P(A|B) = P(A∩B) / P(B)
读作”在B已经发生的条件下,A发生的概率”
直觉比喻:缩小了样本空间
原来的宇宙是Ω,现在你知道B发生了,宇宙缩小到B
在B这个小宇宙里,A还占多大比例?
3.2 例子 掷两个骰子,已知总和≥10,两个都是5或6的概率?
总和≥10的情况:{(4,6),(5,5),(5,6),(6,4),(6,5),(6,6)} → 6种
两个都≥5的情况:{(5,5),(5,6),(6,5),(6,6)} → 4种
P = 4/6 = 2/3
3.3 独立事件判定 A和B独立 ⟺ P(A|B) = P(A) ⟺ P(A∩B) = P(A)×P(B)
直觉:知道B发生了也不改变你对A的判断
注意:互斥 ≠ 独立!互斥事件不独立 (一个发生了另一个一定不发生)
四、贝叶斯定理——用证据更新信念 4.1 公式 $$P(A|B) = \frac{P(B|A) \times P(A)}{P(B)}$$
各部分的名字:
P(A) = 先验概率 (没看到证据前的信念)
P(B|A) = 似然 (如果A为真,看到B的可能性)
P(B) = 证据的总概率
P(A|B) = 后验概率 (看到证据后更新的信念)
4.2 直觉比喻 你是一个侦探:
先验 = 嫌疑人有多可疑(根据背景信息)
似然 = 如果他是凶手,这个证据出现的概率有多大
后验 = 看到证据后,他是凶手的概率
贝叶斯就是”理性地根据新证据调整信念”
4.3 经典例子:检测阳性,真得病了吗? 场景设定:
某病患病率 P(病) = 0.1%(千分之一)
检测灵敏度 P(阳|病) = 99%(真有病时99%测出来)
检测特异度 P(阴|没病) = 95%(没病时95%测正常)→ P(阳|没病) = 5%(假阳性率)
问:测出阳性,真得病的概率?
计算:
P(阳) = P(阳|病)×P(病) + P(阳|没病)×P(没病)
P(阳) = 0.99×0.001 + 0.05×0.999 = 0.00099 + 0.04995 = 0.05094
P(病|阳) = 0.00099 / 0.05094 ≈ 1.94%
反直觉结论:即使检测99%准确,阳性者中只有不到2%真有病!
原因:患病的人太少了,假阳性的绝对数量远超真阳性
Python贝叶斯计算器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 def bayes (prior_a, likelihood_b_given_a, likelihood_b_given_not_a ):""" 贝叶斯定理计算器 prior_a: P(A) 先验概率 likelihood_b_given_a: P(B|A) likelihood_b_given_not_a: P(B|¬A) 返回: P(A|B) 后验概率 """ p_b = likelihood_b_given_a * prior_a + \ likelihood_b_given_not_a * (1 - prior_a) posterior = (likelihood_b_given_a * prior_a) / p_b return posteriorresult = bayes( prior_a=0.001 , likelihood_b_given_a=0.99 , likelihood_b_given_not_a=0.05 ) print (f"检测阳性后真患病概率: {result:.4 f} = {result*100 :.2 f} %" )result2 = bayes( prior_a=result, likelihood_b_given_a=0.99 , likelihood_b_given_not_a=0.05 ) print (f"二次阳性后真患病概率: {result2:.4 f} = {result2*100 :.2 f} %" )
五、安全领域应用 5.1 入侵检测误报率(贝叶斯思维) IDS报警了,真被入侵的概率?
P(入侵) = 0.01%(日常被真入侵的概率很低)
P(报警|入侵) = 95%(真入侵时大概率报警)
P(报警|正常) = 2%(正常流量误报率)
P(入侵|报警) = (0.95×0.0001)/(0.95×0.0001+0.02×0.9999) ≈ 0.47%
结论:绝大多数IDS报警都是误报 ,这就是安全运营的痛
1 2 3 4 5 6 7 ids_result = bayes( prior_a=0.0001 , likelihood_b_given_a=0.95 , likelihood_b_given_not_a=0.02 ) print (f"IDS报警后真入侵概率: {ids_result*100 :.2 f} %" )
5.2 密码碰撞概率 密码空间大小N,随机选k个密码,至少两个相同的概率
这就是生日问题 的推广(详见 19-哈希数学 )
4位数字密码(N=10000),100人至少两人相同:P ≈ 1-e^(-100²/(2×10000)) ≈ 39.3%
5.3 社会工程钓鱼成功率 假设单次钓鱼成功率 p = 3%
发给1000人,至少1人上钩的概率?
P(至少1人) = 1 - (1-0.03)^1000 = 1 - 0.97^1000 ≈ 1.0(几乎必然成功)
1 2 3 4 5 6 7 8 9 10 p_single = 0.03 n_targets = 1000 p_at_least_one = 1 - (1 - p_single) ** n_targets print (f"1000人中至少1人上钩: {p_at_least_one:.10 f} " )p_low = 0.001 print (f"p=0.1%, 1000人至少1人上钩: {1 -(1 -p_low)**1000 :.4 f} " )
六、大数据应用:朴素贝叶斯分类器 6.1 原理 朴素假设:各特征条件独立 (虽然通常不严格成立,但效果好)
分类规则:选使 P(类别|特征) 最大的类别
P(类别|特征1,特征2,…) ∝ P(类别) × ∏P(特征i|类别)
6.2 垃圾邮件过滤器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 from collections import defaultdictimport mathclass NaiveBayesSpamFilter :"""极简朴素贝叶斯垃圾邮件过滤器""" def __init__ (self ): self .word_counts = {'spam' : defaultdict(int ), 'ham' : defaultdict(int )} self .class_counts = {'spam' : 0 , 'ham' : 0 } self .vocab = set () def train (self, emails ): """emails: [(文本, 'spam'/'ham'), ...]""" for text, label in emails: self .class_counts[label] += 1 for word in text.lower().split(): self .word_counts[label][word] += 1 self .vocab.add(word) def predict (self, text ): """返回 'spam' 或 'ham'""" words = text.lower().split() total = sum (self .class_counts.values()) best_label, best_score = None , float ('-inf' ) for label in ['spam' , 'ham' ]: score = math.log(self .class_counts[label] / total) vocab_size = len (self .vocab) total_words = sum (self .word_counts[label].values()) for word in words: count = self .word_counts[label].get(word, 0 ) + 1 score += math.log(count / (total_words + vocab_size)) if score > best_score: best_score = score best_label = label return best_label training_data = [ ("免费 领取 优惠券 点击 链接" , "spam" ), ("中奖 恭喜 获得 大奖 立即 领取" , "spam" ), ("贷款 低息 快速 审批 无抵押" , "spam" ), ("明天 会议 讨论 项目 进度" , "ham" ), ("代码 review 提交 合并 分支" , "ham" ), ("数据库 迁移 方案 评审 周三" , "ham" ), ] clf = NaiveBayesSpamFilter() clf.train(training_data) test_emails = ["免费 领取 大奖" , "明天 代码 review" ] for email in test_emails:print (f"'{email} ' → {clf.predict(email)} " )
七、后端应用:系统可用性计算 7.1 可用性定义 可用性 = 正常运行时间 / 总时间
几个9的含义(一年365天):
等级
可用性
年停机时间
场景
2个9
99%
3.65天
个人项目
3个9
99.9%
8.76小时
一般业务
4个9
99.99%
52.6分钟
重要业务
5个9
99.999%
5.26分钟
金融/电信
7.2 冗余提升可用性 单机可用性 a = 99.9%(3个9)
n台并行冗余 :系统不可用 = 所有机器都挂 = (1-a)^n
系统可用性 = 1 - (1-a)^n
Python计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 def availability (single_a, n_replicas ):"""并行冗余系统可用性""" sys_unavailable = (1 - single_a) ** n_replicas sys_available = 1 - sys_unavailable return sys_availabledef downtime_per_year (avail ):"""年停机时间(分钟)""" return (1 - avail) * 365.25 * 24 * 60 single = 0.999 print ("=== 并行冗余提升可用性 ===" )for n in [1 , 2 , 3 , 5 ]:a = availability(single, n) dt = downtime_per_year(a) nines = -math.log10(1 - a) if a < 1 else float ('inf' ) print (f"{n} 台冗余: 可用性={a:.10 f} , " f"年停机={dt:.4 f} 分钟, ≈{nines:.1 f} 个9" ) import mathdef serial_availability (components ):"""串联系统:总可用性 = 各组件可用性之积""" result = 1.0 for a in components: result *= a return resultchain = [0.999 , 0.999 , 0.999 ] total = serial_availability(chain) print (f"\n三层串联(各99.9%): 总可用性={total:.6 f} " )print (f"年停机: {downtime_per_year(total):.1 f} 分钟" )
八、蒙特卡罗模拟 8.1 核心思想 用大量随机试验的频率来逼近概率或期望值
直觉比喻:不会算面积?往上面扔豆子,数落在里面的比例
适用场景:解析解太难求或不存在时
8.2 蒙特卡罗估算π 原理:在1×1正方形内随机撒点
落在半径=1的四分之一圆内的比例 ≈ π/4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import randomdef monte_carlo_pi (n_points ):"""用蒙特卡罗方法估算π""" inside = 0 for _ in range (n_points): x = random.random() y = random.random() if x*x + y*y <= 1 : inside += 1 return 4 * inside / n_pointsfor n in [1000 , 10000 , 100000 , 1000000 ]:pi_est = monte_carlo_pi(n) error = abs (pi_est - 3.14159265 ) / 3.14159265 * 100 print (f"n={n:>8 } : π≈{pi_est:.6 f} , 误差={error:.3 f} %" )
8.3 蒙特卡罗解实际问题:系统容量规划 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import randomdef simulate_request_overflow (n_simulations, avg_rps, max_capacity ):""" 模拟请求溢出概率 假设请求量服从泊松分布(λ=avg_rps) """ overflow_count = 0 for _ in range (n_simulations): actual_rps = sum (1 for _ in range (int (avg_rps * 3 )) if random.random() < 1 /3 ) if actual_rps > max_capacity: overflow_count += 1 return overflow_count / n_simulationsavg_rps = 100 max_capacity = 120 p_overflow = simulate_request_overflow(10000 , avg_rps, max_capacity) print (f"请求溢出概率: {p_overflow*100 :.2 f} %" )
九、练习题 练习1:加法规则 一个班有40人,25人会Python,20人会Java,10人两种都会
随机选1人,会Python或 Java的概率?
答案:P(Py∪Java) = 25/40 + 20/40 - 10/40 = 35/40 = 87.5%
练习2:补事件 系统有4个独立组件,每个可用性为99%
串联部署,系统至少有一个组件故障的概率?
P(全部正常) = 0.99^4 ≈ 0.9606
P(至少一个故障) = 1 - 0.9606 ≈ 3.94%
练习3:贝叶斯定理 WAF的拦截规则:P(拦截|攻击)=98%,P(拦截|正常)=1%,P(攻击)=0.5%
某请求被拦截,真是攻击的概率?
P(攻击|拦截) = (0.98×0.005) / (0.98×0.005 + 0.01×0.995)
= 0.0049 / (0.0049 + 0.00995) = 0.0049 / 0.01485 ≈ 33.0%
练习4:独立事件 3台独立服务器各有99.9%可用性,并行部署(任一可用即可)
系统可用性是多少?相当于几个9?
P(不可用) = (0.001)^3 = 10^(-9)
P(可用) = 1 - 10^(-9) = 0.999999999 → 9个9
练习5:蒙特卡罗 用蒙特卡罗方法估算:两个骰子之和为7的概率
1 2 3 4 5 6 7 import randomn = 100000 count = sum (1 for _ in range (n) if random.randint(1 ,6 ) + random.randint(1 ,6 ) == 7 ) print (f"P(和=7) ≈ {count/n:.4 f} " )
十、本章小结 概率是对不确定性的量化,取值 [0, 1]
加法规则处理”或”,乘法规则处理”且”,补事件处理”至少”
条件概率缩小样本空间,贝叶斯定理用证据更新信念
贝叶斯在安全(IDS误报分析)和大数据(垃圾邮件过滤)中有直接应用
冗余系统可用性 = 1-(1-a)^n 是后端架构的基本算术
蒙特卡罗:算不了的概率就用大量随机试验去逼近
➡️ 下一页:17-图论基础