モンティ・ホール問題を自分なりに検証する/Pythonサンプル
目次
モンティ・ホール問題を自分なりに検証する/Pythonサンプル
数学の本を読んでて「モンティ・ホール問題」に興味をひかれました。
モンティ・ホール問題とは
もともとは、こんなゲームです。
3つの箱があり、その内の1つに景品が入ってます。残りは空箱です。
プレイヤーは3つの箱のどれかを選ぶことができ、その中に景品があれば、それをもらえるものとします。
いま、プレーヤーが1つの箱を選択した後、景品がどの箱にはいっているかを知っている司会者(モンティ)が、残りの2箱のうち、1つの空き箱を開けます。
ここでプレーヤーは、最初に選んだ箱のままでもよいし、残っている箱に変更しても良いと告げられます。
さて、どうしますか?
普通に考えれば、変えても変えなくても同じのはずです。
3つの箱のどれかひとつなので、最初の段階では「三分の一」。
このうち、司会者がひとつの箱を開けるので残りは二つ。
変えても、変えなくても確率は「二分の一」になるだけのはずですから。
ところが。
マリリン・ボス・サバント(Marilyn vos Savant)さんという女性が、「変えた方が2倍の確率で当たる」と発言したので、さあ大変。
当時の学者さんとかが「確率の勉強をしなおせ。この素人が・・」みたいに反論して、大論争が巻き起こったけど、結局、彼女が正しかった・・。
とまあ。
そんな問題が「モンティ・ホール問題」です。
答えがわかっているのに、直感的には違和感がある
既に答えがでている問題ではあります。
でも、直感的には違和感があります。
だって。
以下のようにA,B,Cの箱があったとき。
当然ですが、選んだ箱が当たりの確率は「三分の一」です。
それを頭にいれたうえで
- プレイヤーが選択する箱(どれを選んでも良い)。
- 当たりの箱(どれか一つ。司会者は知っている。)
- 司会者がオープンできる箱(プレイヤーの選んだ箱と当たり以外の箱)
の3つに場合分けしてみます。
例えば。
プレイヤーがAを選び、当たりもAだった場合は、司会者はB,Cのどちらからの箱をあけることができますが、その時、当たりがBだったら、司会者はCの箱しかオープンできません・・みたいな感じです。
そのそれぞれのケースで、プレイヤーが最初に選んだ箱を変更した場合としなかった場合の「当たり」と「はずれ」の表を書いてみます。
すると。
こんな感じになります。
これでは変更しても、しなくても当たりの確率は同じに見えます。
でも・・、正解はそうじゃないので、上記のアプローチは何かが間違っているのです。
問いかけ自体が「二択である点がポイントだと考える
上記のアプローチのどこが間違っているのかを、よーく考えてみれました。
それで気づいたのは。
この問いかけ自体が「変更する」か「変更しないか」の二択だということです。
この判断は、例えばプレーヤーが最初に選択した箱が当たりの場合、司会者のオープンできる2通りのどちらをオープンしたとしても変わりません。
なので。
司会者が2つの箱のどちらかを選んだとしても、当たり・はずれの確率に影響をあたえません。
そう考えると、実際の確率は以下の表みたいになります。
そうすると。
確かに、変更したら「三分の二」、しなかったら「三分の一」。
変更するほうが2倍の確率になってます。
pythonプログラムの仕様・ソース・結果
上記をpythonで試してみます。
とりあえず、BOXをこんな感じでシュミレート。
box = ['A', 'B', 'C']
司会者が選ぶことが可能なBOXは
openable['AA'] = ['B', 'C']
みたいに定義して、プレイヤーが「A」を選択、当たりが「A」なら、「B」または「C」が選択可能・・てな感じで全パターンを用意します。
あと、プレーヤーが変更した時に選択できるBOXは
changed['AAB'] = 'C'
みたいに、プレーヤー選択「A」、当たりが「A」、司会者が「B」をオープンしたらプレーヤーが変更できるのは「C」です・・みたいに、これも全パターン定義します。
あとは、それを10万回くらい、ランダムに選択してやってみるというプログラムがこちらです。
import random as rnd box = ['A', 'B', 'C'] openable = {} openable['AA'] = ['B', 'C'] openable['AB'] = ['C'] openable['AC'] = ['B'] openable['BB'] = ['A', 'C'] openable['BA'] = ['C'] openable['BC'] = ['A'] openable['CC'] = ['A', 'B'] openable['CA'] = ['B'] openable['CB'] = ['A'] changed = {} changed['AAB'] = 'C' changed['AAC'] = 'B' changed['ABC'] = 'B' changed['ACB'] = 'C' changed['BBA'] = 'C' changed['BBC'] = 'A' changed['BAC'] = 'A' changed['BCA'] = 'C' changed['CCA'] = 'B' changed['CCB'] = 'A' changed['CBA'] = 'B' changed['CAB'] = 'A' count = {} count['change'] = 0 count['stay'] = 0 for i in range(100000): hit = rnd.randint(1, 999) % 3 sel = rnd.randint(1, 999) % 3 if (hit == sel): opn = rnd.randint(1, 99) % 2 else: opn = 0 keystr = box[sel] + box[hit] changedbox = changed[keystr + openable[keystr][opn]] if (box[hit] == changedbox): count['change'] += 1 if (sel == hit): count['stay'] += 1 print('changed:' + str(count['change'])) print('stay:' + str(count['stay']))
この位なら処理は一瞬で終わります。
結果は。
みたいな感じになります。
変更した方が「6605回当たり」で、変更しない場合が「3395回当たり」ということなので、まあ大体2倍くらい変更した方が当たってます。
まあまあ、納得できました。
ではでは。