使用Monte Carlo模拟计算生日重合概率的Java程序问题排查与修复
蒙特卡洛生日概率模拟程序问题排查与修复
问题根源分析
原程序输入班级规模20、共享生日人数2时输出仅7%-8%,远低于预期的41%,核心错误有三个:
- 数组初始化遗漏索引0:
Collision方法中给生日数组赋值时,循环从i=1开始,导致数组第0位始终是默认值0,相当于第一个人的生日固定为1月1日,完全破坏了随机模拟的公平性。 - 碰撞计数与判断逻辑不匹配需求:原方法返回的是所有生日相同的对数,而判断条件
Collision(...)>=birthPpl要求对数大于等于指定人数。当需求是“至少2人共享生日”时,只要存在1对就满足条件,但原程序要求至少2对才计数,直接过滤了大部分符合条件的情况。 - Random对象重复创建:每次调用
Collision都新建Random实例,既影响随机性,也降低了模拟效率。
修复方案
- 修正生日数组的初始化循环,从
i=0开始遍历所有元素,确保每个人的生日都是随机生成的。 - 重构碰撞检测逻辑:统计每个生日出现的次数,只要有任意一个生日的出现次数≥指定的共享人数,就判定为成功,不再统计对数。
- 复用
Random实例,在主方法中创建一次,作为参数传入检测方法。
修复后的完整代码
import java.util.*; public class MonteCarlo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入班级人数: "); int classSize = sc.nextInt(); System.out.println("请输入共享生日的最少人数: "); int birthPpl = sc.nextInt(); System.out.println("计算班级中至少" + birthPpl + "人共享同一生日的概率"); sc.close(); int simulations = 1000000; int success = 0; Random rand = new Random(); // 复用Random实例 for (int i = 0; i < simulations; i++) { if (hasMatchingBirthday(classSize, birthPpl, rand)) { success++; } } double probability = (double) success / simulations * 100; System.out.printf("成功次数: %d, 总仿真次数: %d%n", success, simulations); System.out.printf("概率: %.2f%%%n", probability); } public static boolean hasMatchingBirthday(int classSize, int requiredCount, Random rand) { Map<Integer, Integer> birthdayCounts = new HashMap<>(); for (int i = 0; i < classSize; i++) { int birthday = rand.nextInt(365); birthdayCounts.put(birthday, birthdayCounts.getOrDefault(birthday, 0) + 1); // 提前终止优化:一旦找到符合条件的生日,直接返回 if (birthdayCounts.get(birthday) >= requiredCount) { return true; } } return false; } }
修复说明
- 用HashMap统计每个生日的出现次数,一旦某个生日的计数达到要求,直接返回true,提升了模拟效率。
- 复用Random实例,保证随机性的同时减少对象创建开销。
- 判断逻辑直接匹配需求:只要存在至少
requiredCount人共享生日,就算一次成功,统计结果会符合预期(输入20和2时,结果约为41%)。
内容的提问来源于stack exchange,提问作者rimvoo
相关产品推荐
相关产品推荐

