关于“三门”问题的概率

最新编辑2:

又有网友提出,造成这个结果是因为 随机数Random.Next的方法永远拿不到上限值,也就是说 r.Next(0,2) 永远拿不到2。去source上查了一下确实如此,微软这个方法有点坑。

PS:特别感谢 御坂14491 号的提出,以及MatPopLeaSpo、言心亡、日后再说好不好、最萌的伊卡洛斯等朋友的在线对狙。

最后,公布最新的源码和跑的结果,先看结果:

改变选择有66465此中奖,不改变选择有33535此中奖,总数100000,
主持人选对了次数0

代码修改为:

public class Program
{
static Random r = new Random();  // 三门随机数生成器
static Random R = new Random();  // 我的第一次选择随机数生成器
static Random r_host = new Random(); //主持人选择随机数生成器

static bool[] GetDoors() // 随机产生一个门
{
    switch (r.Next(0, 3))
    {
        case 0: return new bool[3] { true, false, false };
        case 1: return new bool[3] { false, true, false };
        default: return new bool[3] { false, false, true };
    }
}

//主持人根据我的第一次选择随机选一个非门
static int GetHostSelection(bool[] door, int my_firstguess)
{
    if (door[my_firstguess]) //如果我第一次猜中了,那么主持人任选一扇门
    {
        var guess = r_host.Next(0, 3);
        while (guess == my_firstguess)
            guess = r_host.Next(0, 3);
        return guess;
    }
    else //如果我第一次猜错了,那么主持人必须选择错误的那扇门
    {
        for (int i = 0; i < 3; i++)
        {
            if (i != my_firstguess && door[i] == false)
                return i;
        }
    }
    return -1;
}

static int ChangeMySelection(bool[] door, int host_guess, int my_firstguess) //改变我的选择
{
    for (int i = 0; i < 3; i++)
        if (i != host_guess && i != my_firstguess)
            return i;
    return -1;
}

static void Main(string[] args)
{
    int right = 0; //改变选择
    int right2 = 0; //不改变选择
    int host_right = 0; //主持人选对了
    for (int i = 0; i < 100000; i++)
    {
        bool[] door = GetDoors(); //随机生成三门
        int my_firstguess = R.Next(0, 3); // 我随机猜一个门
       // 主持人根据我猜的结果随机选择一个非豪车的门
        int host_guess = GetHostSelection(door, my_firstguess);
        //改变我的选择
        int my_finalguess = ChangeMySelection(door, host_guess, my_firstguess);

        if (door[my_finalguess])
            right++;
        else if (door[my_firstguess])
            right2++;
        if (door[host_guess])
            host_right++;
    }
    Console.WriteLine(string.Format("改变选择有{0}此中奖,不改变选择有{1}此中奖,总数{2},主持人选对了次数{3}", right, right2, 100000, host_right));
    Console.ReadKey();
}

最新编辑:

有网友提出,我应该对比改变选择与不改变不选择中奖的比例,因此我把程序改为如下:

public class Program
{
static Random r = new Random();  // 三门随机数生成器
static Random R = new Random();  // 我的第一次选择随机数生成器
static Random r_host = new Random(); //主持人选择随机数生成器

static bool[] GetDoors() // 随机产生一个门
{
    switch (r.Next(0, 2))
    {
        case 0: return new bool[3] { true, false, false };
        case 1: return new bool[3] { false, true, false };
        default: return new bool[3] { false, false, true };
    }
}

static int GetHostSelection(bool[] door, int my_firstguess) //主持人根据我的第一次选择随机选一个非门
{
    if(door[my_firstguess]) //如果我第一次猜中了,那么主持人任选一扇门
    {
        var guess = r_host.Next(0, 2); 
        while(guess== my_firstguess)
            guess = r_host.Next(0, 2);
        return guess;
    }
    else //如果我第一次猜错了,那么主持人必须选择错误的那扇门
    {
        for (int i = 0; i < 3; i++)
        {
            if (i != my_firstguess && door[i] == false)
                return i;
        }
    }
    return -1;
}

static int ChangeMySelection(bool[] door, int host_guess ,int my_firstguess) //改变我的选择
{
    for (int i = 0; i < 3; i++)
        if (i != host_guess && i != my_firstguess)
            return i;
    return -1;
}

static void Main(string[] args)
{
    int right = 0; //改变选择
    int right2 = 0; //不改变选择
    for (int i=0;i<100000;i++)
    {
        bool[] door = GetDoors(); //随机生成三门
        int my_firstguess = R.Next(0, 2); // 我随机猜一个门
        int host_guess = GetHostSelection(door, my_firstguess); // 主持人根据我猜的结果随机选择一个非豪车的门
        int my_finalguess = ChangeMySelection(door, host_guess, my_firstguess); //改变我的选择
        if(door[my_finalguess])
            right++;
        if (door[my_firstguess])
            right2++;
    }

    Console.WriteLine(string.Format("改变选择有{0}此中奖,不改变选择有{1}此中奖,总数{2}", right, right2, 100000));
    Console.ReadKey();
}

运行结果是:

改变选择有50026此中奖,不改变选择有49974此中奖,总数100000

B站有个视频讲三门问题:

一个美国的电视台节目有个游戏:有三扇门,随机两扇里面是羊,有一扇门是豪车。

参赛选手不知晓哪扇门是豪车,他先任意选一扇门。

主持人知道答案,在选手选择第一扇门后,主持人必须从剩下的两扇门中选择是羊的门。

现在参赛选手选择是否换门,因为他手里的可能是羊也可能是豪车。

这个B站的UP主说换门的中奖概率是2/3,我不同意这个观念。我的理由是,参赛选手实际选择门的次数只有一次,而这一次可以选的门是两扇,前面的都是迷惑眼球的文字游戏。

为了验证我的想法,根据大数字规律,我做了一个C#的程序验证之:

public class Program
{
static Random r = new Random();  // 三门随机数生成器
static Random R = new Random();  // 我的第一次选择随机数生成器
static Random r_host = new Random(); //主持人选择随机数生成器
static Random R_final = new Random();

static bool[] GetDoors() // 随机产生一个门
{
    switch (r.Next(0, 2))
    {
        case 0: return new bool[3] { true, false, false };
        case 1: return new bool[3] { false, true, false };
        default: return new bool[3] { false, false, true };
    }
}

static int GetHostSelection(bool[] door, int my_firstguess) //主持人根据我的第一次选择随机选一个非门
{
    if(door[my_firstguess]) //如果我第一次猜中了,那么主持人任选一扇门
    {
        var guess = r_host.Next(0, 2); 
        while(guess== my_firstguess)
            guess = r_host.Next(0, 2);
        return guess;
    }
    else //如果我第一次猜错了,那么主持人必须选择错误的那扇门
    {
        for (int i = 0; i < 3; i++)
        {
            if (i != my_firstguess && door[i] == false)
                return i;
        }
    }
    return -1;
}

static int GetMySelection(bool[] door, int host_guess ,int my_firstguess) //我根据主持人的选择按50%的概率选择是否改变
{
    if (R_final.Next(0, 1) > 0)
    {
        for (int i = 0; i < 3; i++)
            if (i != host_guess && i != my_firstguess)
                return i;
    }
    return my_firstguess;
}

static void Main(string[] args)
{
    int right = 0;
    int wrong = 0;
    for (int i = 0; i < 100000; i++)
    {  
        //随机生成三门
        bool[] door = GetDoors(); 
        // 我随机猜一个门
        int my_firstguess = R.Next(0, 2); 
        // 主持人根据我猜的结果随机选择一个非豪车的门
        int host_guess = GetHostSelection(door, my_firstguess); 
        //我的最终选择
        int my_finalguess = GetMySelection(door, host_guess, my_firstguess); 

        if (door[my_finalguess])
        {
            right++;
        }
        else
        {
            wrong++;
        }
    }
    Console.WriteLine(string.Format("有{0}此中奖,有{1}此未中奖,总数{2}", right, wrong, 100000));
    Console.ReadKey();
}

运行答案是:

有49925此中奖,有50075此未中奖,总数100000

结果不言自明。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

19 − 1 =