首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

微信紅包隨機分配算法 Java 實現

     編輯:Java 相關知識

微信紅包隨機演算法完整解析與 Java 實現

核心演算法邏輯

基本思路:計算平均值,隨機生成小於平均值的金額,扣除後重新計算剩餘金額與平均值,最後一個紅包領走剩餘所有金額。

版本一:基礎隨機算法

public class WeChatRedPacket {
    public static void distribute(double totalAmount, int count) {
        double min = 0.01; // 最小金額 1 分
        Random random = new Random();
        DecimalFormat df = new DecimalFormat("#.##"); // 保留兩位小數

        for (int i = 1; i < count; i++) {
            // 隨機範圍:最小值 到 剩餘金額
            double amount = Double.parseDouble(df.format(
                min + random.nextDouble() * (totalAmount - min)
            ));
            totalAmount = Double.parseDouble(df.format(totalAmount - amount));
            System.out.printf("第 %d 個紅包:%.2f 元,餘額:%.2f 元%n", i, amount, totalAmount);
        }
        System.out.printf("最後一個紅包:%.2f 元%n", totalAmount);
    }

    public static void main(String[] args) {
        distribute(10.26, 9); // 10.26 元,9 個紅包
    }
}

版本二:改進版(確保不超過 2 倍平均值)

public static double[] distributeV2(double totalAmount, int count) {
    Random r = new Random();
    DecimalFormat df = new DecimalFormat(".##");
    double[] result = new double[count];
    double remaining = totalAmount;
    double avg = Double.parseDouble(df.format(totalAmount / count));
    int idx = 0;

    for (int i = count; i > 0; i--) {
        if (i == 1) {
            result[idx] = Double.parseDouble(df.format(remaining));
        } else {
            double amount;
            while (true) {
                amount = Double.parseDouble(df.format(r.nextDouble() * remaining));
                if (amount > 0 && amount < avg * 2) break; // 限制最大不超過 2 倍平均
            }
            remaining = Double.parseDouble(df.format(remaining - amount));
            result[idx] = amount;
            avg = Double.parseDouble(df.format(remaining / (i - 1))); // 重新計算平均
            idx++;
        }
    }
    return result;
}

版本三:安全邊界算法(保證剩餘紅包不低於最小值)

public static void distributeSafe(double total, int count, double min) {
    DecimalFormat df = new DecimalFormat("#.##");
    for (int i = 1; i < count; i++) {
        // 安全上限:確保剩餘 (count-i) 個紅包至少能分到最小值
        double safeMax = (total - (count - i) * min) / (count - i);
        double amount = min + Math.random() * (safeMax - min);
        amount = new BigDecimal(amount).setScale(2, RoundingMode.HALF_UP).doubleValue();
        total = new BigDecimal(total - amount).setScale(2, RoundingMode.HALF_UP).doubleValue();
        System.out.printf("第 %d 個:%.2f 元,餘額:%.2f 元%n", i, amount, total);
    }
    System.out.printf("第 %d 個:%.2f 元%n", count, total);
}

版本四:雙邊限制算法(上下界動態調整)

public static void distributeAdvanced(int count, float total) {
    float min = 1; // 最小 1 元
    List<Float> amounts = new ArrayList<>();
    DecimalFormat df = new DecimalFormat("###.##");

    for (int i = 1; i < count; i++) {
        // 最大值:確保剩餘紅包不小於最小值
        float max = total - min * (count - i);
        int k = (count - i) / 2;
        if (count - i <= 2) k = count - i; // 最後兩個特殊處理
        max = max / k; // 限制在平均線附近

        // 隨機生成 [min, max] 區間金額
        float amount = (min * 100 + (float) Math.random() * (max * 100 - min * 100 + 1)) / 100;
        amount = Float.parseFloat(df.format(amount));

        total -= amount;
        amounts.add(amount);
        System.out.printf("第 %d 個:%.2f 元,剩餘:%.2f 元%n", i, amount, total);
    }
    amounts.add(total);
    System.out.printf("第 %d 個:%.2f 元%n", count, total);

    // 找出手氣最佳
    int bestIdx = amounts.indexOf(Collections.max(amounts));
    System.out.println("手氣最佳:第 " + (bestIdx + 1) + " 個");
}

關鍵點總結

  1. 使用 BigDecimal 處理金額,避免浮點數精度問題。
  2. 動態計算安全上限,確保剩餘紅包都能分到最小值。
  3. 限制隨機範圍(如 0.5~2 倍平均值),避免極端分配。
  4. 最後一個紅包領走餘額,保證總金額精確。

效能與公平性權衡

上述算法時間複雜度 O(n),完全滿足高併發紅包場景。實際生產環境建議結合 Lua 腳本在 Redis 中原子執行,避免分佈式一致性問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved