モンティホールパラドックス

昨日、行動ファイナンスの本を読んでて、出てきたクイズ。
詳細は以下のとおりです。
http://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


どうにも納得できず、プログラムを作って確認してみた。
しかも人のプログラムだと落とし穴があるかもということで、自分で新たに作って確認してみた。
したらば、バグってて(①を忘れてた)、50%の確率でびびったが、直すとたしかに66%ぐらいだった。


恐るべし大数の法則、プログラムって便利だな。


しかし、それでもなお、納得いかず、30分ほどは説明を読み考えた。「人が持つ確率に対する直感」というのが、いかに間違っているか、というのが、よくわかった気がした。


あと、この問題が間違いやすいのは、確率の問題もあるが、状況(設定)の誤解もあるように感じた。この問題の場合、司会者(モンティさん)は味方であるが、敵であるように思いやすい。モンティさんが敵ならば、「プレイヤーが当たりのドアを選んでいるから、ハズレさせようとしている」と考えるのが普通だと思う。例えば、②のようなコードを追加すると、勝率は0%に落ちる(逆にドアを変えない場合33%)。②は、モンティさんが、プレイヤーの味方でなく完全な敵の場合のコードで、「最初にプレイヤーがハズレドアを開いたときに、負けにされちゃう」という場合です。また、最初のバグプログラムの場合(①がない場合)は、ドアを変えても変えなくても勝率50%です。①がない場合というのは、「モンティさんが最初開けるドアは、どちらかのハズレドアを開くだけで、自分が選んだハズレドアを開くこともある」という場合です。この場合、モンティさんは敵でも味方でもなく、中立といったところでしょうか。


というわけで、「ドアを変えない(or変えても変えなくても同じ)という直感」自体は間違っているわけでなく、「モンティーさんは味方なのに敵(or中立)と思っちゃう」状況認識が間違いなのかもなーと、思った。

import java.security.SecureRandom;

public class Game {
        
    private static int gameCount;
    private static int winCount;
    private static int loseCount;

    private SecureRandom rand = new SecureRandom();

    public void doGame(){

        int chooseDoor = rand.nextInt(3);
        int priseDoor = rand.nextInt(3);

        //②ハズレの場合は、負けだったらば
//        if(chooseDoor != priseDoor){
//            loseCount++;
//            gameCount++;
//            return;
//        }
        
        //司会者が当たり以外のドアを開く
        //当たりドア、自分が選んだドア以外のドア
        int openDoor;
        while(true){
            openDoor = rand.nextInt(3);
            if(openDoor != priseDoor && openDoor != chooseDoor){//①
                break;
            }
        }

        //ドアを変える
        //先に選んだドア、開けたドアでないドア
        for(int i=0; i<3; i++){
            if(i != openDoor && i != chooseDoor){
                chooseDoor = i;
                break;
            }
        }
        
        if(chooseDoor == priseDoor){
            winCount++;
        }else{
            loseCount++;
        }
        
        gameCount++;
    }
    
    
    public static void main(String[] args) {
        Game game = new Game();
        while(gameCount < 10000){
            game.doGame();
        }
        
        System.out.println("win=" + winCount + "(" + ((float)winCount / gameCount)+ ")");
        System.out.println("lose=" + loseCount + "(" + ((float)loseCount / gameCount)+ ")");

    }
    
}