モンティホール問題

<font color="blue"><B>数学史上最も議論を巻き起こした<a href="https://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%B3%E3%83%86%E3%82%A3%E3%83%BB%E3%83%9B%E3%83%BC%E3%83%AB%E5%95%8F%E9%A1%8C">モンティーホール</A> 問題とは、</B>

:::note info

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++
/////////////////////////////////
// モンティーホール問題 (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#
/////////////////////////////////
// モンティーホール問題 (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());
        }
    }
}



```