簡體   English   中英

生成大量隨機數的有效方法

[英]Efficient way to generate lots of random numbers

我有一個Java方法,必須在很短的時間內生成大量隨機數。 我的第一種方法是使用Math.random(運行速度非常快),但是我有一個假設,因為我稱Math.random如此之快,所以“隨機”並不是真正的隨機(或不太隨機)因此(但我需要盡可能隨機)。

我現在有兩個問題:

  1. 我的假設是正確的,因為在很短的時間內調用次數眾多,隨機輸出的隨機性就會降低嗎? 並且如果1.的答案是“是”:
  2. 用隨機性減少問題的最快方法(每次通話)是什么?

我已經使用過SecureRandom ,但是它至少比普通Math.random慢15倍,這對我的要求來說太慢了。

TL; DR:您的推定是錯誤的。

Math.random作用於java.util.Random的單個實例:

返回一個帶正號的雙精度值,大於或等於0.0且小於1.0。 從該范圍(近似)均勻分布偽隨機地選擇返回值。

首次調用此方法時,它會創建一個新的偽隨機數生成器,就像通過表達式

new java.util.Random()

JavaDoc

現在, java.util.Random使用線性同余公式 ,該公式的種子編號為“ 很可能與該構造函數的任何其他調用不同。1

由於這是偽隨機級數-即它將從同一種子中獲得完全相同的值-從Math.random提取數字的速度不會影響其隨機性。

使用Random類的隨機數使用一種位整型int為您提供新int的算法。 無論您調用它有多快或多少次,它都將使用相同的算法。 進步就是進步。

要對此進行測試,請給其添加數字(例如42)。然后觀察進度。 再次以相同的數量播種。 完全相同的進度。

這種方法的缺點是數字不是真正隨機的。 它們是相當隨機的,並且對大多數事情都足夠好,但是並不是完全隨機的。

我通過一系列的測試運行了Random方法的輸出。 它以飛揚的色彩通過了它們中的大多數,一種是臨界線,而另一種則是平坦失敗。 這就是我們所說的隨機性。

另外,由於它使用日期時間戳進行播種,因此在某些情況下可以預知。 想像一下一個人,該人在該周的每個星期一早上啟動並運行任務。 有一些可預測性,因為它將以星期一早上8點至8:30之間的時間戳記運行。

因此,對於大多數與安全性無關的操作,隨機性已經足夠了。 甚至很多。

另一方面,SecureRandom將生成真正的隨機數。 它通過查看系統時序以及其他因各種因素而每秒變化的事物來實現此目的。

不利的一面是,這些因素僅在一秒鍾內就會頻繁變化,因此SecureRandom在一段時間內只能生成有限數量的隨機數。 它確實嘗試提前生成一些內容並緩存它們以供使用,但是您可以刪除緩存。

這樣,就像我的反滲透濾水器。 它裝有一加侖已經過濾的水。 如果一次性使用一整加侖的水,那么您得到的水的過濾速度就會很高-大約每5秒1盎司或類似的水量。 第一加侖是快的,然后真的很慢。

如果可以使用Java8,則建議使用java.utils.SplitableRandom 它更快,統計分布更好。 在我的測試中,java.utils.SplitableRandom比java.utils.Random快30倍。

我用tobijdc答案來寫這個答案。

  1. 給定相同的初始種子值,(偽)隨機數生成器會產生相同的結果,而與調用它的頻率無關。 它完全是確定性的,與速度無關。 種子的選擇取決於時間(如果未明確指定),而不取決於生成的序列。
  2. 如果需要更快的速度,則可以預先計算大於所需長度的偽隨機數序列的值,然后僅使用一次調用生成器來選擇序列中的起始位置。 這樣,您可以在所有后續運行的一次初始調用之后簡單地讀出值。 您的性能將受到索引和讀取保存該表的內存的速度的限制。 根據您的應用程序,可能不建議重用該序列。

盡管Random可能足夠好,但是您可以通過使用更接近所需功能的函數來改進Math.random() 例如

Random rand = new Random();

for ( loop ) {
   int dice = rand.nextInt(6) + 1;

這比使用Math.random()快得多,但是如果您需要很long

long l = rand.nextLong();

在這種情況下, l具有64位隨機性,但是Math.random()最多具有53位(實際上,它只有48位)

如果您快速需要大量數字(例如用於模擬或蒙特卡洛集成),那么加密安全的RNG不夠快。 java.util.Random()速度很快,但是PRNG的質量很差。 您需要的是高質量的快速PRNG,例如Mersenne Twister或XorShift。 例如,看看http://xorshift.di.unimi.it/或我自己的ojrandlib。

嘗試使用Java KissAESPRNG內部生成器。 它是線程安全的,在批量請求中使用時,速度大約是Random的兩倍,並且可以產生128位加密強度高(但如果可重設種子,則可以重復)的偽隨機數。 它基於AES CTR模式,該模式在大多數系統上都經過了高度優化。

kiss.util.AESPRNG prng = new kiss.util.AESPRNG();
double [] x = new double [1_000_000];
prng.nextDoubles(x,0,x.length);

如果需要可重復的序列,請使用seed(byte [] value16)或seed(double value)。 重設順序。 它是對Random的直接替代,但是具有許多用於范圍或批量數的便捷方法。 它確實比任何建議的替代方案都好:快速,可重復和128位強隨機性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM