モンティ・ホール問題を Python で解いてみた

ベイズ推定の例題としてよく取り上げられるモンティ・ホール問題.説明を聞けばなんとなくは分かるけど,自分の手で確かめてみたいという方へ.

モンティ・ホール問題とは

3つのドアがあり,どれか一つが当たりのドアです.
(1) プレイヤーはどれか一つのドアを選びます.
(2) 正解のドアを知っている司会者 (モンティ) が,
   残りの 2 つのドアから外れのドアを 1 つ開けてくれます.
(3) 一つ外れのドアが分かった状態で,
  プレイヤーはもう一度ドアを選択できます.

このとき,プレイヤーは最初に選択したドアにとどまるべきでしょうか?
それとも,ドアを変更すべきでしょうか?

問題の説明はこちらのサイトを参考にさせて頂きました.確率計算による解法も載っていますので,気になる方は合わせてどうぞ.

シミュレーションによる解法

モンティ・ホール問題をシミュレーションで解いてみます.1000 回の試行を行い,ドアを変更しない場合と変更する場合で正解を当てる回数がそれぞれ何回あったかを求めます.まずはライブラリのインポートから.

import random

次に,モンティ・ホール問題 1 回の試行を関数化します.
ドアを変更しないのが正解だった回数を表す変数 stay と,ドアを変更する場合が正解だった回数を表す変数 exchange を設定し,1 回の試行ごとにアップデートしていきます.

def MontyHall(stay, exchange):
   doors = ["A", "B", "C"]
   door_with_prize = random.choice(doors)
   # プレイヤーが最初に選択するドア
   players_door_1 = random.choice(doors)
   
   # モンティが開けるドアは,プレイヤーのドアと正解のドア以外からランダムに選択
   Montys_choices = [d1 for d1 in doors \
                     if d1 not in ([door_with_prize, players_door_1])]
   Montys_door = random.choice(Montys_choices)
   
   # プレイヤーがドアを交換する場合のドア
   players_door_exchange = [d2 for d2 in doors \
                            if d2 not in ([players_door_1, 
                                           Montys_door])][0]
   
   # 結果の評価
   if door_with_prize == players_door_1:
       stay += 1
   elif door_with_prize == players_door_exchange:
       exchange += 1
       
   return(stay, exchange)

1,000 回の試行を行い,stay・exchange がそれぞれどの程度になるか確かめます.

stay, exchange = 0, 0
max_iter = 1000

for i in range(max_iter):
   stay, exchange = MontyHall(stay, exchange)
   
# ドアを交換するのが正解であった割合 (%)
rate_exchange = 100 * exchange/max_iter

print("stay: {}, exchange:{}, rate_exchange: {} %"\
     .format(stay, exchange, rate_exchange))

出力は以下のようになりました.
stay: 353, exchange:647, rate_exchange: 64.7 %
(乱数シードを固定していないので,シミュレーションごとに差が生じます)

このシミュレーション結果から,「ドアを変更したほうが当たりのドアを選択する確率が高い」ということが確認できました.

おまけ

max_iter を増やしていくと,rate_exchange が 2/3 へ収束していくことが分かります.

参考

高校数学の美しい物語:モンティ・ホール問題とその解説

いいなと思ったら応援しよう!