每日算法 — 使用java实现轮盘赌:蒙特卡洛模拟与条件概率决策

引言

轮盘赌(Roulette)是赌场中最经典的概率游戏之一,玩家通过预测小球落入的号码或颜色来下注。本文用Java实现轮盘赌的概率分析系统,讲解蒙特卡洛模拟方法、条件概率计算以及期望值驱动的最优下注策略,提供完整的模拟代码与统计分析。

轮盘赌规则与概率模型

欧式轮盘布局

欧式轮盘包含37个格子:数字0(绿色)和1-36(红色18个、黑色18个)。常见下注方式及赔率:

下注类型 赔率 中奖概率
单个数字 35:1 1/37 ≈ 2.70%
红黑 1:1 18/37 ≈ 48.65%
奇偶 1:1 18/37 ≈ 48.65%
大小(1-18/19-36) 1:1 18/37 ≈ 48.65%
列注 2:1 12/37 ≈ 32.43%
打注(12个连续) 2:1 12/37 ≈ 32.43%

期望值计算

轮盘赌的庄家优势来自于0号。以红黑下注为例:

期望值 = (18/37) * 1 + (19/37) * (-1) = -1/37 ≈ -2.70%

无论采用何种策略,长期期望收益均为负。本文的算法价值在于理解概率模型和蒙特卡洛方法。

蒙特卡洛模拟

蒙特卡洛方法通过大量随机模拟来估计概率分布和统计特征:

public class RouletteMonteCarlo {
    private static final int[] RED_NUMBERS = {
        1, 3, 5, 7, 9, 12, 14, 16, 18, 
        19, 21, 23, 25, 27, 30, 32, 34, 36
    };
    private static final Set<Integer> RED_SET = Arrays.stream(RED_NUMBERS)
        .boxed().collect(Collectors.toSet());
    private final Random random = new Random();

    /**
     * 模拟一次轮盘旋转,返回结果号码
     */
    public int spin() {
        return random.nextInt(37); // 0-36
    }

    /**
     * 判断号码颜色
     */
    public String getColor(int number) {
        if (number == 0) return "GREEN";
        return RED_SET.contains(number) ? "RED" : "BLACK";
    }

    /**
     * 蒙特卡洛模拟红黑下注策略
     * @param initialBankroll 初始资金
     * @param betSize 每局下注金额
     * @param numRounds 模拟局数
     * @param numSimulations 模拟次数
     * @return 统计结果:平均剩余资金、破产概率等
     */
    public SimulationResult simulateRedBlack(
            double initialBankroll, double betSize, 
            int numRounds, int numSimulations) {

        double totalRemaining = 0;
        int bankruptcies = 0;
        double maxBankroll = 0;
        List<Double> finalBankrolls = new ArrayList<>();

        for (int sim = 0; sim < numSimulations; sim++) {
            double bankroll = initialBankroll;
            double peak = bankroll;

            for (int round = 0; round < numRounds && bankroll >= betSize; round++) {
                int result = spin();
                String color = getColor(result);

                if (color.equals("RED")) {
                    bankroll += betSize; // 赢得等额赌注
                } else {
                    bankroll -= betSize; // 输掉赌注(BLACK或GREEN)
                }

                peak = Math.max(peak, bankroll);
            }

            if (bankroll < betSize) bankruptcies++;
            totalRemaining += bankroll;
            finalBankrolls.add(bankroll);
            maxBankroll = Math.max(maxBankroll, peak);
        }

        Collections.sort(finalBankrolls);
        return new SimulationResult(
            totalRemaining / numSimulations,
            (double) bankruptcies / numSimulations,
            maxBankroll,
            finalBankrolls.get(numSimulations / 4),   // 25%分位
            finalBankrolls.get(numSimulations * 3 / 4) // 75%分位
        );
    }
}

条件概率与序列分析

赌徒常犯的错误是”赌徒谬误”——认为连续出现红色后黑色更可能出现。实际上每次旋转都是独立事件:

public class ConditionalProbability {
    private RouletteMonteCarlo roulette = new RouletteMonteCarlo();

