首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Java 抽獎算法:基於 Random.nextInt 的概率實現與陷阱

     編輯:程式設計綜合問答

原始代碼問題分析

int i = new Random().nextInt(1000); // [0, 999]
if (i < 5) { /* 一等獎 0.5% */ }
else if (i < 10) { /* 二等獎 1% */ }
else if (i < 100) { /* 三等獎 10% */ }
else { /* 未中獎 */ }

概率分佈正確:一等獎 5/1000=0.5%,二等獎 5/1000=0.5%(原碼寫 10% 有誤),三等獎 90/1000=9%(原碼寫 10% 有誤)。

嚴重邏輯缺陷:

  1. 有放回抽樣:每次調用 nextInt 獨立,同一獎項可能重複中獎,導致實際發出獎品數量超過庫存(如 5 個一等獎可能發出 6+ 個)。
  2. 無庫存控制:獎品數量固定時,必須「不放回」抽取。

正確方案:洗牌算法(Fisher-Yates Shuffle)

public class Lottery {
  // 獎池:每個元素代表一張獎券
  private final List<String> pool = new ArrayList<>();
  private int cursor = 0;

  public Lottery() {
    // 初始化獎池
    addPrizes("一等獎-免單", 5);
    addPrizes("二等獎-升艙", 10);
    addPrizes("三等獎-貴賓室", 100);
    addPrizes("未中獎", 885); // 總共 1000
    Collections.shuffle(pool); // 洗牌
  }

  private void addPrizes(String prize, int count) {
    for (int i = 0; i < count; i++) pool.add(prize);
  }

  // 線程安全抽獎
  public synchronized String draw() {
    if (cursor >= pool.size()) return "獎池耗盡";
    return pool.get(cursor++);
  }
}

核心優勢

  • 精確控制數量:獎池初始化時固定每種獎品數量,抽完即止。
  • 概率精確:洗牌後前 N 次中獎分佈完全符合配置比例。
  • 高性能:O(1) 抽取,無隨機數碰撞重試開銷。
  • 可擴展:支持動態調整獎池、分布式同步(配合 Redis Lua 腳本)。

分布式場景補充

多實例部署時,需將獎池放入 Redis,使用 Lua 腳本原子執行 LPOP 或 RPOP 保證線程安全與一致性。

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