三门问题

三门问题概述

    三门问题(Monty Hall problem)亦称为蒙提霍尔问题、蒙特霍问题或蒙提霍尔悖论,大致出自美国的电视游戏节目Let’s Make a Deal。问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)。参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门是否会增加参赛者赢得汽车的机率。如果严格按照上述的条件,那么答案是会。不换门的话,赢得汽车的几率是1/3。换门的话,赢得汽车的几率是2/3。

解决方法:蒙特·卡罗方法

蒙特·卡罗方法(Monte Carlo method),也称统计模拟方法,是二十世纪四十年代中期由于科学技术的发展和电子计算机的发明,而被提出的一种以概率统计理论为指导的一类非常重要的数值计算方法。当所求解问题是某种随机事件出现的概率,或者是某个随机变量的期望值时,通过某种“实验”的方法,以这种事件出现的频率估计这一随机事件的概率,或者得到这个随机变量的某些数字特征,并将其作为问题的解。

也就是,用多次随机试验的事件出现频率来近似作为事件发生的概率。

代码实现

package 三门问题;
import java.util.Scanner;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
//三门
class ThreeDoor{
    String[] threeDoor = new String[3];
    //设置门后的奖品
    public void setPrize() {
        int carPosition = (int )(Math.random()*10)%3;
        threeDoor[0] = "Goat";
        threeDoor[1] = "Goat";
        threeDoor[2] = "Goat";
        threeDoor[carPosition] = "Car";
    }
}
class Player{
    int firstChoicePosition = (int )(Math.random()*10)%3;
    int lastChoicePosition = firstChoicePosition;
    //选择一个门
    public void choiceDoor() {
        irstChoicePosition = (int)(Math.random()*100)%3;
        lastChoicePosition = firstChoicePosition;
    }
    //更换为另一个门
    public void changeDoor(int firstGoatPosition) {
        lastChoicePosition = 3 - firstChoicePosition - firstGoatPosition;
    }
}
//主持人
class Presenter{
    int firstGoatPosition = 0;//主持人打开的门
    //打开后面为山羊的门
    public void openFirstGoatPosition(String[] threeDoor,int playFirstChoicePosition) {
        if (threeDoor[playFirstChoicePosition].equals("Car")) {
            do {
                firstGoatPosition=(int )(Math.random()*10)%3;
            } while (firstGoatPosition==playFirstChoicePosition);
        }
        else {
            for(int i=0;i<3;i++)
                if (!threeDoor[i].equals("Car")&&i!=playFirstChoicePosition) {
                    firstGoatPosition=i;
            }
        }
    }
}
//计分牌
class Scorer{
    int playCount = 0;//玩的总次数
    int choiceCarCount = 0;//获得车的次数
    int choiceGoatCount = 0;//获得山羊的次数
    //计分
    public void score(String[] threeDoor,int playLastChoicePosition) {
        playCount++;
        if (threeDoor[playLastChoicePosition].equals("Car")) {
        choiceCarCount++;
        }
        else {
        choiceGoatCount++;
        }
    }
    //获得车的概率
    public void statistics() {
        DecimalFormat df=new DecimalFormat("##.00%");
        System.out.println("Choice Goat Count: " + choiceGoatCount);
        System.out.println("Choice Car  Count: " + choiceCarCount);
        System.out.println("Choice Car  Rate : " + df.format((float)choiceCarCount/playCount));
    }
}
//设置模式
class Moder{
    String mode = "";//模式A:换门 B:不换门
    int playTotalCount = 0;//玩的总次数
    //设置模式
    public void setMode() {
        Scanner sc = new Scanner(System.in);
        //设置选门模式
        while (!mode.equals("A") && !mode.equals("B")) {
            System.out.println("Plase input mode: A[Change]  B[Don't Change]");
            mode = sc.nextLine();
            if(!mode.equals("A") && !mode.equals("B"))
                System.out.println("Input Error, Input again.");
        }
        //设置玩的总次数
        while (playTotalCount<=0) {
            System.out.println("Plase input play total count: ");
            try {
                Scanner scCnt = new Scanner(System.in);
                playTotalCount = scCnt.nextInt();
            } catch (Exception e) {
                playTotalCount=0;
            }
            if(playTotalCount<=0)
                System.out.println("Input Error, Input again.");
        }
        System.out.println();
    }
    public void showMode() {
        if(mode.equals("A"))
            System.out.println("Mode: [Change]");
        else
            System.out.println("Mode: [Don't Change]");
        System.out.println("Play Total Count: " + playTotalCount);
        System.out.println();
    }
}
//测试
public class Test {
    public static void main(String[] args) {
        ThreeDoor threeDoor = new ThreeDoor();  //三门
        Player player = new Player();           //玩家
        Presenter presenter = new Presenter();  //主持人
        Scorer scorer = new Scorer();           //计分者
        Moder moder = new Moder();              //设置模式者
        moder.setMode();  //设置模式
        moder.showMode(); //显示模式
        //循环玩多次
        for(int i=0; i<moder.playTotalCount; i++) {
            threeDoor.setPrize(); //设置门后奖品
            player.choiceDoor();  //玩家选择一个门
            //主持人打开一扇是山羊的门
            presenter.openFirstGoatPosition(threeDoor.threeDoor,player.firstChoicePosition);
            if(moder.mode.equals("A"))
                player.changeDoor(presenter.firstGoatPosition); //玩家换另外一扇门
            //计分
            scorer.score(threeDoor.threeDoor,player.lastChoicePosition);
        }
        scorer.statistics();  //统计获得汽车的概率
    }
}

实验结果

换门结果:

Plase input mode: A[Change]  B[Don't Change]
A
Plase input play total count: 
100000
Mode: [Change]
Play Total Count: 100000
Choice Goat Count: 33289
Choice Car  Count: 66711
Choice Car  Rate : 66.71%

不换门结果:

Plase input mode: A[Change]  B[Don't Change]
B
Plase input play total count: 
100000
Mode: [Don't Change]
Play Total Count: 100000
Choice Goat Count: 66489
Choice Car  Count: 33511
Choice Car  Rate : 33.51%

总结

在游戏开始的时候,1,2,3号门中奖的概率是相等的,都是1/3

P(1号门中奖)+P(2号门中奖)+P(3号门中奖)=1

当玩家选择了1号门时,玩家中奖的概率即为1/3

P(1号门中奖)=1/3

那剩下的2,3号门的中奖概率就为2/3

P(2号门中奖)+P(3号门中奖)=2/3

此时,主持人将2号和3号门中没有奖的2号门打开了,并确认地告诉你这个门没有奖,则2号门中奖的概率为0,那3号门中奖的概率为2/3,即为玩家选择的1号门的两倍

P(2号门中奖)=0=>P(3号门中奖)=2/3

所以当玩家换3号门时中奖概率为2/3,不换门时中奖概率为1/3