![]() |
![]() |
![]() |
パラドックス系の話題で挙がる
モンティ・ホール問題
結果を聞いても、納得できない人が多い結構有名な問題です。
ここでプレーヤーは、最初に選んだドアを、残っている開けられていないドアに変更してもよいと言われる。プレーヤーはドアを変更すべきだろうか?」
1990年9月9日発行、ニュース雑誌 Parade にて、マリリン・ボス・サヴァントが連載するコラム「マリリンにおまかせ」において上記の読者投稿による質問に「正解は『ドアを変更する』である。なぜなら、ドアを変更した場合には景品を当てる確率が2倍になるからだ」と回答した。すると直後から、読者からの「彼女の解答は間違っている」との約1万通の投書が殺到し、本問題は大議論に発展した。
ドアを変えても変えなくても、確率同じでしょ?
というのが納得できない理由です。
確かに直感的には、1/2に感じます。
当然1/2だろ。何を考える必要がある。とさえ思います。
こういう時は実際に動かして検証すると良いと思います。
実装方針
- PHPで実装してみる(趣味)
- 最初のドアが当たりの回数と2つ目のドア(選びなおすドア)が当たりの回数を数える
- 考えをまとめる目的なので、動作確認してないです。ごめんなさい。
- 念のため100万回試行できるようにしておこうか・・
そして実装(PHP)
まず、問題の意味をできるだけ、表現するように実装してみました。
forの中身がキモですね。
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<?php $first_hit_cnt = 0; //一枚目のドアが当たり回数 $second_hit_cnt = 0;//2枚目(選びなおし)のドアの当たり回数 $try_cnt = 1000000; //100万回試行 for ($i = 0; $i < $try_cnt; $i++) { //あえて明示的に初期化(nullはまだ決まってないよ。と言う意味で使ってます) $first_door = null;// 最初に選ぶドア $open_door = null;// 司会が開くドア $second_door = null;// 2つ目の選択肢のドア // 3つのドア $doors = array(TRUE, FALSE, FALSE); //あたりはTRUE ハズレはFALSE //ランダムに shuffle($doors); //1つ選ぶ $first_door = array_shift($doors); //ここで$doorsの中身は2つになる $rest_door_1 = array_shift($doors); // 残りのドア1つ目 $rest_door_2 = array_shift($doors); // 残りのドア2つ目 // 残り2つのドアから外れのドアを1つ選んで開く if ($rest_door_1) { $open_door = $rest_door_2; //外れドア $second_door = $rest_door_1; // 当たりドア } else //ちょっと違和感あるelseですが、当たりドアは1つしかないので if ($rest_door_2) { $open_door = $rest_door_1; // 外れドア $second_door = $rest_door_2; // 当たりドア } // 結果を記録 // 最初のドアが当たりの場合 if ($first_door) $first_hit_cnt++; // 2つ目の選択肢のドアが当たりの場合 if ($second_door) $second_hit_cnt++; } //結果表示 echo ‘■回数’ . “¥n”; echo $first_hit_cnt; echo “¥n”; echo $second_hit_cnt; echo “¥n”; echo ‘■割合’ . “¥n”; echo ‘1 : ‘ . $second_hit_cnt / $first_hit_cnt; |
リファクタリングする
問題を整理したいので、
リファクタリングしていきます。
まず、
3つのドアの関係性を結果から場合わけでまとめます。
すいません、事前準備みたいな感じになってしまいました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
〜省略〜 // 最初に選んだドアあたりなら、残りの2枚はハズレ確定 // (当たりのドアならif の条件は真となります) if ($first_door) { //あえて明示的に書いてみる //最初のドアが当たりなら、他のドアは当然ハズレ $open_door = FALSE; $second_door = FALSE; // 最初に選んだドアが外れでないと、 // 当たりの可能性がないのでelse ifとする } else if ($rest_door_1) { $open_door = $rest_door_2; //外れドア $second_door = $rest_door_1; // 当たりドア } else if ($rest_door_1) { $open_door = $rest_door_1; // 外れドア $second_door = $rest_door_2; // 当たりドア } |
結果回数を格納するカウンターを、
判別した時点でカウントされるように、移動してしまう。
この部分の内容を
1 2 3 4 5 6 7 |
// 結果を記録 // 最初のドアが当たりの場合 if ($first_door) $first_hit_cnt++; // 2つ目の選択肢のドアが当たりの場合 if ($second_door) $second_hit_cnt++; |
こちらにまとめる。
上の内容は同時に削除する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
if ($first_door) { $open_door = FALSE; $second_door = FALSE; $first_hit_cnt++; // 一つ目が当たり } else if ($rest_door_1) { $open_door = $rest_door_2; // 外れドア $second_door = $rest_door_1; // 当たりドア $second_hit_cnt++; // 2つ目が当たり } else if ($rest_door_2) { $open_door = $rest_door_1; // 外れドア $second_door = $rest_door_2; // 当たりドア $second_hit_cnt++; // 2つ目が当たり } |
$open_doorと$second_doorは、以降どこにも使用されていない。
結果をカウントできるため、消去可能。
また、2つ目の選択肢のドアが何なのかは、
結果から見ると、全く関係ないという事ですね。
さらに、else if ($rest_door_1)と $rest_door_2 のブロックの中身が
同じなので・・
1 2 3 4 5 6 7 8 |
if ($first_door) { $first_hit_cnt++; // 一つ目が当たり } else if ($rest_door_1) { $second_hit_cnt++; // 2つ目が当たり } else if ($rest_door_2) { $second_hit_cnt++; // 2つ目が当たり } |
orでまとめる。
1 2 3 4 5 |
if ($first_door) { $first_hit_cnt++; // 一つ目が当たり } else if ($rest_door_1 || $rest_door_2) { $second_hit_cnt++; // 2つ目が当たり } |
ここまで来ると、
という事が見た目で分かります。
最初のドアが当たりなら、$rest_door_1と$rest_door_2は外れ確定。
最初のドアが外れなら、
$rest_door_1か$rest_door_2に当たりが存在する為、
このように省略できます。
1 2 3 4 5 |
if ($first_door) { $first_hit_cnt++; } else { $second_hit_cnt++; } |
さらに三項演算子で、1行で表すと(ダメでしょうか)
1 |
$first_door ? $first_hit_cnt++ : $second_hit_cnt++; |
やりすぎですけど、スッキリした気がします。
公式ぽいですねえ。
わかった事
結果が決まってしまう事が分かります。
- 最初のドアが当たりなら、最初のドアが当たり
- 最初のドアがハズレなら、2つ目の選択肢のドアが当たり
という事ですので、
最初のドアが当たりの確率は、1/3
最初のドアがハズレの確率は、2/3
最初のドアがハズレであれば、2つ目の選択肢のドアが当たりなので
2つ目の選択肢のドアの当たりの確率は、2/3
最初のドアが当たりの確率は 1/3
2つ目の選択肢のドアの当たりの確率は 2/3
確かに2つ目の選択肢のドアは、
最初のドアが当たる確率の2倍になりました。
ちなみに、リファクタリング後のコード
モンティ・ホールの問題をリファクタリングしていくと、こうなってしまいました。
いきなりこんなコード見せられても、何なのか、全く意味わかんないですね。
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 |
<?php $first_hit_cnt = 0; //一枚目のドアが当たり回数 $second_hit_cnt = 0;//2枚目(選びなおし)のドアの当たり回数 $try_cnt = 1000000; //100万回試行 for ($i = 0; $i < $try_cnt; $i++) { // 3つのドア(&初期化) $doors = array(TRUE, FALSE, FALSE); //あたりはTRUE ハズレはFALSE //ランダムに shuffle($doors); //1つ選ぶ $first_door = array_shift($doors); //ここで$doorsの中身は2つになる //結果を記録 $first_door ? $first_hit_cnt++ : $second_hit_cnt++; } //結果表示 echo ‘■回数’ . “¥n”; echo $first_hit_cnt; echo “¥n”; echo $second_hit_cnt; echo “¥n”; echo ‘■割合’ . “¥n”; echo ‘1 : ‘ . $second_hit_cnt / $first_hit_cnt; |
あとがき
パラドックスは、ソーシャルゲームに利用されて、
そして禁止されてきた歴史があります。
コンプガチャが最たるものですね。
コンプガチャはちょっと勘の良い人であれば、
これやばい仕様のやつだ、と。気がつくと思うのですが、
モンティ・ホール問題くらい気づきづらいパラドックスを組み込んだガチャが存在するのなら・・
と、すでに存在しているのでしょうか。
ドキドキします・・