弄懂“三门问题”,成功概率翻倍,来用代码验证一下

开发 前端
看到一段关于“三门问题”的视频,第一感觉就是视频的结论有误。本想一笑了之,但看了评论,迷惑了:三门问题的答案到底是什么?

[[432338]]

看到一段关于“三门问题”的视频,第一感觉就是视频的结论有误。本想一笑了之,但看了评论,迷惑了:三门问题的答案到底是什么?

作为勤学好问的码农,不知道最终答案,还是很难受的,于是深入研究一下,发现”小丑竟然是自己“。如果你想挑战一下自己,可以先跳过推理和结论部分,自己先得出一个答案,然后再看看是否正确。

一条朋友圈

在花了一个小时,弄懂三门问题之后,发了一条这样的朋友圈:

三门问题:有三扇门,其中一扇后面是汽车,另外两扇是山羊。当你选择一扇门后,主持人从另外两扇门中打开一扇有山羊的。那么,此时换门是否会增加获得汽车的概率?

第一次错:直觉,换与不换都是1/2的概率;差点止步于此,得出结论:都是骗人的。

第二次错:列举,(选1,去2,换)、(选2,去1,换)、(选3,去1,不换)、(选3、去2、不换),看似概率依旧是1/2。但这里犯了一个错误,没引入首选的概率,也就是后两种情况不能按1/4算,只能按1/6算。

第三次引入概率:1/3(选1,去2,换)、1/3(选2,去1,换)、1/6(1/3 * 1/2)(选3,去1,不换)、1/6(1/3 * 1/2)(选3、去2、不换),后两项合计只有1/3概率。

所以,三门问题的答案是:选择换。概率会从原来的1/3,变成2/3;

通过这个问题在想:有时候,坚持可能是错的,可能是主观判断,可能环境已经发生了变化;但有时候又要坚持,要坚持对答案的怀疑,不断寻找答案。

如果从底层逻辑来说就是:坚持动态的看待问题。也就是:士别三日当刮目相待。

发完这条朋友圈,感觉这个问题有必要通过程序实现一下,同时写篇文章分享出来,于是就有了这篇文章。

如果上面的分析没看懂,也没关系,下面就结合代码再分析实践一下。

三门问题

三门问题出自美国的电视游戏节目Let's Make a Deal,问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)。

问题场景:

参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门是否会增加参赛者赢得汽车的机率。

据说,90%的人都选择了不换。你的选择是什么呢?

概率分析

先看下图,存在汽车、山羊1、山羊2,三个门:

三扇门的概率

选手选择三个门的概率都是三分之一,下面进行具体的假设:

  • 假设选手选择了山羊1,那么主持人打的门只能是山羊2,因为有汽车的门是不能打开的。这种情况发生的概率为:1/3(选手选择山羊1的概率)* 1(主持人的选择是确定的) = 1/3;此时如果换,则赢得汽车;
  • 假设选手选择了山羊2,那么主持人打的门只能是山羊1,因为有汽车的门是不能打开的。这种情况发生的概率为:1/3(选手选择山羊2的概率)* 1(主持人的选择是确定的) = 1/3;此时如果换,则赢得汽车;
  • 假设选手选择了汽车,那么主持人有两种打开选择:山羊1和山羊2。主持人选择山羊1的概率:1/3(选手选择汽车的概率)* 1/2(主持人二选一) = 1/6;主持人选择山羊2的概率:1/3(选手选择汽车的概率)* 1/2(主持人二选一) = 1/6;所以,当选手选择了汽车的门时,发生的概率为:1/3 * 1/2 + 1/3 * 1/2 = 1/3。此时如果不换,则赢得汽车;

很显然,三种情况发生的概率都为三分之一,换之后赢得汽车的概率是不换的2倍。也就是说:换之后,赢得汽车的概率变成了2/3。

程序演示

