买彩票能中大奖?用Java盘点常见的概率悖论 | 京东云技术团队

引言

《双色球头奖概率与被雷劈中的概率哪个高?》

《3人轮流射击,枪法最差的反而更容易活下来?》

让我们用Java来探索ta们!

悖论1:著名的三门问题

**规则描述:**你正在参加一个游戏节目,你被要求在三扇门中选择一扇:其中一扇后面有一辆车;其余两扇后面则是山羊。你选择了一道门,假设是一号门,然后知道门后面有什么的主持人,开启了另一扇后面有山羊的门,假设是三号门。他然后问你:“你想选择二号门吗?请问若想获得车,参赛者应该换二号门吗?

**论证:**分析需求,拆解为如下代码

1/** 2 * <p> 三门问题解决方案 </p> 3 * @author yuanfeng.wang 4 * @since 2023/8/29 5 */ 6import java.util.Random; 7 8public class ThreeDoorSolution { 9 10 public static void main(String[] args) { 11 // 模拟执行1万次,打印获胜的概率 12 threeDoor(10000); 13 } 14 15 /** 16 * 三门问题逻辑拆解 17 * @param numSimulations 总共执行多少轮游戏 18 */ 19 private static void threeDoor(int numSimulations) { 20 int switchWins = 0; 21 int stayWins = 0; 22 23 Random random = new Random(); 24 for (int i = 0; i < numSimulations; i++) { 25 // 随机确定车所在的门 26 int carDoor = random.nextInt(3); 27 28 // 玩家随机选择一扇门 29 int playerChoice = random.nextInt(3); 30 31 // 主持人随机打开一扇门:要求该门不是玩家选择的,且必须是羊 32 int openedDoor; 33 do { 34 openedDoor = random.nextInt(3); 35 } while (openedDoor == carDoor || openedDoor == playerChoice); 36 37 // 换门后的选择:不能是打开的门,不能是玩家选择的门,则是交换之后的门 38 int finalChoice; 39 do { 40 finalChoice = random.nextInt(3); 41 } while (finalChoice == playerChoice || finalChoice == openedDoor); 42 43 // 计算是否换门获胜 44 if (finalChoice == carDoor) { 45 switchWins++; 46 } 47 48 // 计算不换门获胜 49 if (playerChoice == carDoor) { 50 stayWins++; 51 } 52 } 53 54 // 输出结果 55 System.out.println("在 " + numSimulations + " 次模拟中:"); 56 System.out.println("换门获胜的概率:" + (double) switchWins / numSimulations); 57 System.out.println("不换门获胜的概率:" + (double) stayWins / numSimulations); 58 } 59} 60// 模拟运行,打印结果如下 61// 在 10000 次模拟中: 62// 换门获胜的概率:0.6679 63// 不换门获胜的概率:0.3321 64

结论:三门问题看似一道简单的概率题,几十年来却一直引发巨大争议,持两种不同观点的人基本是五五开;事实上始终选择换门的玩家,获胜的概率2/3,而保持原方案的胜率只有1/3

悖论2:双色球我能中大奖

**规则描述:**从1-33个红色球中随机选出6个,再从1-16个蓝色球中随机选择1个,最终开奖出一注 6+1组合球,无顺序要求;

  • 一等奖:中6红 + 1蓝
  • 二等奖:中6红
  • 三等奖:中5红 + 1蓝
  • 四等奖:中4红 + 1蓝,或只中5个红
  • 五等奖:中3红 + 1蓝,或只中4个红
  • 六等奖:中1蓝

**论证:**分析玩法,计算一等奖中奖率,从33个红球样本中选择6个,计算总共的组合数,即数学公式C(n, m) = n!/((n-m)! * m!),代入计算C(33, 6) = 33!/((33-6)! * 6!) = 1107568,再乘以16,最终得出一等奖获奖概率1/17721088。

分析规则,以下代码展示了开奖一次,购买N注时,打印中奖信息的程序,当代入N=500万时,多次执行,可以很轻松打印出一等奖

1 2import java.util.*; 3 4/** 5 * <p>双色球随机模拟</p> 6 * @author yuanfeng.wang 7 * @since 2023/8/29 8 */ 9public class SsqSolution { 10 11 private static Random random = new Random(); 12 13 /** 14 * 开奖的红球 15 */ 16 private static Set<Integer> winningRedBalls; 17 18 /** 19 * 开奖的蓝球 20 */ 21 private static int winningBlueBall; 22 23 // 静态块初始化一组开奖号码 24 static { 25 // 篮球 01-16 26 winningBlueBall = random.nextInt(16) + 1; 27 28 // 红球 01-33生成6个 29 winningRedBalls = new HashSet<>(); 30 while (winningRedBalls.size() < 6) { 31 int num = random.nextInt(33) + 1; 32 winningRedBalls.add(num); 33 } 34 } 35 36 public static void main(String[] args) { 37 play(500_0000); 38 } 39 40 /** 41 * 42 * @param num 运行一次程序只开一次奖,此参数表示总共购买多少注 43 */ 44 public static void play(int num) { 45 System.out.println("\n本期开奖号码:"); 46 System.out.println("红球:" + winningRedBalls + " 篮球:" + winningBlueBall); 47 for (int i = 0; i < num; i++) { 48 playOnce(); 49 } 50 } 51 52 private static void playOnce() { 53 Set<Integer> userRedBalls = getUserSelectedRedBalls(); 54 int userBlueBall = getUserSelectedBlueBall(); 55 56 int redBallMatch = countMatchingBalls(userRedBalls, winningRedBalls); 57 boolean blueBallMatch = (userBlueBall == winningBlueBall); 58 59 if (redBallMatch == 6 && blueBallMatch) { 60 System.out.println("\n恭喜你中了一等奖!"); 61 System.out.println("玩家购买的号码:"); 62 System.out.println("红球:" + userRedBalls + " 蓝球:" + userBlueBall); 63 } else if (redBallMatch == 6) { 64 System.out.println("\n恭喜你中了二等奖!"); 65 } else if (redBallMatch == 5 && blueBallMatch) { 66// System.out.println("\n恭喜你中了三等奖!"); 67 } else if (redBallMatch == 5 || (redBallMatch == 4 && blueBallMatch)) { 68// System.out.println("\n恭喜你中了四等奖!"); 69 } else if (redBallMatch == 4 || (redBallMatch == 3 && blueBallMatch)) { 70// System.out.println("\n恭喜你中了五等奖!"); 71 } else if (blueBallMatch) { 72// System.out.println("\n恭喜你中了最小奖!"); 73 } else { 74 //没中奖,不打印记录 75 } 76 } 77 78 /** 79 * 返回玩家选择的6个红球,范围1-33,不重复 80 */ 81 private static Set<Integer> getUserSelectedRedBalls() { 82 Set<Integer> userRedBalls = new HashSet<>(); 83 while (userRedBalls.size() < 6) { 84 int num = random.nextInt(33) + 1; 85 userRedBalls.add(num); 86 } 87 return userRedBalls; 88 } 89 90 /** 91 * 玩家选择的1个蓝球,范围1-16 92 */ 93 private static int getUserSelectedBlueBall() { 94 return random.nextInt(16) + 1; 95 } 96 97 /** 98 * 匹配中了几个红球 99 * @return 中红球个数 100 */ 101 private static int countMatchingBalls(Set<Integer> userBalls, Set<Integer> winningBalls) { 102 int count = 0; 103 for (int ball : userBalls) { 104 if (winningBalls.contains(ball)) { 105 count++; 106 } 107 } 108 return count; 109 } 110 111} 112

**结论:**排除其它因素,头奖概率约1700万分之1,这个结论并不直观,例举如下几个进行对比

1.一家祖孙三代人的生日都在同一天的概率约为27万分之一

2.小行星撞击地球的概率保守推测是200万分之一

3.生出全男或全女四胞胎的概率约为352万分之一

悖论3:三个枪手

**描述:**三个小伙子同时爱上了一个姑娘,为了决定他们谁能娶这个姑娘,他们决定用枪进行一次决斗。A的命中率是30%,B比他好些,命中率是50%,最出色的枪手是C,他从不失误,命中率是100%。由于这个显而易见的事实,为公平起见,他们决定按这样的顺序:A先开枪,B第二,C最后。然后这样循环,直到他们只剩下一个人。那么A第一枪应该怎么打?谁活下来的概率最大?

**论证:**每个人的目标都是活下来,为了目标寻找最好的策略。以下开始分人讨论

A:

  • 若A开枪射杀了B,则下个开枪是C,C会100%射杀A,这不是一个好策略
  • 若A开枪射杀了C,则下一轮B会有50%的几率杀掉自己
  • 若A开枪未打中,则下一轮可以坐山观虎斗,所以A最好的策略看似是故意打空枪更好一些

B:

  • 若A已经将C射杀,此时B与A互相射击,B的生存率高于A
  • B只能选择射杀C,因为只要C活着,都会优先射杀B

C:

  • 先消除威胁大的B,然后再杀掉A,只要自己有开2枪的机会,直接获胜

结论:需求太复杂,暂未实现生存概率计算😭,欢迎补充悖论3的代码论证过程

作者:京东保险 王苑沣

来源:京东云开发者社区 转载请注明来源

点赞
收藏

评论区

加载中...

相关推荐

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

mysql中like用法

like的通配符有两种%(百分号):代表零个、一个或者多个字符。\(下划线):代表一个数字或者字符。1\.name以"李"开头wherenamelike'李%'2\.name中包含"云",“云”可以在任何位置wherenamelike'%云%'3\.第二个和第三个字符是0的值wheresalarylike'\00%'4\

MXNET:丢弃法

除了前面介绍的权重衰减以外,深度学习模型常常使用丢弃法(dropout)来应对过拟合问题。方法与原理为了确保测试模型的确定性,丢弃法的使用只发生在训练模型时,并非测试模型时。当神经网络中的某一层使用丢弃法时,该层的神经元将有一定概率被丢弃掉。设丢弃概率为$p$。具体来说,该层任一神经元在应用激活函数后,有$p$的概率自乘0,有

FLV文件格式

1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  

买彩票能中大奖?用Java盘点常见的概率悖论 | 京东云技术团队 - HelloWorld