三门问题
简介
三门问题(Monty Hall Problem)是一个著名的概率论问题,它源于美国的一档电视游戏节目。问题的描述如下:
有三扇门,其中一扇门后面有一辆汽车,另外两扇门后面则是山羊。
参赛者选择一扇门,希望能赢得汽车。
主持人(Monty Hall)知道每扇门后面的奖品,他会打开剩下的两扇门中的一扇,露出一只山羊。
此时,主持人会给参赛者一个选择:坚持原来的选择,还是换到另一扇未打开的门。
问题是:参赛者应该坚持原来的选择,还是换到另一扇门,或者说两种策略都无所谓?
反直觉之处
根据概率论,参赛者更换门的策略胜率更高。
更换门的策略胜率为2/3
而坚持原来选择的策略胜率为1/3。
如果此时另一位参赛者入场,在这两个门中选其中一个,两个门的概率是1/2。
这个结论可能违反直觉,但可以通过条件概率和贝叶斯定理来解释。
当主持人打开一扇门后,参赛者的信息发生了变化。原本,每扇门背后有汽车的概率都是1/3。但在主持人打开一扇门后,剩下的两扇门中,一扇门背后有汽车的概率变为了1/2。然而,由于主持人总是打开一扇有山羊的门,这意味着如果参赛者最初选择了山羊(2/3的概率),那么更换门后就会赢得汽车;而如果参赛者最初选择了汽车(1/3的概率),更换门后就会得到山羊。因此,更换门的策略胜率为2/3。
程序分析
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 1000 //测试次数
int main()
{
srand(time(NULL)); //设置随机数种子
int ball = 0; //汽车所在的门的编号
int choice = 0; //参赛者选择的门的编号
int open = 0; //主持人打开的门的编号
int switch_win = 0; //更换门后赢得汽车的次数
int stay_win = 0; //保持原选择后赢得汽车的次数
for (int i = 0; i < N; i++) //循环测试N次
{
ball = rand() % 3 + 1; //随机生成汽车所在的门编号,1到3之间
choice = rand() % 3 + 1; //随机生成参赛者选择的门编号,1到3之间
open = rand() % 3 + 1;
do //确保主持人打开的不是参赛者选择,也不是有车的门。
{
open = rand() % 3 + 1; //随机生成主持人打开的门编号,1到3之间
} while (open == ball || open == choice); //
if (choice == ball) //如果参赛者选择的门就是汽车所在的门
{
stay_win++; //保持原选择后赢得汽车的次数加一
}
else //如果参赛者选择的门不是汽车所在的门
{
switch_win++; //更换门后赢得汽车的次数加一
}
}
printf("更换门后赢得汽车的次数:%d\n", switch_win); //输出更换门后赢得汽车的次数
printf("保持原选择后赢得汽车的次数:%d\n", stay_win); //输出保持原选择后赢得汽车的次数
return 0;
}
输出:
更换门后赢得汽车的次数:662
保持原选择后赢得汽车的次数:338
个人分析
3个门感受还是不太直观,假设:
有100个杯子,其中一个有球
参赛者选择其中一个杯子
主持人打开其余的98个空杯子,只剩下最后两个杯子,其中一个有球。
此时保持原来选择的中奖率为1/100
而改变选择的中奖率为99/100
循环1000次结果如下:
分析验证
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 1000 //测试次数
#define M 100 //杯子数量
int main()
{
srand(time(NULL)); //设置随机数种子
int ball = 0; //小球所在的杯子编号
int choice = 0; //参赛者选择的杯子编号
int open = 0; //主持人打开的杯子编号
int switch_win = 0; //更换杯子后赢得小球的次数
int stay_win = 0; //保持原选择后赢得小球的次数
for (int i = 0; i < N; i++) //循环测试N次
{
ball = rand() % M + 1; //随机生成小球所在的杯子编号,1到M之间
choice = rand() % M + 1; //随机生成参赛者选择的杯子编号,1到M之间
for (int j = 0; j < M - 2; j++) //主持人打开M - 2个空杯子
{
do
{
open = rand() % M + 1; //随机生成主持人打开的杯子编号,1到M之间
} while (open == ball || open == choice); //确保主持人打开的杯子既不是小球所在的杯子,也不是参赛者选择的杯子
}
if (choice == ball) //如果参赛者选择的杯子就是小球所在的杯子
{
stay_win++; //保持原选择后赢得小球的次数加一
}
else //如果参赛者选择的杯子不是小球所在的杯子
{
switch_win++; //更换杯子后赢得小球的次数加一
}
}
printf("更换杯子后赢得小球的次数:%d\n", switch_win); //输出更换杯子后赢得小球的次数
printf("保持原选择后赢得小球的次数:%d\n", stay_win); //输出保持原选择后赢得小球的次数
return 0;
}
输出
更换杯子后赢得小球的次数:991
保持原选择后赢得小球的次数:9
空空如也!