上面做了理论分析,下面写一段代码,来验证一下:

  1. public class ThreeDoors { 
  2.  
  3.  /** 
  4.   * 随机选择器 
  5.   */ 
  6.  private static final Random RANDOM = new Random(); 
  7.  
  8.  /** 
  9.   * 成功总次数 
  10.   */ 
  11.  private static int SUCCESS_COUNT = 0; 
  12.  
  13.  /** 
  14.   * 重复执行10w次 
  15.   */ 
  16.  private static final int PLAY_TIMES = 100000; 
  17.  
  18.  public static void main(String[] args) { 
  19.  
  20.   // 执行游戏10w次 
  21.   for (int i = 0; i < PLAY_TIMES; i++) { 
  22.    playGame(); 
  23.   } 
  24.  
  25.   // 计算选择"换"的概率 
  26.   BigDecimal yield = new BigDecimal(SUCCESS_COUNT) 
  27.     .divide(new BigDecimal(PLAY_TIMES), 4, RoundingMode.HALF_UP) 
  28.     .multiply(new BigDecimal(100)); 
  29.   System.out.println("执行" + PLAY_TIMES + "次实验,选择【交换】的概率为:" + yield + "%"); 
  30.  } 
  31.  
  32.  public static void playGame() { 
  33.  
  34.   // 初始化三扇门,默认为false,都没有车 
  35.   boolean door1 = false, door2 = false, door3 = false
  36.  
  37.   // 选手选择的门是否为汽车,true:是 
  38.   boolean pickedDoor; 
  39.   // 最后剩下的门是否为汽车,true:是 
  40.   boolean leftDoor; 
  41.  
  42.   // 第一步:随机选择一扇门,放入汽车 
  43.   switch (pickDoor(3)) { 
  44.    case 1: 
  45.     door1 = true
  46.     break; 
  47.    case 2: 
  48.     door2 = true
  49.     break; 
  50.    case 3: 
  51.     door3 = true
  52.     break; 
  53.    default
  54.     System.out.println("异常数值"); 
  55.     break; 
  56.   } 
  57.  
  58.   // 第二步:选手选择一扇门,依旧采用上面选门的算法 
  59.   int playerPickedDoor = pickDoor(3); 
  60.  
  61.   // 第三步:主持人移除一扇有山羊的门 
  62.   // 其中主持人只能二选一,移除1一扇门,相当于选择了另一扇门 
  63.   if (playerPickedDoor == 1) { 
  64.    // 选手选择门1 
  65.    pickedDoor = door1; 
  66.    // 如果门2有车,则只能移除门3 
  67.    if (door2) { 
  68.     leftDoor = door2; 
  69.    } else if (door3) { 
  70.     // 如果门3有车,则只能移除门2 
  71.     leftDoor = door3; 
  72.    } else { 
  73.     // 两个门都没车,随机二选一 
  74.     if (pickDoor(2) == 1) { 
  75.      leftDoor = door2; 
  76.     } else { 
  77.      leftDoor = door3; 
  78.     } 
  79.    } 
  80.   } else if (playerPickedDoor == 2) { 
  81.    // 选手选择门2 
  82.    pickedDoor = door2; 
  83.    // 如果门1有车,则只能移除门3 
  84.    if (door1) { 
  85.     leftDoor = door1; 
  86.    } else if (door3) { 
  87.     // 如果门3有车,则只能移除门1 
  88.     leftDoor = door3; 
  89.    } else { 
  90.     // 两个门都没车,随机二选一 
  91.     if (pickDoor(2) == 1) { 
  92.      leftDoor = door1; 
  93.     } else { 
  94.      leftDoor = door3; 
  95.     } 
  96.    } 
  97.   } else { 
  98.    // 选手选择门3 
  99.    pickedDoor = door3; 
  100.    // 如果门1有车,则只能移除门2 
  101.    if (door1) { 
  102.     leftDoor = door1; 
  103.    } else if (door2) { 
  104.     // 如果门2有车,则只能移除门1 
  105.     leftDoor = door2; 
  106.    } else { 
  107.     // 两个门都没车,随机二选一 
  108.     if (pickDoor(2) == 1) { 
  109.      leftDoor = door1; 
  110.     } else { 
  111.      leftDoor = door2; 
  112.     } 
  113.    } 
  114.   } 
  115.  
  116.   // 第四步:上述结果一定的情况,选手选择更换门 
  117.   pickedDoor = leftDoor; 
  118.  
  119.   // 第五步:判断该门是否有车 
  120.   if (pickedDoor) { 
  121.    SUCCESS_COUNT++; 
  122.   } 
  123.  } 
  124.   
  125.  /** 
  126.   * 随机选择一个门 
  127.   */ 
  128.  public static int pickDoor(int bound) { 
  129.   return RANDOM.nextInt(bound) + 1; 
  130.  } 

上述实现方法,暂且未考虑算法优化,只是简单情况判断处理。

上述实现分以下几步:

  • 第一步:随机选择一扇门,放入汽车,这里采用Random随机数,如果对应的门后为车,则对应的值设置为true;
  • 第二步:选手选择一扇门,算法依旧采用Random随机数;
  • 第三步:在选手选择一扇门的前提下,主持人移除一扇没有汽车的门。这里并未处理移除的门,而是记录了移除之后剩下的那扇门的值。如果两扇门都没有车,则随机二选一。
  • 第四步:选手选择交换,即选手选择的门变成了剩下的那扇门。
  • 第五步:开门,验证,如果成功记录一次;
  • 第六步:执行10w次之后,计算百分比;

最终打印日志如下:

  1. 执行100000次实验,选择【交换】的概率为:66.7500% 

多执行几次,会发现几乎都在66%-67%之间,说明选择【换】,的确可以让成功的概率翻倍。

小结

最后,回顾一下整个过程:无意看到一条讲”三门问题“的视频,先是做出了直观判断(错误的),对别人的结论嗤之以鼻,然后发现许、异议。于是,开始寻求佐证,最终得到了正确的答案。

正像在朋友圈中说的:有时候,坚持可能是错的,可能是因为主观判断,也可能是因为环境已经发生了变化;但有时候又要坚持,要坚持对答案的怀疑,对答案的不断追寻。

这也应该是我们做事的底层逻辑,不能单靠【感觉】来判断,更多的要采用事实作为依据。特别是程序员,我们还可以用程序来解决类似的问题。

 

同时,你是否发现,用程序来解决生活中的一些问题,不也是很有意思的吗?

 

责任编辑:武晓燕 来源: 程序新视界
相关推荐

2021-08-18 06:48:52

编程语言开发IT

2021-08-18 10:44:18

编程语言FlutterUnity

2012-04-08 14:09:50

小米

2017-05-26 08:42:28

PHPPython语言

2021-04-21 14:19:52

javaignalHandle接口

2020-07-30 08:27:33

Javascript闭包变量

2021-10-15 22:19:15

电脑蓝屏重启

2023-02-04 20:08:24

前端开发

2021-03-15 09:44:39

Broker源码RocketMQ

2022-08-24 08:07:11

MyBatisSQLMySQL

2022-03-24 13:36:18

Java悲观锁乐观锁

2010-03-17 17:33:47

云计算

2020-09-26 21:23:26

程序员代码编程

2021-12-27 18:00:30

对象数组Java

2009-11-17 11:14:25

Oracle扩展

2020-12-10 08:44:35

WebSocket轮询Comet

2021-12-18 20:46:38

亚马逊云科技阿里云IaaS

2022-02-23 08:55:06

数据迁移分库分表数据库

2011-08-05 09:33:56

OracleUser ProcesServer Proc

2010-09-26 08:50:10

ERP软件询问
点赞
收藏

51CTO技术栈公众号