跳转至

概率和期望

对于随机变量 \(X\),设其第 \(i\) 种取值为 \(x_i\),出现的概率是 \(P(X=x_i)\),则其期望 \(E(x)\) 定义为:

\[ E(X)=\sum_{i=1}{x_iP(X=x_i)} \]

数学期望反映变量平均取值的大小。

性质

线性性

不需要两变量无关:

\[ \begin{align*} &E(X+Y)=E(X)+E(Y)\\ &E(aX)=aE(X) \end{align*} \]
证明

可加性:

\[ \begin{align*} &E(X+Y)\\ =&\sum_{i}\sum_{j}(x_i+y_j)P(X=x_i,Y=y_j)\\ =&\sum_{i}\sum_{j}x_i P(X=x_i,Y=y_j)+\sum_{i}\sum_{j}y_j P(X=x_i,Y=y_j)\\ =&\sum_{i}x_i\sum_{j}P(X=x_i,Y=y_j)+\sum_{j}y_j\sum_{i}P(X=x_i,Y=y_i)\\ =&\sum_{i}x_i P(X=x_i)+\sum_{j}y_j P(Y=y_j)\\ =&E(X)+E(Y) \end{align*} \]

数乘性:

\[ \begin{align*} &E(aX)\\ =&\sum_{x}{axP(X=x)}\\ =&a\sum_{x}{xP(X=x)}\\ =&aE(X) \end{align*} \]

可乘性

需要 \(x\)\(y\) 相互独立:

\[ E(XY)=E(X)E(Y) \]

全期望公式

\[ E(X)=\sum_{y_i}{E(X|Y=y_i)P(Y=y_i)} \]

例题

Y661 单选错位

题目大意

试卷上共有 \(n\) 道单选题,第 \(i\) 道单选题有 \(a_i\) 个选项,每个选项成为正确答案的概率都是相等的。

如果你将第 \(i\) 道题的正确答案填到了第 \(i+1\) 道题的位置上(特别的,你将第 \(n\) 道题的答案填到了第 \(1\) 道题的位置上),问做对题数量的期望是多少。

可以将每道题看作一个随机变量 \(X_i\),做对了取值为 \(1\),没做对为 \(0\)。根据期望的线性性,我们只需求出每道题作对的期望是多少就可以了。

我们将 \(a_1\) 的值复制到 \(a_{n+1}\) 的位置上,就无需考虑 \(n\rightarrow 1\) 的情况了。

考虑 \(a_i\)\(a_{i+1}\) 的大小关系:

  • \(a_i\ge a_{i+1}\),做对第 \(i+1\) 道题的概率为 \(\frac{1}{a_{i}}\)
  • \(a_i< a_{i+1}\),做对第 \(i+1\) 道题的概率为 \(\frac{1}{a_{i+1}}\)

由期望的定义得到

\[ E(X_i)=1\times P(X_i)+0\times \big(1-P(X_i)\big)=P(X_i) \]

又因为

\[ P(X_i=1)=\frac{1}{\max(a_i,a_{i+1})} \]

所以答案为

\[ \sum_{i=1}^{n}{\frac{1}{\max(a_i,a_{i+1})}} \]

Y662 期望分数

题目大意

给定一个长度为 \(n\) 的字符串,由 ab? 组成。计算分数的规则如下:极长的连续 \(x\)a 可以得到 \(x^2\) 分。对于 ? 的地方,有 \(\frac{1}{2}\) 的概率为 a ,另外 \(\frac{1}{2}\) 的概率为 b,求该字符串的期望分数。

我们将连续段产生的贡献统一下放到右端点处理。

考虑第 \(i\) 位的边际贡献。若 \([1,i-1]\) 中末尾的连续段长度为 \(x_{i-1}\),则应对答案产生 \((x_{i-1}+1)^2-x_{i-1}^2=2x_{i-1}+1\) 的贡献。这个式子是线性的,可以直接使用期望的线性性进行计算。设 \([1,i-1]\) 段的末尾连续段长度的期望为 \(x_{i-1}\),则第 \(i\) 位的贡献为:

\[ \begin{cases} 2x_{i-1}+1 ,&c_i=\text{'a'}\\ 0 ,&c_i=\text{'b'}\\ \frac{2x_{i-1}+1}{2} ,&c_i=\text{'?'} \end{cases} \]

\(x\) 的转移:

\[ x_i= \begin{cases} x_{i-1}+1 ,&c_i=\text{'a'}\\ 0 ,&c_i=\text{'b'}\\ \frac{x_{i-1}+1}{2} ,&c_i=\text{'?'} \end{cases} \]
代码
#include<iostream>
using namespace std;
const int N = 3E5 + 10;

