数学史上最も議論を巻き起こしたモンティーホール 問題とは、
- 箱が3つあり、1つが当たり。 客は1つ選ぶ。
- 1.で箱を開けないで、運営が他2つの箱を見て当たりではない箱を開ける。
- 当然、箱が2つになり、どちらかに当たりが入っている。
- 始めに選んだ箱をそのまま開けるか、又はもう一方の箱にするか。
- その場合の当たりの出る確率はどうなるか。
答えは選び直しの方が確立が2倍になる。
実際にコードを書いて実証してみました。
どう考えても50%。二重スリット同様、この宇宙の物理法則プログラムのバグに違いない。。
回数(N) | 当たり数 選び直し有(x) | 当たり数 選び直し無(y) | x割y |
---|---|---|---|
100 | 76 | 26 | 2.923076923 |
1,000 | 670 | 352 | 1.903409090 |
10,000 | 6,692 | 3,351 | 1.997015816 |
100,000 | 66,725 | 33,289 | 2.004415873 |
1,000,000 | 666,567 | 333,530 | 1.998521872 |
10,000,000 | 6,668,441 | 3,331,391 | 2.001698689 |
100,000,000 | 66,661,997 | 33,339,322 | 1.999500679 |
1,000,000,000 | 666,671,881 | 333,324,650 | 2.000067744 |
10,000,000,000 | 6,666,804,668 | 3,333,386,023 | 2.000009786 |
100,000,000,000 | 66,666,660,439 | 33,333,324,259 | 2.000000357 |
1,000,000,000,000 | 666,665,927,317 | 333,334,004,449 | 1.999993755 |
※今回の試験では1兆回行うと選び直しの方が1.99999375527倍当たりが多くなりました。事前の予測値と近似しています。(1000億回の方が結果としては良い)
それぞれ計算してるのでN=x+yにはなりません。
計2兆回分は就寝前に起動させて翌朝終わり。単純な処理でもそれなりに時間掛かる。
C++ Ver. (ConsoleApp)
/////////////////////////////////
// モンティーホール問題 (c)inf102
/////////////////////////////////
#include <iostream>
//////////////////////////////////////////////////////////
// 回数
#define N 10000
// 変更有
#define MODE true
// 変更無
//#define MODE false
//////////////////////////////////////////////////////////
int fnc(bool flg) {
int atari = (int)rand() % 3;
// 3つの箱用意 0,1,2
int box[3];
box[0] = 0;
box[1] = 0;
box[2] = 0;
// 当たりの箱設定 1==当たり
if (atari == 0) box[0] = 1;
if (atari == 1) box[1] = 1;
if (atari == 2) box[2] = 1;
// ユーザが箱選ぶ 0,1,2
int UC = (int)rand() % 3;
// 運営が開けた番号
int UNEI_OPEN = 0;
// 選び直し無し
if (flg == false) {
// 正解
if (atari == UC) {
return 1;
}
// 不正解
else return 0;
}
// 選び直しあり
// 0の箱を最初に選択した場合、
if (UC == 0){
// 客が選んだ0とそれ以外の箱で外れの箱開ける
if (box[1] == 1) UNEI_OPEN = 2;
if (box[2] == 1) UNEI_OPEN = 1;
// 運営が2の箱を開けたら、客は1の箱を選び直したという事になるので1の箱が当たりか調べる
if (UNEI_OPEN == 2) {
// 1の箱が当たりなら 1(当たりを引いたという事をリターン)
if (box[1] == 1) return 1;
else return 0; // はずれなので 0をリターン
}
// 運営が1の箱を開けたら、客は2の箱を選び直したという事になるので2の箱が当たりか調べる
if (UNEI_OPEN == 1) {
if (box[2] == 1) return 1;
else return 0;
}
}
if (UC == 1) {
if (box[0] == 1) UNEI_OPEN = 2;
if (box[2] == 1) UNEI_OPEN = 0;
if (UNEI_OPEN == 2) {
if (box[0] == 1) return 1;
else return 0;
}
if (UNEI_OPEN == 0) {
if (box[2] == 1) return 1;
else return 0;
}
}
if (UC == 2) {
if (box[0] == 1) UNEI_OPEN = 1;
if (box[1] == 1) UNEI_OPEN = 0;
if (UNEI_OPEN == 0) {
if (box[1] == 1) return 1;
else return 0;
}
if (UNEI_OPEN == 1) {
if (box[0] == 1) return 1;
else return 0;
}
}
return (2);
}
void main(void){
srand((unsigned int)time(NULL));
unsigned long hit = 0;
for (long f = 0; f <= N; f++) {
// true == 選び直しあり
if (fnc(MODE)) hit++;
}
std::cout << "END " << hit;
int a;
std::cin >> a;
}
C# ver.
/////////////////////////////////
// モンティーホール問題 (c)inf102
/////////////////////////////////
using System;
using System.Windows;
namespace Mon {
public partial class MainWindow : Window {
///////////////////////////////////////////////
const long N = 100000;
const bool MODE = true; // 選び直し有り
//const bool MODE = false; // 選び直し無し
///////////////////////////////////////////////
static Random rnd;
public MainWindow() {
InitializeComponent();
rnd=new Random();
}
int fnc (bool flg) { // true == 選直し有
// 当たりの番号
int atari = (int)rnd.Next(0, 3); // 0,1,2
var box = new int[3];
box[0] = 0;
box[1] = 0;
box[2] = 0;
// 当たりの箱設定 1==当たり
if (atari == 0) box[0] = 1;
if (atari == 1) box[1] = 1;
if (atari == 2) box[2] = 1;
// ユーザ選び 0,1,2
int UC = (int)rnd.Next(0, 3);
// 運営が開けた番号
int UNEI_OPEN=0;
// 選び直し無し
if ( flg == false){
// 正解
if (atari == UC){
return 1;
}
// 不正解
else return 0;
}
// 選び直しあり
if (UC==0) {
// 運営が外れ箱開ける
if (box[1] == 1) UNEI_OPEN=2;
if (box[2] == 1) UNEI_OPEN=1;
if (UNEI_OPEN == 2) {
if(box[1] == 1) return 1;
else return 0;
}
if (UNEI_OPEN == 1) {
if(box[2] == 1) return 1;
else return 0;
}
}
if (UC==1) {
if (box[0] == 1) UNEI_OPEN=2;
if (box[2] == 1) UNEI_OPEN=0;
if (UNEI_OPEN == 2) {
if(box[0] == 1) return 1;
else return 0;
}
if (UNEI_OPEN == 0) {
if(box[2] == 1) return 1;
else return 0;
}
}
if ( UC==2) {
if (box[0] == 1) UNEI_OPEN=1;
if (box[1] == 1) UNEI_OPEN=0;
if(UNEI_OPEN == 0) {
if(box[1] == 1) return 1;
else return 0;
}
if(UNEI_OPEN == 1) {
if(box[0] == 1) return 1;
else return 0;
}
}
return (2);
}
void msg(string ss) {
MessageBox.Show(ss);
}
private void Button_Click(object sender, RoutedEventArgs e) {
long hit = 0;
for(long f = 0; f <= N; f++) {
// true == 選び直しあり
if (fnc(MODE) == 1) hit++;
}
msg(hit.ToString());
}
}
}