《A First Course in Probability》-chaper3-条件概率和独立性-贝叶斯公式、全概率公式
设有事件A、B。
下面结合具体的题目进一步理解这种方法:
Q1:保险公司认为人可以分为两类,一类易出事故,另一类则不易出事故。统计表明,一个易出事故者在一年内发生事故的概率是0.4,而对不易出事故者来说,这个概率可以减小到0.2,若假定第一类人占人口比例的30%,现有一个新人来投保,那么该人在一年内出事故的概率有多大?
分析:这里我们要求解该人出事故的概率,那么设事件A是该人出事故。而对于事件B,我们当前有三种选择:
(1) 该人是易出事故为事件B1.
(2) 该人不是易出事故为事件B2.
(3) 该人未出事故为事件B3.
此时我们分别利用上文给出的贝叶斯公式代入计算,此时会发现,(1)(2)的结果是相同的,但是对于(3),我们最终将会推出P(A) = P(A)。而我们很容易看到的一点是,B1、B2和A均是相交集合,而B3则与A是互斥集合。这告诉我们,在应用贝叶斯公式的时候求解事件A的概率时,所选取的第二个事件B应该是与事件A相交的。
本质上讲,所谓的全概率公式、贝叶斯公式就是基于独立性的定义和基本的组合原理,将问题进一步程度的抽象。
全概率公式:
贝叶斯公式:
Ex1:
考虑n张含有一张黑桃“A”的扑克,从第一张扑克开始翻牌,游戏者只有一次猜测机会,可以选择第i次翻牌猜测这张牌是否是黑桃“A”,那么游戏者采取这样的策略能够让获胜几率更大呢?
分析:要解决这个问题,分别讨论第i次猜牌胜利的几率即可
Ex2:
在回答一道多项选择问题时,学生或者知道正确答案,或者就猜一个。令p表示他知道正确答案的概率,则1-p表示猜的概率。假定学生猜中正确答案的概率为1/m,m就是多项选择题的可选答案数。在已知他回答正确的条件下,该学生知道正确答案的概率是多少?
分析:这是比较典型的贝叶斯公式的应用了,时空上应该知道正确答案然后回答正确,但是现在反过来,正是贝叶斯公式能够处理的。
Ex3:一项血液化验有95%的把我诊断某种疾病,但是,这项化验用于健康人也会有1%的“伪阳性”结果(即如果一个健康人接受这项化验,则化验结果乌镇此人患有该疾病的概率是0.01)。如果该疾病的患者事实上只占总人口的0.5%,若某人化验结果为阳性,则此人确实患疾病的概率是多少?
分析:单从问题来看,一般的时空逻辑是像得病,然后化验成阳性,现在的问题变成了已经成阳性,反过来问你真的患病的概率,能够看到是典型的贝叶斯公式的应用。
ACM中的概率问题主要涉及到基本的概率公式(全概率、条件概率),然后可能基于递推的思路去解决问题(概率dp),也可能用到数学期望以及一些组合数学中的结论。
Ex4:决斗(uva 1636).
首先在手枪里随机装一些子弹,然后打了一枪,发现没有子弹。你希望下一枪也没有子弹,是应该直接再开一枪(输出SHOOT),还是转一下再开(输出ROTATE)?如果两种策略下没有子弹的概率相等,输出EQUAL。
手枪里的子弹看成一个环形序列,开枪一次对准下一个位置。例如,子弹序列为0011,第一次开枪一定在位置1或2,因此开枪之后位置位于2或3.
分析:很典型的数学问题,解决思维层面在编程计算上几乎没有什么难度。我们分别来看两种策略下没子弹的概率。
对于直接再打一枪,能够看到它是基于第一枪打孔的条件概率,它的计算方法应该是环形01串中”00”的数量除以”00”、”01”的数量之和,后者其实本质上是整个串中0的数量。
对于转一下再开枪,这一枪打孔的概率就是0的比率。
设a是环形01串中“00”个个数,b是“0”的个数,比较a/b和b/n(字符串长)的大小即可。
Ex5:麻球。 (uva 11021)
题目大意:每个麻球的生存周期是一天,一天结束后它可能繁殖出[0,n-1]个麻球,它们的概率分别是P[0]~P[i-1],那么起初k个麻球,在前m天全部死亡的概率是(包括不足m天就死亡的)?
分析:由各个麻球的独立性,我们其实只需要求解一个麻球进行繁殖,在前m天就全部死亡的概率,假设记为f[m],那么最终结果就是f[m]的k次方.
那么下面我们面临的问题变成了如何求解f[m]。
这里就要用到全概率公式,或者理解成动态规划也可以。这里我们通过概率的角度去解释。
因此我们容易得到如下的递推关系式.
简单的参考代码如下:
#include<cstdio> #include<cmath> #include<cstring> using namespace std; const int maxn = 1000 + 10; const int maxm = 1000 + 10; int n , k , m; double P[maxn] , f[maxm]; int main() { int T; int tt = 1; scanf("%d",&T); while(T--) { memset(f , 0 , sizeof(f)); scanf("%d%d%d",&n,&k,&m); for(int i = 0;i < n;i++) scanf("%lf",&P[i]); f[1] = P[0]; for(int i = 2;i <= m;i++) { for(int j = 0;j < n;j++) f[i] += P[j]*pow(f[i-1],j); } printf("Case #%d: %.7lf\n",tt++ , pow(f[m],k)); } return 0; }
Ex6:蒙提霍尔问题的推广(uva 10491).
这个问题以蒙提霍尔问题为背景。笔者在《Numberical Methods》的专栏中曾经介绍过,这里将牛、车、主持人打开的门的数量分别记为a、b、c,那么请问在玩家选择了一个门之后,主持人打开c个门后是牛的门,玩家总是采取换门策略的情况下,抽中车的概率。
利用简单的全概率公式即可。
Uva11181:
给出n个人买东西的概率,n个人之见是否买东西是独立的 ,现在问在n个人中k个人买东西的情况下,第i个人买了东西的概率是多少。
分析:设n个人中k个人买了东西为事件E,第i个人买了东西的事件是Ei,那么根据条件概率的概念,这道问题即是求P(Ei|E)=P(EEi) / P(E)
现在的问题在于,如何编程计算P(E)和P(EEi),这是一个基于dfs的基本的生成组合的问题。我们即将买的人标记为1,没买的标记为0.
P(E)需要求出总长为n的01序列中含有k个1的所有序列,然后对应着得到概率,累加之后得到P(E).
P(EEi)则是求出长度为n的01序列中k个1但是第i个必须是1的所有序列,然后得到对应的概率和,得到P(EEi).
然后二者相除得到最终的结果。
参考代码如下:
#include<cstdio> #include<cstring> using namespace std; const int maxn = 25; double pro[maxn]; int visit[maxn]; int n , k; double pa , pb; double temp; void dfs(int num , int now) { if(num == k) { double p = 1.0; for(int i = 0;i < n;i++) { if(visit[i]== 1) p *= pro[i]; //printf("%d",visit[i]); else p *= (1 - pro[i]); } temp += p; } for(int i = now + 1;i < n;i++) { if(!visit[i]) { visit[i] = 1; dfs(num + 1 , i); visit[i] = 0; } } } int main() { int tt = 1; while(scanf("%d %d",&n,&k) &&(n || k)) { printf("Case %d:\n",tt++); for(int i = 0;i < n;i++) scanf("%lf",&pro[i]); temp = 0.0; dfs(0 , -1); pa = temp; //printf("%lf ",pa); for(int i = 0;i < n;i++) { visit[i] = 1; temp = 0.0; dfs(1 , -1); pb = temp; //printf("%lf ",pb); printf("%.6lf\n",pb/pa); visit[i] = 0; } } }