int n;
double Elen, ans;

int main() {

    cin >> n;
    for(int i = 1; i <= n; i++) {
        char c;
        cin >> c;
        if(c == 'o') {
            ans += Elen * 2 + 1;
            Elen = Elen + 1;
        } else if(c == 'x') {
            Elen = 0;
        } else {
            ans += (Elen * 2 + 1) / 2;
            Elen = (Elen + 1) / 2;
        }
    }

    printf("%.4lf\n", ans);

    return 0;
}

Y1-1 期望分数

题意

有一个长度为 \(n\)01 串,第 \(i\) 个位置有 \(p_i\) 的概率为 1\(1-p_i\) 的概率为 0。一串长度为 \(x\) 的极长的连续的 1 会产生 \(x^3\) 的价值。

问原串价值之和的期望是多少。

考虑长度为 \(i-1\) 的前缀,设其内部的价值之和为 \(f_{i-1}\),其末尾连续 1 的长度为 \(x_{i-1}\)。考虑第 \(i\) 位的边际贡献:

\[ \begin{cases} (x_{i-1}+1)^3-x_{i-1}^3,&a_i=1\\ 0,&a_i=0 \end{cases} \]

将第一种情况展开:

\[ (x_{i-1}+1)^3-x_{i-1}^3=3x_{i-1}^2+3x_{i-1}+1 \]

我们只要知道了 \(E(x_{i-1}^2)\)\(E(x_{i-1})\),就可以立刻求出该位产生贡献的期望。

如果我们只记录 \(E(x_{i-1})\),则无法求出 \(E(x_{i-1}^2)\),因为乘法要求两个变量无关。但我们可以同时记录 \(E(x_{i-1}^2)\)

\[ x_i^2= \begin{cases} (x_{i-1}+1)^2,&a_i=1\\ 0,&a_i=0 \end{cases} \]

展开第一种情况:

\[ x_i^2=x_{i-1}^2+2x_{i-1}+1 \]

因此

\[ E(x_i^2)=\Big(E(x_{i-1}^2)+2E(x_{i-1})+1\Big)p_i \]

这说明,我们可以通过 \(E(x_{i-1})\)\(E(x_{i-1}^2)\) 直接递推出 \(E(x_{i}^2)\)。开三个变量,分别记录 \(f_i\)\(E(x_i)\)\(E(x_i^2)\) 即可。

代码
#include<iostream>
#include<iomanip>
#define ld long double
using namespace std;
const int N = 1e5 + 10;

int n;

ld x1, x2, f;

int main() {

    cin >> n;
    for(int i = 1; i <= n; i++) {
        ld p;
        cin >> p;
        f = f + p * (3 * x2 + 3 * x1 + 1);
        x2 = p * (x2 + 2 * x1 + 1);
        x1 = p * (x1 + 1);
    }

    cout << fixed << setprecision(1) << f << endl;

    return 0;
}

Y1-4 图上游走

题意

给定一个 \(n\) 个点 \(m\) 条边的无向连通图。初始你位于 \(1\) 号节点,每一步你会等概率的走向当前节点连接的任意一条边,同时获得等于这条边编号的分数。当你走到 \(n\) 号节点,游戏立即结束。

问应该如何给边编号,使得游戏结束时的期望得分最小。

\(n\le 500,\ m\le \frac{n(n-1)}{2}\)

我们只需要知道游戏结束之前,每条边期望被走过的次数,然后将所有边按照这个期望降序排序,依次赋予 \([1,m]\) 的编号,即可最小化期望得分。

\(g(u,v)\) 表示在游戏结束之前走过 \(u\rightarrow v\) 的单向边的期望次数。那么边 \((u,v)\) 被走过的期望次数就是 \(g(u,v)+g(v,u)\)

考虑 \(g(u,v)\) 之间的转移。显然有:

\[ \sum_{v}{g(u,v)}-\sum_{v}{g(v,u)}= \begin{cases} 0,&u\ne 1\text{ and } u\ne n\\ 1,&u=1\\ -1,&u=n\\ \end{cases} \]

然而,如果我们直接依照这个式子,以所有 \(g(u,v)\) 为未知数列出一个线性方程组,其系数矩阵不一定满秩。比如,考虑一个最简单的情况:\(n=2\),其对应的方程组为:

\[ \begin{cases} g(1,2)-g(2,1)=1\\ g(2,1)-g(1,2)=-1\\ \end{cases} \]