    /**
     * 验证赌徒谬误:检验连续出现红色后下一次出现黑色的概率
     * 理论值应为 18/37 ≈ 48.65%,与历史序列无关
     */
    public double testGamblerFallacy(int sequenceLength, int numTests) {
        int blackAfterReds = 0;
        int totalCases = 0;

        for (int test = 0; test < numTests; test++) {
            List<String> history = new ArrayList<>();

            // 生成历史序列直到满足条件
            while (true) {
                int num = roulette.spin();
                history.add(roulette.getColor(num));

                if (history.size() >= sequenceLength) {
                    // 检查最近sequenceLength次是否全为RED
                    boolean allRed = true;
                    for (int i = history.size() - sequenceLength; i < history.size(); i++) {
                        if (!history.get(i).equals("RED")) {
                            allRed = false;
                            break;
                        }
                    }

                    if (allRed) {
                        // 记录下一次结果
                        int next = roulette.spin();
                        if (roulette.getColor(next).equals("BLACK")) {
                            blackAfterReds++;
                        }
                        totalCases++;
                        break;
                    }
                }
            }
        }

        return totalCases > 0 ? (double) blackAfterReds / totalCases : 0;
    }

    /**
     * 计算在连续N次红色后,未来M次内至少出现一次黑色的概率
     */
    public double probabilityOfBlackWithin(int afterRedStreak, int futureSpins) {
        // 每次独立,P(黑色) = 18/37
        double pBlack = 18.0 / 37;
        double pNoBlack = Math.pow(1 - pBlack, futureSpins);
        return 1 - pNoBlack;
    }
}

马丁格尔策略模拟

马丁格尔(Martingale)是一种著名的加倍下注策略:输后将赌注翻倍,直到赢为止:

public class MartingaleStrategy {
    private RouletteMonteCarlo roulette = new RouletteMonteCarlo();

    /**
     * 模拟马丁格尔策略
     * @return 最终资金,-1表示破产
     */
    public double simulate(double bankroll, double baseBet, int maxRounds) {
        double currentBet = baseBet;

        for (int round = 0; round < maxRounds; round++) {
            if (bankroll < currentBet) return -1; // 破产

            int result = roulette.spin();
            String color = roulette.getColor(result);

            if (color.equals("RED")) {
                bankroll += currentBet; // 赢回等额赌注
                currentBet = baseBet;   // 重置为基础赌注
            } else {
                bankroll -= currentBet; // 输掉赌注
                currentBet *= 2;        // 加倍下注
            }
        }

        return bankroll;
    }

    /**
     * 批量模拟马丁格尔策略,统计破产率
     */
    public StrategyResult batchSimulate(double bankroll, double baseBet, 
                                         int maxRounds, int simulations) {
        int bankruptcies = 0;
        double totalProfit = 0;
        int maxConsecutiveLosses = 0;

        for (int i = 0; i < simulations; i++) {
            double result = simulate(bankroll, baseBet, maxRounds);
            if (result < 0) {
                bankruptcies++;
            } else {
                totalProfit += (result - bankroll);
            }

            // 模拟单次最长连败
            int losses = 0;
            int maxLoss = 0;
            for (int r = 0; r < maxRounds; r++) {
                if (!roulette.getColor(roulette.spin()).equals("RED")) {
                    losses++;
                    maxLoss = Math.max(maxLoss, losses);
                } else {
                    losses = 0;
                }
            }
            maxConsecutiveLosses = Math.max(maxConsecutiveLosses, maxLoss);
        }

        return new StrategyResult(
            (double) bankruptcies / simulations,
            totalProfit / (simulations - bankruptcies),
            maxConsecutiveLosses
        );
    }
}

完整项目结构

roulette-simulator/
├── src/
│   ├── model/
│   │   ├── SimulationResult.java  # 模拟结果封装
│   │   └── StrategyResult.java    # 策略结果封装
│   ├── core/
│   │   └── RouletteMonteCarlo.java # 蒙特卡洛核心引擎
│   ├── analysis/
│   │   ├── ConditionalProbability.java # 条件概率分析
│   │   └── MartingaleStrategy.java     # 马丁格尔策略
│   └── report/
│       └── StatisticalReport.java      # 统计报告生成
└── Main.java

复杂度分析

模块 时间复杂度 说明
单次旋转 O(1) 随机数生成
蒙特卡洛模拟 O(S × R) S=模拟次数, R=每模拟局数
条件概率检验 O(S × E[L]) E[L]为达到条件期望等待长度
马丁格尔模拟 O(R) 每轮一次旋转

总结

本文通过Java实现了轮盘赌的概率分析系统。蒙特卡洛模拟验证了大数定律下的期望收益,条件概率分析揭示了赌徒谬误的数学本质,马丁格尔策略模拟展示了加倍下注策略的破产风险。读者可以通过这些算法深入理解概率论在博弈分析中的应用。