ベイズの定理に納得がいかなかったからPythonで試してみたら納得がいったって話。
動機
以下のサイトを見ていて、
「どう見ても3/8じゃん?P(A)も8/17じゃん?」
ってなって納得行かなかった。
なので、実際にコードを書いて実験してみて、そもそもの考え方が間違ってたんだなーと理解した。
第1の実験(袋3つとも、確率が同じ)
条件
わかりやすくするため、ボールの数を変えます。
たくさん試行回数を増やせば、まぁまぁいい感じの割合になるでしょう(コードでは1200万回試してます)
袋の名前 | 白ボールの数 | 赤ボールの数 | このバッグが選ばれている状態で白ボールを引く確率 |
---|---|---|---|
bag_A | 1 | 1 | 1/2 |
bag_B | 2 | 2 | 2/4 = 1/2 |
bag_C | 3 | 3 | 3/6 = 1/2 |
勘違いしていたロジック
すべてのボールから、1つだけランダムに選ぶ。
bag_Aから選ばれている確率は大体1/6。
私が愚かであったときの考え。
import random
balls = [['bag_A', 'white'], ['bag_A', 'red'],
['bag_B', 'white'], ['bag_B', 'red'],
['bag_B', 'white'], ['bag_B', 'red'],
['bag_C', 'white'], ['bag_C', 'red'],
['bag_C', 'white'], ['bag_C', 'red'],
['bag_C', 'white'], ['bag_C', 'red']]
chosen_bags = []
for _ in range(12_000_000):
chosen_bag, chosen_color = random.choice(balls) # 1回の抽選のみ
if chosen_color == 'white':
chosen_bags.append(chosen_bag)
print(f'A: {chosen_bags.count("bag_A")}') # A: 1_000_000くらい
print(f'B: {chosen_bags.count("bag_B")}') # B: 2_000_000くらい
print(f'C: {chosen_bags.count("bag_C")}') # C: 3_000_000くらい
正しいロジック
まずbagを選び、その上でボールを選ぶ。
bag_Aから選ばれている確率は大体1/3。
import inspect
import random
def name_of(var):
# 渡された引数の名称を返す
callers_local_vars = inspect.currentframe().f_back.f_locals.items()
result = [var_name for var_name,
var_val in callers_local_vars if var_val is var]
return result[0]
bag_A = ["white_ball", "red_ball"]
bag_B = ["white_ball", "white_ball", "red_ball", "red_ball"]
bag_C = ["white_ball", "white_ball", "white_ball", "red_ball", "red_ball", "red_ball"]
bags = [bag_A, bag_B, bag_C]
chosen_bags = []
for _ in range(12_000_000):
chosen_bag = random.choice(bags) # 1回目の抽選
chosen_ball = random.choice(chosen_bag) # 2回目の抽選
if chosen_ball == 'white_ball':
chosen_bags.append(name_of(chosen_bag))
print(f'A: {chosen_bags.count("bag_A")}') # A: 2_000_000くらい
print(f'B: {chosen_bags.count("bag_B")}') # B: 2_000_000くらい
print(f'C: {chosen_bags.count("bag_C")}') # C: 2_000_000くらい
第2の実験(袋3つ、どれも確率が異なる)
条件と結果
またまた、ボールの数を変えます。
第1の実験ではどの袋でも白ボールが出る確率は同じでしたが、
今回はその割合を変更します。
袋の名前 | 白ボールの数 | 赤ボールの数 | このバッグが選ばれている状態で白ボールを引く確率 |
---|---|---|---|
bag_A | 1 | 1 | 1/2 = 6/12 = 50% |
bag_B | 1 | 2 | 1/3 = 4/12 = 33% |
bag_C | 1 | 3 | 1/4 = 3/12 = 25% |
愚かであった頃の自分だった場合、
A:B:C が1:1:1になると考えてましたが…。
print(f'A: {chosen_bags.count("bag_A")}') # A: 2001292 (46.185%)
print(f'B: {chosen_bags.count("bag_B")}') # B: 1333267 (30.769%)
print(f'C: {chosen_bags.count("bag_C")}') # C: 998_652 (23.046%)
1:1:1とはなりませんね。
bag_Aになる可能性が高い。
ループ数を3億6千万回(30倍)にしてみる
下の式でも書きますが、36が分母になるのでキリいい感じになるだろうと思ってやってみました。
確かに、公式通りの確率(6:4:3)に近づきましたね。
print(f'A: {chosen_bags.count("bag_A")}') # A: 59995474 (46.155%)
print(f'B: {chosen_bags.count("bag_B")}') # B: 39995278 (30.769%)
print(f'C: {chosen_bags.count("bag_C")}') # C: 29995808 (23.076%)
ベイズの公式での計算もして、実験結果とベイズの公式での計算結果がほぼ同じであることを確かめる
(次の段階で約分しやすいように、分母は36で揃えておく)
bag_Aかつwhite_ballを P(AnW) = P(A) * P(W|A) = 1/3 * 6/12 = 6/36
bag_Bかつwhite_ballを P(BnW) = P(B) * P(W|B) = 1/3 * 4/12 = 4/36
bag_Cかつwhite_ballを P(CnW) = P(C) * P(W|C) = 1/3 * 3/12 = 3/36
上記より、white_ballになる確率を P(W) として (6 + 4 + 3) /36 = 13/36
bag_Aになる確率: P(AnW)/P(W) = 6/13 = 46.154%
bag_Bになる確率: P(BnW)/P(W) = 4/13 = 30.769%
bag_Cになる確率: P(CnW)/P(W) = 3/13 = 23.077%
実験の結果とだいぶ近いですね。
なかなかいい感じなのではないでしょうか?
計算上も「白ボールを引く確率 + 赤ボールを引く確率 = 100%」であることを確認する。
愚かしかった頃の自分は、ベイズの公式の計算を見て「そんな複雑な計算してたら、足したときに1にならないっしょ」って疑ってました。
それを晴らす上でも、一応の計算。
最終的に、P(W) + P(R) = 1 になればOKですね。
P(W) は先程確認したとおり、 (6 + 4 + 3) /36 = 13/36
**P(R)**は、
bag_Aかつred_ballを P(AnR) = P(A) * P(R|A) = 1/3 * 6/12 = 6/36
bag_Bかつred_ballを P(BnR) = P(B) * P(R|B) = 1/3 * 8/12 = 8/36
bag_Cかつred_ballを P(CnR) = P(C) * P(R|C) = 1/3 * 9/12 = 9/36
上記より、P(R) = (6 + 8 + 9) /36 = 23/36
P(W) + P(R) = 13/36 + 23/36 = 1
うん、なりますね。
理解はした。でも、自然に利用するまでは難しそう
実際に「これはベイズを使わねば!」っていう判断って難しそうですね。
ランダムに選ぶ回数が2回以上になっているか?というところは注意してみていかないと、また同じミスをおかしてしまいそう。
似たようなことを普段からやっている環境にいるわけではいないので、忘れてしまいそうです。
1ヶ月後の自分でも、「ボールは全部で9個、白ボールは全部で3個なんだから、単純にP(W) = 3/9でしょ?」ってなってしまいそうです。
しかも、いやらしいことに3/9 = 12/36なので、結構近いからミスに気づきにくいんですよね。。。
chosenはchooseの過去分詞
choose - chose - chosen
読み方は「チョウズン」みたいな感じ。音節は2つ。
知らない人がいるかも?
Discussion