java.util.Random 是 J2ME 遊戲、加密、模擬中最常用的工具類。CLDC 1.0 即內置,API 極簡,但無數新手因誤解「種子機制」與「實例化時機」導致「隨機數全是一樣的」慘劇。本文從原理到實戰,徹底講透正確姿勢。
| 構造器/方法 | 說明 |
|---|---|
Random() | 以系統時間 (ms) 為種子 |
Random(long seed) | 指定種子,序列完全確定 |
next(int bits) | 返回 bits 位隨機數,範圍 [0, 2^bits) |
nextInt() | 等價 next(32),32 位有符號整數 |
nextLong() | 64 位長整數 |
setSeed(long seed) | 重置種子,重啟序列 |
關鍵點:Random 實現 Runnable,內部為線性同餘生成器 (LCG),屬於偽隨機——同種子必產生同序列。
// 錯誤示範:每次迭代 new 實例,種子皆為當前時間毫秒數
for (int i = 0; i < 20; i++) {
Random r = new Random(); // 若循環極快,多次取到同一毫秒種子
System.out.println(r.nextInt()); // 大量重複數字!
}
原因:CLDC 實現多以 System.currentTimeMillis() 作為無參構造器種子。毫秒級精度在緊湊循環下極易碰撞。
// 正確:全局/成員變量持有唯一實例
private Random random = new Random(); // 或 new Random(System.currentTimeMillis())
public void generate() {
for (int i = 0; i < 20; i++) {
System.out.println(random.nextInt()); // 序列連續,統計特性良好
}
}
J2ME 無 nextInt(bound),需自行映射:
// [0, n) 整數
public int nextInt(Random r, int n) {
if (n <= 0) throw new IllegalArgumentException();
return (int) ((r.nextLong() & 0x7FFFFFFFFFL) % n); // 取模偏差極小,可接受
}
// [min, max] 整數
public int nextInt(Random r, int min, int max) {
return min + nextInt(r, max - min + 1);
}
new Random(12345L),輸出確定序列,便於回歸驗證。Random 可預測,絕不**用於密鑰生成、Session ID、支付隨機數**。System.currentTimeMillis()、Runtime.freeMemory()、鍵盤事件時間戳等作種子:new Random(System.currentTimeMillis() ^ Runtime.getRuntime().freeMemory() ^ keyEventTime)。private void placeMines(int[][] board, int mineCount, Random r) {
int cells = board.length * board[0].length;
boolean[] placed = new boolean[cells];
int placedCount = 0;
while (placedCount < mineCount) {
int idx = (int) ((r.nextLong() & 0x7FFFFFFFFFL) % cells);
if (!placed[idx]) {
placed[idx] = true;
board[idx / board[0].length][idx % board[0].length] = MINE_FLAG;
placedCount++;
}
}
}
優勢:無重複、均勻分佈、單次遍歷,優於原文「允許重複、實際地雷數不定」的 naïve 寫法。
new Random()。nextLong() & Long.MAX_VALUE 取模,避免負數陷阱。