关于java:买彩票能中大奖用Java盘点常见的概率悖论-京东云技术团队

35次阅读

共计 4281 个字符,预计需要花费 11 分钟才能阅读完成。

引言

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

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

让咱们用 Java 来摸索 ta 们!

悖论 1:驰名的三门问题

规定形容 :你正在加入一个游戏节目,你被要求在三扇门中抉择一扇:其中一扇前面有一辆车;其余两扇前面则是山羊。你抉择了一道门,假如是一号门,而后晓得门前面有什么的主持人,开启了另一扇前面有山羊的门,假如是三号门。他而后问你:“你想抉择二号门吗?请问若想取得车,参赛者应该换二号门吗?

论证 :剖析需要,拆解为如下代码

/**
 * <p> 三门问题解决方案 </p>
 * @author yuanfeng.wang
 * @since 2023/8/29
 */
import java.util.Random;

public class ThreeDoorSolution {public static void main(String[] args) {
        // 模仿执行 1 万次,打印获胜的概率
        threeDoor(10000);
    }

    /**
     * 三门问题逻辑拆解
     * @param numSimulations 总共执行多少轮游戏
     */
    private static void threeDoor(int numSimulations) {
        int switchWins = 0;
        int stayWins = 0;

        Random random = new Random();
        for (int i = 0; i < numSimulations; i++) {
            // 随机确定车所在的门
            int carDoor = random.nextInt(3);

            // 玩家随机抉择一扇门
            int playerChoice = random.nextInt(3);

            // 主持人随机关上一扇门:要求该门不是玩家抉择的,且必须是羊
            int openedDoor;
            do {openedDoor = random.nextInt(3);
            } while (openedDoor == carDoor || openedDoor == playerChoice);

            // 换门后的抉择:不能是关上的门,不能是玩家抉择的门,则是替换之后的门
            int finalChoice;
            do {finalChoice = random.nextInt(3);
            } while (finalChoice == playerChoice || finalChoice == openedDoor);

            // 计算是否换门获胜
            if (finalChoice == carDoor) {switchWins++;}

            // 计算不换门获胜
            if (playerChoice == carDoor) {stayWins++;}
        }

        // 输入后果
        System.out.println("在" + numSimulations + "次模仿中:");
        System.out.println("换门获胜的概率:" + (double) switchWins / numSimulations);
        System.out.println("不换门获胜的概率:" + (double) stayWins / numSimulations);
    }
}
// 模仿运行,打印后果如下
// 在 10000 次模仿中:// 换门获胜的概率:0.6679
// 不换门获胜的概率:0.3321

论断 :三门问题看似一道简略的概率题,几十年来却始终引发微小争议,持两种不同观点的人根本是五五开;事实上始终抉择换门的玩家,获胜的概率 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 万时,屡次执行,能够很轻松打印出一等奖


import java.util.*;

/**
 * <p> 双色球随机模仿 </p>
 * @author yuanfeng.wang
 * @since 2023/8/29
 */
public class SsqSolution {private static Random random = new Random();

    /**
     * 开奖的红球
     */
    private static Set<Integer> winningRedBalls;

    /**
     * 开奖的蓝球
     */
    private static int winningBlueBall;

    // 动态块初始化一组开奖号码
    static {
        // 篮球 01-16
        winningBlueBall = random.nextInt(16) + 1;

        // 红球 01-33 生成 6 个
        winningRedBalls = new HashSet<>();
        while (winningRedBalls.size() < 6) {int num = random.nextInt(33) + 1;
            winningRedBalls.add(num);
        }
    }

    public static void main(String[] args) {play(500_0000);
    }

    /**
     *
     * @param num 运行一次程序只开一次奖,此参数示意总共购买多少注
     */
    public static void play(int num) {System.out.println("\n 本期开奖号码:");
        System.out.println("红球:" + winningRedBalls + "篮球:" + winningBlueBall);
        for (int i = 0; i < num; i++) {playOnce();
        }
    }

    private static void playOnce() {Set<Integer> userRedBalls = getUserSelectedRedBalls();
        int userBlueBall = getUserSelectedBlueBall();

        int redBallMatch = countMatchingBalls(userRedBalls, winningRedBalls);
        boolean blueBallMatch = (userBlueBall == winningBlueBall);

        if (redBallMatch == 6 && blueBallMatch) {System.out.println("\n 祝贺你中了一等奖!");
            System.out.println("玩家购买的号码:");
            System.out.println("红球:" + userRedBalls + "蓝球:" + userBlueBall);
        } else if (redBallMatch == 6) {System.out.println("\n 祝贺你中了二等奖!");
        } else if (redBallMatch == 5 && blueBallMatch) {//            System.out.println("\n 祝贺你中了三等奖!");
        } else if (redBallMatch == 5 || (redBallMatch == 4 && blueBallMatch)) {//            System.out.println("\n 祝贺你中了四等奖!");
        } else if (redBallMatch == 4 || (redBallMatch == 3 && blueBallMatch)) {//            System.out.println("\n 祝贺你中了五等奖!");
        } else if (blueBallMatch) {//            System.out.println("\n 祝贺你中了最小奖!");
        } else {// 没中奖,不打印记录}
    }

    /**
     * 返回玩家抉择的 6 个红球, 范畴 1 -33,不反复
     */
    private static Set<Integer> getUserSelectedRedBalls() {Set<Integer> userRedBalls = new HashSet<>();
        while (userRedBalls.size() < 6) {int num = random.nextInt(33) + 1;
            userRedBalls.add(num);
        }
        return userRedBalls;
    }

    /**
     * 玩家抉择的 1 个蓝球, 范畴 1 -16
     */
    private static int getUserSelectedBlueBall() {return random.nextInt(16) + 1;
    }

    /**
     * 匹配中了几个红球
     * @return 中红球个数
     */
    private static int countMatchingBalls(Set<Integer> userBalls, Set<Integer> winningBalls) {
        int count = 0;
        for (int ball : userBalls) {if (winningBalls.contains(ball)) {count++;}
        }
        return count;
    }

}

论断 :排除其它因素,头奖概率约 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 的代码论证过程

作者:京东保险 王苑沣

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

正文完
 0