概率与数学期望
离散概率初步
一个经典的例子就是抛硬币:
连续抛3次硬币,恰好有两次正面的概率有多少:
抛3次硬币,一共有8可能:HHH , HHT , HTH , HTT , THH ,THT , TTH ,TTT
这八种情况的概率是相等的
这里的{HHH , HHT , HTH , HTT , THH ,THT , TTH ,TTT}用专业术语讲就是样本空间
“恰好有两个正面”这个事件可以表示为:A={HHT , HTH , THH}
所以P(A)=3/8
接下来要说的就是一个经典问题:生日问题
一共有23个人,至少两个人的生日相同的概率超过50%,为了简单起见,每个人的生日都不是2月29日
实际上这个例子和抛硬币有异曲同工之妙
每个人的生日是365天中等概率选择的,因此样本空间是|S|=365^23
接下来要计算“至少两个人的生日相同”,这个不大好统计,所以我们遵循原则:
正难则反
先计算出任何两个人的生日都不同,然后用总数减去就好了,于是有式子
在实际计算的时候,A(365,23)和365^23都是无法直接计算的
但是概率是一个不超过1的double,并且此处不需要太高的精度,所以我们直接计算:
double P(int n,int m)
{
double ans=1.0;
for (int i=0;i<m;i++) ans*=(n-i);
return ans;
}
double birthday(int n,int m)
{
double ans=P(n,m);
for (int i=1;i<=m;i++) ans/=n;
return 1-ans;
}
但是如果m太大,上面那个程序就会爆掉
我们的解决方法是边乘边除:
double birthday(int n,int m)
{
double ans=1.0;
for (int i=0;i<m;i++) ans*=(double)(n-i)/n;
return 1-ans;
}
和int,ll一样,double类型也要时刻注意溢出
条件概率
公式:P(A|B)=P(AB)|P(B)
P(A|B):在事件B发生的前提下,事件A发生的概率
P(AB):事件A和B同时发生的概率
贝叶斯公式
P(A|B)=P(B|A)*P(A)/P(B)
这也是条件概率中很重要的一个公式
全概率公式
把样本空间S分成若干个不想交的部分B1,B2,B3,…,Bn,
则 P(A)=P(A|B1)*P(B1)+P(A|B2)*P(B2)+…+P(A|Bn)*P(Bn)
这里的P(A|B)是指B事件发生的条件下,事件A发生的概率
其实ta的思想很简单:
比如,参加NOI,得到金牌,银牌,铜牌,当炮灰的概率分别是0.1,0.2,0.3,0.4,
在这种情况下,保送上清华的概率分别是1.0,0.8,0.5,0.1,
则被报送的总概率是0.1*1.0+0.2*0.8+0.3*0.5+0.4*0.1
使用全概率公式的关键就是划分时间空间
只有把所有情况不重复,不遗漏的进行分类,
并计算出每个事件的概率,才能得出正确的答案
数学期望
简单地说,随机变量X的数学期望EX就是所有可能值按概率加权的和
比如一个随机变量有1/2的概率等于1,1/3的概率等于2,1/6的概率等于3
那么这个随机变量的数学期望为
1/21+1/32+1/63
在非正式场合中,可以说这个随机变量“在平均情况下的值”为1/21+1/32+1/63
在解决和数学期望相关的题目时,大多数是可以直接考虑定义法解决
求出各个取值和对应的概率
当然,如果是等概率事件(所有事件的概率完全相等)
我们可以用:
各个情况之和/总情况数量(相当于求一个平均值,不是一般的好用啊)
如果遇到困难,我们可以考虑一下两个法宝:
- 期望的线性性质:
有限个随机变量之和的数学期望等于每个的数学期望之和
即E(X+Y)=EX+EY
- 全期望公式:
类似全概率公式,把所有情况不重复,不遗漏的分成若干类,每个计算数学期望,
最后把这些数学期望按照每类的概率加权求和
实际上,我们最常用的就是
- 全概率公式
把样本空间S分成若干个不想交的部分B1,B2,B3,…,Bn,
则P(A)=P(A|B1)P(B1)+P(A|B2)P(B2)+…+P(A|Bn)*P(Bn)
这里的P(A|B)是指B事件发生的条件下,事件A发生的概率
- 全期望公式
类似全概率公式,把所有情况不重复,不遗漏的分成若干类,每个计算数学期望,
最后把这些数学期望按照每类的概率加权求和
练习
给出一个有向无环的连通图,起点为 1,终点为 N,每条边都有一个长度。
数据保证从起点出发能够到达图中所有的点,图中所有的点也都能够到达终点。
绿豆蛙从起点出发,走向终点。
到达每一个顶点时,如果有 K 条离开该点的道路,绿豆蛙可以选择任意一条道路离开该点,并且走向每条路的概率为 1/K。
现在绿豆蛙想知道,从起点走到终点所经过的路径总长度的期望是多少?
输入格式
第一行: 两个整数 N,M,代表图中有 N 个点、M 条边。
第二行到第 1+M 行: 每行 3 个整数 a,b,c,代表从 a 到 b 有一条长度为 c 的有向边。
输出格式
输出从起点到终点路径总长度的期望值,结果四舍五入保留两位小数。
数据范围
1≤N≤105,
1≤M≤2N
输入样例:
4 4
1 2 1
1 3 2
2 3 3
3 4 4
输出样例:
7.00
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010, M = 200010;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
int dout[N];
double f[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
double dp(int u)
{
if (f[u] >= 0) return f[u];
f[u] = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
f[u] += (w[i] + dp(j)) / dout[u];
}
return f[u];
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for (int i = 0; i < m; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
dout[a] ++ ;
}
memset(f, -1, sizeof f);
printf("%.2lf\n", dp(1));
return 0;
}
Admin 生日那天,Rainbow 来找 Admin 玩扑克牌。
玩着玩着 Rainbow 觉得太没意思了,于是决定给 Admin 一个考验。
Rainbow 把一副扑克牌(54 张)随机洗开,倒扣着放成一摞。
然后 Admin 从上往下依次翻开每张牌,每翻开一张黑桃、红桃、梅花或者方块,就把它放到对应花色的堆里去。
Rainbow 想问问 Admin,得到 A 张黑桃、B 张红桃、C 张梅花、D 张方块需要翻开的牌的张数的期望值 E 是多少?
特殊地,如果翻开的牌是大王或者小王,Admin 将会把它作为某种花色的牌放入对应堆中,使得放入之后 E 的值尽可能小。
由于 Admin 和 Rainbow 还在玩扑克,所以这个程序就交给你来写了。
输入格式
输入仅由一行,包含四个用空格隔开的整数,A,B,C,D。
输出格式
输出需要翻开的牌数的期望值 E,四舍五入保留 3 位小数。
如果不可能达到输入的状态,输出 -1.000。
数据范围
0≤A,B,C,D≤15
输入样例:
1 2 3 4
输出样例:
16.393
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 14;
const double INF = 1e20;
int A, B, C, D;
double f[N][N][N][N][5][5];
double dp(int a, int b, int c, int d, int x, int y)
{
double &v = f[a][b][c][d][x][y];
if (v >= 0) return v;
int as = a + (x == 0) + (y == 0);
int bs = b + (x == 1) + (y == 1);
int cs = c + (x == 2) + (y == 2);
int ds = d + (x == 3) + (y == 3);
if (as >= A && bs >= B && cs >= C && ds >= D) return v = 0;
int sum = a + b + c + d + (x != 4) + (y != 4);
sum = 54 - sum;
if (sum <= 0) return v = INF;
v = 1;
if (a < 13) v += (13.0 - a) / sum * dp(a + 1, b, c, d, x, y);
if (b < 13) v += (13.0 - b) / sum * dp(a, b + 1, c, d, x, y);
if (c < 13) v += (13.0 - c) / sum * dp(a, b, c + 1, d, x, y);
if (d < 13) v += (13.0 - d) / sum * dp(a, b, c, d + 1, x, y);
if (x == 4)
{
double t = INF;
for (int i = 0; i < 4; i ++ ) t = min(t, 1.0 / sum * dp(a, b, c, d, i, y));
v += t;
}
if (y == 4)
{
double t = INF;
for (int i = 0; i < 4; i ++ ) t = min(t, 1.0 / sum * dp(a, b, c, d, x, i));
v += t;
}
return v;
}
int main()
{
cin >> A >> B >> C >> D;
memset(f, -1, sizeof f);
double t = dp(0, 0, 0, 0, 4, 4);
if (t > INF / 2) t = -1;
printf("%.3lf\n", t);
return 0;
}