前言
概率DP是动态规划在概率问题上的应用。投骰子几次能到达终点? 看似随机的问题,背后有严密的数学期望推导。很多人只记住公式,却不知道期望的线性性、为什么逆推而不是正推。
我并没有能力让你成为概率论专家,我只是想让你彻底理解概率DP的状态定义、期望的数学性质、以及为什么有些问题要逆推。每个公式都会详细证明,作为算法系列40篇的完美收官。
摘要
从"骰子期望步数"问题出发,剖析概率DP的核心思想与数学推导。通过期望的定义与性质、正推与逆推的选择依据、以及全期望公式的应用,揭秘如何用DP求解随机问题。配合详细数学证明,给出概率DP的透彻理解。
一、什么是期望
周一早上,哈吉米问南北绿豆:"数学期望是什么?"
南北绿豆:"期望就是随机变量的平均值。"
1.1 期望的定义
定义:
随机变量X的期望:
E(X) = Σ x × P(X = x)
= 所有可能值 × 对应概率
意义:X的"平均值"
示例:投骰子
骰子:1, 2, 3, 4, 5, 6
每个点数概率:1/6
E(X) = 1×(1/6) + 2×(1/6) + 3×(1/6) + 4×(1/6) + 5×(1/6) + 6×(1/6)
= (1+2+3+4+5+6) / 6
= 21 / 6
= 3.5
期望是3.5(平均点数)
哈吉米:"期望就是加权平均值。"
1.2 期望的重要性质
性质1:期望的线性性
E(X + Y) = E(X) + E(Y)
即使X和Y不独立,这个性质也成立
为什么这个性质重要?
南北绿豆:"因为它让复杂问题变简单。"
示例:
投2次骰子,点数之和的期望?
方法1:枚举所有36种情况
(1,1), (1,2), ..., (6,6)
计算每种情况的概率和点数
很繁琐
方法2:线性性
E(第1次 + 第2次) = E(第1次) + E(第2次)
= 3.5 + 3.5
= 7
简单多了!
哈吉米:"期望可以拆开算,太方便了。"
二、例题1:骰子到达终点的期望步数
问题描述:
一个棋盘有n个格子(0到n-1),你在位置0
每次投骰子(1-6点),往前走对应步数
问题:到达位置n-1的期望步数是多少?
示例:
n = 10
期望步数 ≈ ?
哈吉米:"这题怎么做?"
南北绿豆:"用期望DP。"
2.1 状态定义
dp[i]的定义:从位置i出发,到达位置n-1的期望步数。
为什么这样定义?
阿西噶阿西:"因为从i出发更好推导。"
正推 vs 逆推:
正推(从起点出发):
dp[i] = 从0到i的期望步数
难点:到达i可能有很多路径,概率难算
逆推(从终点回推):
dp[i] = 从i到n-1的期望步数
简单:从i出发,只需考虑下一步的6种情况
哈吉米:"逆推只看下一步,正推要看所有路径,所以逆推简单。"
2.2 状态转移方程
从位置i出发,投骰子:
掷到1:到位置i+1,期望步数dp[i+1]
掷到2:到位置i+2,期望步数dp[i+2]
...
掷到6:到位置i+6,期望步数dp[i+6]
每种情况概率1/6
全期望公式:
E(从i到n-1的步数) = 1 + E(下一步后到n-1的步数)
= 1 + (1/6)×dp[i+1] + (1/6)×dp[i+2] + ... + (1/6)×dp[i+6]
状态转移方程:
dp[i] = 1 + (dp[i+1] + dp[i+2] + ... + dp[i+6]) / 6
特殊情况:
如果i+k >= n-1,说明一步到达,贡献0
dp[n-1] = 0(终点,不需要步数)
为什么要+1?
南北绿豆:"因为投了1次骰子,步数+1。"
详细推导:
E(从i到n-1的总步数)
= E(这一步) + E(剩余步数)
= 1 + E(从下一个位置到n-1)
= 1 + (1/6)×Σ dp[i+k],k从1到6
哈吉米:"全期望公式:总期望 = 当前步 + 后续期望。"
2.3 代码实现
Java版本:
public double expectedSteps(int n) {
double[] dp = new double[n];
dp[n - 1] = 0; // 终点
// 从后往前推
for (int i = n - 2; i >= 0; i--) {
double sum = 0;
int count = 0;
// 投骰子6种情况
for (int k = 1; k <= 6; k++) {
if (i + k >= n - 1) {
count++; // 一步到达,贡献0
} else {
sum += dp[i + k];
count++;
}
}
dp[i] = 1 + sum / count;
}
return dp[0];
}
C++版本:
double expectedSteps(int n) {
vector<double> dp(n, 0);
dp[n - 1] = 0;
for (int i = n - 2; i >= 0; i--) {
double sum = 0;
int count = 0;
for (int k = 1; k <= 6; k++) {
if (i + k >= n - 1) {
count++;
} else {
sum += dp[i + k];
count++;
}
}
dp[i] = 1 + sum / count;
}
return dp[0];
}
Python版本:
def expectedSteps(n):
dp = [0.0] * n
dp[n - 1] = 0
for i in range(n - 2, -1, -1):
total = 0
count = 0
for k in range(1, 7):
if i + k >= n - 1:
count += 1
else:
total += dp[i + k]
count += 1
dp[i] = 1 + total / count
return dp[0]
时间复杂度:O(n)
三、概率DP vs 期望DP
南北绿豆:"概率DP和期望DP有啥区别?"
3.1 核心区别
| 类型 | 定义 | 典型问题 |
|---|---|---|
| 概率DP | dp[i] = 到达状态i的概率 | 正推:从起点推概率 |
| 期望DP | dp[i] = 从状态i出发的期望 | 逆推:从终点回推期望 |
3.2 为什么期望DP要逆推
详细解释:
期望DP正推的困难:
dp[i] = 到达i的期望步数
问题:到达i可能有多条路径,每条概率不同
E(到达i) = Σ P(路径k) × 步数k
概率P(路径k)难算(要考虑所有前驱)
期望DP逆推:
dp[i] = 从i出发到终点的期望步数
简单:只看下一步的选择
E(从i出发) = 1 + Σ P(选择k) × dp[下一步k]
概率P(选择k)通常是已知的(比如1/6)
哈吉米:"逆推只看未来(下一步),正推要看过去(所有路径),所以逆推简单。"
南北绿豆:"完全正确!期望DP优先考虑逆推。"
四、全期望公式
南北绿豆:"期望DP的数学基础是全期望公式。"
4.1 全期望公式
定理:
E(X) = Σ E(X | Aᵢ) × P(Aᵢ)
其中A₁, A₂, ..., Aₙ是完备事件组
人话解释:
总期望 = 各种情况的期望 × 各种情况的概率
应用在骰子问题:
E(从i出发的步数)
= E(掷到1) × P(掷到1) + E(掷到2) × P(掷到2) + ... + E(掷到6) × P(掷到6)
= (1 + dp[i+1]) × 1/6 + (1 + dp[i+2]) × 1/6 + ... + (1 + dp[i+6]) × 1/6
= 1 + (dp[i+1] + dp[i+2] + ... + dp[i+6]) / 6
哈吉米:"全期望公式就是加权平均!"
五、概率DP与期望DP总结
5.1 核心要点
南北绿豆总结:
- 概率DP:求到达某状态的概率(正推)
- 期望DP:求从某状态出发的期望(逆推)
- 全期望公式:E(X) = Σ E(X|A) × P(A)
- 期望线性性:E(X+Y) = E(X) + E(Y)
5.2 解题步骤
阿西噶阿西:
1. 判断是概率还是期望
2. 定义状态dp[i]
3. 找状态转移(用全期望公式)
4. 确定边界条件
5. 确定推导方向(正推/逆推)
5.3 识别技巧
南北绿豆:
- 看到期望、平均,想期望DP
- 看到概率、可能性,想概率DP
- 看到随机、"骰子,想概率/期望DP
哈吉米:"概率问题也能用DP,数学真美妙。"
参考资料:
- 《概率论与数理统计》- 盛骤
- 《算法竞赛进阶指南》- 李煜东