Java 中的隨機種子如何運作?
1. 概述
隨機性是一個令人著迷的概念,可應用於密碼學、遊戲、模擬和機器學習等各個領域。在電腦系統中,真正的隨機性是難以捉摸的。
在 Java 中,隨機性通常是使用偽隨機數產生器(PRNG) 產生的。這些生成器並不是真正隨機的,而是依賴生成數字序列的演算法,這些數字看似隨機,但由稱為種子的起點確定。
在本教程中,我們將探討這些隨機種子在 Java 中的工作原理,並揭示它們在隨機數生成中的作用。我們還將討論不同的 Java 類別如何利用這些種子來產生可預測的隨機值序列,以及這些機制對各種應用程式的影響。
2. Random班級如何運作?
為了在 Java 中產生隨機數,我們使用Random類,它產生看起來隨機的數字。然而,我們得到的是一個偽隨機數,這意味著雖然序列看起來是隨機的,但確定性演算法是根據初始輸入(種子)產生它的。
許多程式語言(包括 Java)中的Random實作都使用線性同餘產生器 (LCG) 演算法。該演算法基於一個簡單的數學公式產生一個數字序列:
X n+1 = (aX n + C) % m
其中X n是目前值, X n+1是下一個值, a是乘數, c是增量, and m是模數。初始值X 0是種子。
a,c,和m的選擇會顯著影響產生的隨機數的品質。取 mod m的餘數類似於計算出球在有編號部份的旋轉輪上的最終位置。
例如,我們取m = 10 且X 0 = a = c = 7 時所得的序列為
7,6,9,0,7,6,9,0,...
如上例所示,對於a, m, c, and X 0 .
3. 種子的作用
種子是啟動PRNG進程的初始輸入。種子就像一把鑰匙,可以從大量預先確定的集合中解鎖特定的數字序列。使用相同的種子將始終產生相同的數字序列。例如,使用種子 35 初始化Random物件並要求它產生 12 個隨機數,每次執行程式碼時都會產生相同的序列:
public void givenNumber_whenUsingSameSeeds_thenGenerateNumbers() {
Random random1 = new Random(35);
Random random2 = new Random(35);
int[] numbersFromRandom1 = new int[12];
int[] numbersFromRandom2 = new int[12];
for(int i = 0 ; i < 12; i++) {
numbersFromRandom1[i] = random1.nextInt();
numbersFromRandom2[i] = random2.nextInt();
}
assertArrayEquals(numbersFromRandom1, numbersFromRandom2);
}
當我們需要可預測的結果來進行測試或調試、模擬和加密時,此屬性至關重要,但它也允許在需要時實現隨機性。
4. Java 中的預設種子
我們可以建立一個Random類別物件而不指定種子,Java 將使用目前系統時間作為種子。**在內部, Random類別呼叫其建構函數,該構造函數採用long種子參數,但它根據系統時間計算該種子。**
這種方法提供了一定程度的隨機性,但並不完美。系統時間相對可預測,幾乎同時建立的兩個Random物件有可能具有相似的種子,從而產生相關的隨機序列。
我們可以使用System.nanoTime()來獲得更精確且更難以預測的種子。然而,即使這種方法也有其限制。對於真正不可預測的數字,我們需要使用加密隨機數產生器(CSPRNG)或基於硬體的隨機數產生器(HRNG)。
讓我們來看看如何使用System.nanoTime()作為種子:
public void whenUsingSystemTimeAsSeed_thenGenerateNumbers() {
long seed = System.nanoTime();
Random random = new Random(seed);
for(int i = 0; i < 10; i++) {
int randomNumber = random.nextInt(100);
assertTrue(randomNumber >= 0 && randomNumber < 100);
}
}
5. 超越Random類別
我們可以使用 Java 中的Random類別輕鬆產生隨機數。但是,還有其他選項可用。有些更適合需要高品質或加密安全隨機數的應用程式。
5.1. SecureRandom
java.util.Random的標準 JDK 實作使用線性同餘產生器(LCG) 演算法來提供隨機數。該演算法的問題在於它的加密強度不高。換句話說,產生的值更容易預測,因此攻擊者可以利用它來破壞我們的系統。
為了克服這個問題,我們應該在任何安全決策中使用java.security.SecureRandom 。
5.2. ThreadLocalRandom
Random類別在多執行緒環境中表現不佳。簡而言之,多執行緒環境中Random效能不佳的原因是爭用——因為多個執行緒共享同一個Random實例。
為了解決這個限制, Java 在 JDK 7 中引入了java.util.concurrent.ThreadLocalRandom類別 - 用於在多執行緒環境中產生隨機數。
六、結論
在本文中,我們看到種子在控制Random類的行為方面發揮關鍵作用。我們還觀察如何使用相同的種子一致地產生相同的隨機數序列,從而產生相同的輸出。
當我們了解隨機種子的作用及其背後的演算法時,我們就可以在 Java 應用程式中產生隨機數時做出明智的選擇。這有助於我們確保它們滿足我們對品質、可重複性和安全性的特定需求。
與往常一樣,完整的源代碼可以在 GitHub 上取得。