LoginSignup
1
0

[その他] モンティホール問題

Posted at

数学史上最も議論を巻き起こしたモンティーホール 問題とは、

  1. 箱が3つあり、1つが当たり。 客は1つ選ぶ。
  2. 1.で箱を開けないで、運営が他2つの箱を見て当たりではない箱を開ける。
  3. 当然、箱が2つになり、どちらかに当たりが入っている。
  4. 始めに選んだ箱をそのまま開けるか、又はもう一方の箱にするか。
  5. その場合の当たりの出る確率はどうなるか。
    答えは選び直しの方が確立が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());
        }
    }
}



1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0