基本概率与条件概率
package main
import (
"fmt"
"math/rand"
)
// 蒙特卡洛估算 π
// 原理:单位圆面积 = π,单位正方形面积 = 4,比值 = π/4
func estimatePi(n int) float64 {
inside := 0
for i := 0; i < n; i++ {
x := rand.Float64()*2 - 1
y := rand.Float64()*2 - 1
if x*x+y*y <= 1 {
inside++
}
}
return 4 * float64(inside) / float64(n)
}
// 模拟掷骰子,验证大数定律
func diceMean(n int) float64 {
sum := 0
for i := 0; i < n; i++ {
sum += rand.Intn(6) + 1
}
return float64(sum) / float64(n)
}
func main() {
fmt.Printf("π ≈ %.5f (理论 3.14159)\n", estimatePi(1_000_000))
// 大数定律验证:骰子期望 = 3.5
for _, n := range []int{10, 100, 10000, 1000000} {
fmt.Printf("n=%7d: 均值=%.4f\n", n, diceMean(n))
}
}
生日悖论:碰撞比你想的早
「23 个人中有两个人生日相同的概率 > 50%」——这就是生日悖论。直觉上应该需要 183 人,但数学告诉我们只需要 23 人。原因:碰撞发生在任意两人之间,而不是你和某个特定人之间。这直接解释了为什么哈希表在装载率 70% 时冲突率就已经很显著了。
package main
import (
"fmt"
"math"
)
// 计算 k 个人中至少两人生日相同的概率
func birthdayCollisionProb(k, n int) float64 {
// 所有人生日不同的概率
pNone := 1.0
for i := 0; i < k; i++ {
pNone *= float64(n-i) / float64(n)
}
return 1 - pNone
}
// 哈希表碰撞:m 个槽,n 个键,碰撞概率
func hashCollisionProb(m, n int) float64 {
return birthdayCollisionProb(n, m)
}
func main() {
// 生日悖论:365 天,多少人时概率超 50%
for k := 1; k <= 30; k++ {
p := birthdayCollisionProb(k, 365)
if p > 0.5 {
fmt.Printf("%d 个人时概率首次超 50%%: %.1f%%\n", k, p*100)
break
}
}
// 哈希表:1024 个槽,放多少个键时碰撞概率超 10%?
for n := 1; n <= 1024; n++ {
if hashCollisionProb(1024, n) > 0.1 {
fmt.Printf("槽=1024,键=%d 时碰撞率>10%%\n", n)
break
}
}
// 近似公式:n ≈ sqrt(2m * ln(1/(1-p)))
approx := math.Sqrt(2 * 1024 * math.Log(1/(1-0.1)))
fmt.Printf("近似公式结果: %.0f\n", approx)
}
期望与方差:量化随机
package main
import (
"fmt"
"math"
)
// 离散随机变量的期望和方差
func expectation(vals []float64, probs []float64) float64 {
e := 0.0
for i, v := range vals {
e += v * probs[i]
}
return e
}
func variance(vals []float64, probs []float64) float64 {
e := expectation(vals, probs)
v := 0.0
for i, x := range vals {
d := x - e
v += d * d * probs[i]
}
return v
}
func main() {
// 骰子
vals := []float64{1, 2, 3, 4, 5, 6}
probs := []float64{1.0 / 6, 1.0 / 6, 1.0 / 6, 1.0 / 6, 1.0 / 6, 1.0 / 6}
e := expectation(vals, probs)
v := variance(vals, probs)
fmt.Printf("期望=%.2f 方差=%.4f 标准差=%.4f\n", e, v, math.Sqrt(v))
// 期望=3.50 方差=2.9167 标准差=1.7078
// 缓存命中期望:命中率 0.8,单次查询期望 IO 次数
// 命中:0 次磁盘 IO,未命中:1 次磁盘 IO
hitRate := 0.8
expectedIO := 0*hitRate + 1*(1-hitRate)
fmt.Printf("命中率 80%% 时,每次查询期望 IO: %.1f 次\n", expectedIO)
}
贝叶斯定理:更新信念
贝叶斯定理 P(A|B) = P(B|A)×P(A) / P(B) 描述了如何用新证据更新概率估计。在垃圾邮件过滤、异常检测、医疗诊断中大量使用。==先验 × 似然 = 后验==(差一个归一化常数)。
✓推荐做法
- 哈希表负载因子控制在 0.7 以下(碰撞概率急剧上升前)
- A/B 测试前先算所需样本量,而不是靠感觉
- 蒙特卡洛方法适用于高维积分、期权定价等解析解难求的场景
✗不推荐
- 把概率当百分比随意计算——注意独立性假设是否成立
- 忽视小概率大影响事件——期望值不是全部,方差也很重要
⚠常见误区
- 条件概率 P(A|B) ≠ P(B|A)——混淆两者叫做「检察官谬误」
判断标准:能计算哈希表碰撞概率并解释为什么负载因子不能太高 → 掌握本章。
概率论是量化无知的科学——它不消除不确定性,它让你诚实地面对它。
— E.T. Jaynes《概率论:科学的逻辑》