【Puzzle】不公平的硬币

  |  

摘要: 不公平的硬币

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings


之前概率面试题连载已经写了17期了,题目主要是《Fifty challenging problems in probability with solutions》这本书中的题。以及朋友在面试中遇到的题。对于每道题,首先给出思路参考,然后用蒙特卡洛模拟的方式验证结果。往期的内容整理在这篇文章里;或者看这个 github 仓库

之前断更了很长时间是因为按顺序刷书里面的题时,有一个题卡住了,是一个有点复杂的贝叶斯公式的问题,推公式计算的结果跟模拟的结果始终对不上,后来一直没解决。现在过了一段时间,准备重新做一下哪个题,解决了之后再发出来。今天的第 18 期我们先看一个贝叶斯公式里面比较简单但是很有代表性的一个题,回忆一下贝叶斯公式的背景和拿到问题时基本的分析思路,然后我们再分析之前卡壳的那道题。


贝叶斯公式的背景

我们有一个假设 H,可能成立可能不成立,也就是 H 是有一定概率成立的,记为 P(H)。例如我们有三个盒子,名称分别为 a,b,c。现在随机取出一个盒子,我们可以猜测该盒子的名称,如果我们猜 a,这就构成了一个假设:随机抽到的盒子的名称是 a。因为盒子共有 3 个,且盒子是随机取的,我们可以认为 P(H) = 1/3,这个概率称为先验概率。先验概率是根据经验来的,前面的例子的 1/3 就是一个经验值。

现在我们得到了一条证据 e,这个证据一般是某个实验的观察值。还是以三个盒子举例,a, b, c 三个盒子内均有一些球,其中 a 中有 10 个黑球,b 中有 10 个白球,c 中有 5 个黑球 5 个白球。我们可以针对盒子名称做实验:抽出一个球看颜色。假设看到的颜色是白色,我们就得到了一条证据:随机抽出一个球的颜色是白色。根据实际情况,证据 e 可能加强假设 H 成立的可能性,也可能削弱假设 H 成立的可能性。我们想知道的是当我们拿到证据 e 之后,假设 H 成立的概率是多少,这个概率记为 P(H|e),称为后验概率,后验概率是根据数据(也就是证据)来的。

下面这个根据先验概率 P(H) 和证据 e,计算后验概率 P(H|e) 的公式称为贝叶斯公式。

拿到一个贝叶斯公式的概率问题,首先要分析的就是假设是什么,证据是什么。然后再套公式就不容易套错了。

问题

有 10 枚硬币,其中有 1 枚是不公平的,随机抛出正面的概率为 0.8,另外 9 枚都是公平的,也就是随机抛出正面的概率是 0.5。

现在从 10 枚硬币中随机取出 1 枚,随机地抛了 3 次,结果为”正正反”。求该硬币为不公平硬币的概率。

思路参考

根据题目描述,假设 H 是 “该硬币为不公平硬币”,证据 e 是 “随机抛出该硬币 3 次的结果为正正反”

现在我们要求的是当我们有了证据 e 的条件下,假设 H 成立的概率是多少,也就是 P(H|e)

贝叶斯公式如下

其中 P(H) 是先验概率,根据业务理解,也就是本题给的条件,是 1/10。相应地 $P(\overline{H})$ 就是 9/10。

P(e|H) 是当假设 H 成立时,在一次观察中获得证据 e 的概率。也就是如果该硬币为不公平硬币,随机抛3次得到正正反的概率。

有了以上概率值,我们就可以根据贝叶斯公式计算后验概率 P(H|e) 了

蒙特卡洛模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import numpy as np
from multiprocessing import Pool

def flip(p):
# 如果投掷结果为正,返回 True
return np.random.rand() < p

def test(ps):
np.random.seed()
N = 0 # 记录实验结果为"正正反"的次数
n = 0 # 记录实验结果为"正正反"时,不公平硬币的次数
T = int(1e7)
for _ in range(T):
# 随机取到的硬币,如果 idx 为 9 则取到不公平硬币
idx = np.random.randint(0, 10)
p = ps[idx]
# 实验: 投 3 次,如果结果为"正正反"则记录
if not flip(p):
continue
if not flip(p):
continue
if flip(p):
continue
N += 1
if idx == 9:
n += 1
return n / N

p1 = 0.5 # 公平硬币出正面的概率为 0.5
p2 = 0.8 # 不公平硬币出正面的概率为 0.8
ps = [p1] * 9 + [p2] # 10 枚硬币,其中 1 枚为不公平硬币
args = [ps] * 8
pool = Pool(8)
ts = pool.map(test, args)
for i in range(len(args)):
t = ts[i]
print("P(不公平硬币|投掷结果为\"正正反\"): {:.6f}".format(t))

模拟结果

1
2
3
4
5
6
7
8
P(不公平硬币|投掷结果为"正正反"): 0.102335
P(不公平硬币|投掷结果为"正正反"): 0.102071
P(不公平硬币|投掷结果为"正正反"): 0.102196
P(不公平硬币|投掷结果为"正正反"): 0.102102
P(不公平硬币|投掷结果为"正正反"): 0.102111
P(不公平硬币|投掷结果为"正正反"): 0.102182
P(不公平硬币|投掷结果为"正正反"): 0.102154
P(不公平硬币|投掷结果为"正正反"): 0.102363

Share