概率与数学期望

离散概率初步

一个经典的例子就是抛硬币:
连续抛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/2
1+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;
}



posted @ 2022-03-12 09:28  PassName  阅读(725)  评论(0编辑  收藏  举报