有无穷解。

事实上,我们还没有利用“等概率前往任意一条边”这个条件。根据这个条件,可以推导出:从一个点出发前往每条边的期望次数也是均匀的,即 \(\forall u,\ \forall v_1,v_2\in to[u], \ g(u,v_1)=g(u,v_2)\)

证明

设事件 \(A_i\) 表示一共经过了 \(u\) 节点 \(i\) 次;变量 \(B\) 表示从 \(u\) 走到 \(v\) 的次数;\(Y_j\in\{0,1\}\) 表示第 \(j\) 次到达 \(u\) 节点时,是否前往了 \(v\) 节点。

\[ \begin{align*} E(B)&=\sum_{i=0}^{\infty}{E(B\mid A_i)P(A_i)}\\ &=\sum_{i=0}^{\infty}{E\Big(\sum_{j=1}^{i}Y_j\mid A_i\Big)P(A_i)}\\ &=\sum_{i=0}^{\infty}{\Big(\sum_{j=1}^{i}E\big(Y_j\mid A_i\big)\Big)P(A_i)} \end{align*} \]

显然,第 \(j\) 次的选择和 \(A_i\) 无关。设变量 \(C\) 表示到达 \(u\) 节点的次数,上式等于:

\[ \begin{align*} &\sum_{i=0}^{\infty}{\Big(\sum_{j=1}^{i}E(Y_j)\Big)P(A_i)}\\ =&\sum_{i=0}^{\infty}{\frac{i}{deg[u]}P(A_i)}\\ =&\frac{1}{deg[u]}\sum_{i=0}^{\infty}{iP(A_i)}\\ =&\frac{1}{deg[u]}E(C)\\ \end{align*} \]

\(v\) 无关。

加入这个条件后,方程组变得异常复杂,方程的数量比未知数的数量多。然而我们注意到,既然 \(g(u,v)\) 只与 \(u\) 有关,不如从节点的角度考虑。记 \(f(u)\) 表示 \(u\) 节点期望被经过的次数(即 \(E(C)\)),容易写出转移式:

\[ f(u)=\sum_{v\in to[u]}{\frac{f(v)}{deg[v]}} \]

从点的角度考虑还有一个好处:方程的数量为 \(O(n)\) 级别。我们只需要使用高斯消元即可 \(O(n^3)\) 求出所有 \(f(u)\)。这个方程每行只有第 \(i\) 列是 \(-1\),其它位置均为非负,系数矩阵显然满秩。

求出 \(f(u)\) 之后,根据 \(g(u,v)=\frac{f(u)}{deg[u]}\) 即可求出每条边期望经过的次数。

代码
#include<iostream>
#include<vector>
#include<algorithm>
#include<iomanip>
#define ld long double

using namespace std;
const int N = 510;

struct Edge {
    int u, v;
} edg[N * N];

int n, m;
int deg[N];
ld w[N * N];

ld a[N][N];

// 矩阵必然满秩,因此消元得到的增广矩阵的系数部分必为单位矩阵,无需特判其它情况
void gs() {
    for(int i = 1; i <= n - 1; i++) {
        int mx = i;
        for(int j = i + 1; j <= n - 1; j++) {
            if(abs(a[j][i]) > abs(a[mx][i])) mx = j;
        }
        for(int j = 1; j <= n; j++) swap(a[i][j], a[mx][j]);
        for(int j = n; j >= i; j--) a[i][j] /= a[i][i];
        for(int j = 1; j <= n - 1; j++) {
            if(j == i) continue;
            for(int k = n; k >= i; k--) a[j][k] = a[j][k] - a[j][i] * a[i][k];
        }
    }
}

vector<int> adj[N];

ld ans;

int main() {

    cin >> n >> m;
    for(int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        edg[i] = {u, v};
        adj[u].push_back(v);
        adj[v].push_back(u);
        ++deg[u];
        ++deg[v];
    }

    for(int i = 1; i <= n - 1; i++) {
        for(int &j : adj[i]) {
            if(j != n) a[i][j] = 1.0l / deg[j];
        }
        a[i][i] = -1;
    }
    a[1][n] = -1;

    gs();

    for(int i = 1; i <= m; i++) {
        int u = edg[i].u, v = edg[i].v;
        w[i] = a[u][n] / deg[u] + a[v][n] / deg[v];
    }

    sort(w + 1, w + 1 + m);

    for(int i = 1; i <= m; i++) {
        ans += (m - i + 1) * w[i];
    }

    cout << fixed << setprecision(3) << ans << endl;

    return 0;
}