談到隨機性,這大概是一個令人困惑哲學問題吧。隨機行為精確地說究竟指的是什么,最好是有定量的定義。Kolmogorov曾提出一種判定隨機性的方法:對于無窮的隨機數序列,無法用其子序列描述。J.N.Franklin則認為:如果一個序列具有從一個一致同分布的隨機變量中獨立抽樣獲得的每個無限序列都有的性質,則是隨機的。這些定義都不是很精確,有時甚至會導致矛盾。可見數學家在談到這個問題時是多么的審慎。隨機數生成器只是一種產生符合特定分布的隨機數的算法。這些所謂的隨機數序列實際上是周期性的。從實用的角度出發,隨機數生成器如果能夠在盡可能多的場合中產生正確的結果,那么它就是好的。但是這個愿望無法完全實現。因為每一個生成器都會在特定的場合失效,比如說可能無法達到隨機數的均勻性或者隨機數之間隱藏著關聯。已經有大量的隨機數生成器,但是找到好的、易移植的、達到工業水準的隨機數生成器是一個難以實現的目標。生成非均勻分布的標準方法是先產生均勻分布隨機數,然后將其轉化為特定分布的隨機數。
一個比較簡單的方法,用隨機數填充一個位圖
這是每種方法生成 500x500的位圖的所用時間
MersenneTwister: 5449毫秒
Math.random: 111毫秒
Random: 82毫秒
ThreadLocalRandom: 74毫秒
哪個比較差一目了然。
生成部分代碼
public static boolean Math_random_nextBoolean() {
return getNextBoolean(Math.random(), Math.random());
}
public static boolean MersenneTwister_nextBoolean() {
return getNextBoolean(new MersenneTwister(System.nanoTime()).nextDouble(), new MersenneTwister(System.nanoTime()).nextDouble());
}
public static boolean Random_nextBoolean() {
return getNextBoolean(new Random(System.nanoTime()).nextDouble(), new Random(System.nanoTime()).nextDouble());
}
public static boolean ThreadLocalRandom_nextBoolean() {
return getNextBoolean(ThreadLocalRandom.current().nextDouble(), ThreadLocalRandom.current().nextDouble());
}
private static boolean getNextBoolean(double probability, double probabilitynew) {
if (probability < 0.0 || probability > 1.0)
throw new IllegalArgumentException("probability must be between 0.0 and 1.0 inclusive.");
if (probability == 0.0) return false; // fix half-open issues
else if (probability == 1.0) return true; // fix half-open issues
return probabilitynew < probability;
}
/**
* Returns the next pseudorandom, uniformly distributed
* {@code double} value between {@code 0.0} and
* {@code 1.0} from this random number generator's sequence.
*
* <p>The general contract of {@code nextDouble} is that one
* {@code double} value, chosen (approximately) uniformly from the
* range {@code 0.0d} (inclusive) to {@code 1.0d} (exclusive), is
* pseudorandomly generated and returned.
*
* <p>The method {@code nextDouble} is implemented by class {@code Random}
* as if by:
* <pre> {@code
* public double nextDouble() {
* return (((long)next(26) << 27) + next(27))
* / (double)(1L << 53);
* }}</pre>
*
* <p>The hedge "approximately" is used in the foregoing description only
* because the {@code next} method is only approximately an unbiased
* source of independently chosen bits. If it were a perfect source of
* randomly chosen bits, then the algorithm shown would choose
* {@code double} values from the stated range with perfect uniformity.
* <p>[In early versions of Java, the result was incorrectly calculated as:
* <pre> {@code
* return (((long)next(27) << 27) + next(27))
* / (double)(1L << 54);}</pre>
* This might seem to be equivalent, if not better, but in fact it
* introduced a large nonuniformity because of the bias in the rounding
* of floating-point numbers: it was three times as likely that the
* low-order bit of the significand would be 0 than that it would be 1!
* This nonuniformity probably doesn't matter much in practice, but we
* strive for perfection.]
*
* @return the next pseudorandom, uniformly distributed {@code double}
* value between {@code 0.0} and {@code 1.0} from this
* random number generator's sequence
* @see Math#random
*/
public double nextDouble() {
return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT;
}
Random類中實現的隨機算法是偽隨機,也就是有規則的隨機。在進行隨機時,隨機算法的起源數字稱為種子數(seed),在種子數的基礎上進行一定的變換,從而產生需要的隨機數字。
相同種子數的Random對象,相同次數生成的隨機數字是完全相同的。也就是說,兩個種子數相同的Random對象,第一次生成的隨機數字完全相同,第二次生成的隨機數字也完全相同。這點在生成多個隨機數字時需要特別注意。
/**
* Returns a {@code double} value with a positive sign, greater
* than or equal to {@code 0.0} and less than {@code 1.0}.
* Returned values are chosen pseudorandomly with (approximately)
* uniform distribution from that range.
*
* <p>When this method is first called, it creates a single new
* pseudorandom-number generator, exactly as if by the expression
*
* <blockquote>{@code new java.util.Random()}</blockquote>
*
* This new pseudorandom-number generator is used thereafter for
* all calls to this method and is used nowhere else.
*
* <p>This method is properly synchronized to allow correct use by
* more than one thread. However, if many threads need to generate
* pseudorandom numbers at a great rate, it may reduce contention
* for each thread to have its own pseudorandom-number generator.
*
* @return a pseudorandom {@code double} greater than or equal
* to {@code 0.0} and less than {@code 1.0}.
* @see Random#nextDouble()
*/
public static double random() {
return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
-
MersenneTwister.java
梅森旋轉算法(Mersenne twister) 是一個偽隨機數發生算法。由。Makoto Matsumoto(松本真) 和Takuji Nishimura(西村拓士)在1997年開發的,基于有限二進制字段上的矩陣線性遞歸field F_{2}。 可以快速產生高質量的偽隨機數, 修正了古典隨機數發生算法的很多缺陷。
梅森旋轉算法這個名字來自周期長度取自梅森素數的這樣一個事實。這個算法通常使用兩個相近的變體,不同之處在于使用了不同的梅森素數。一個更新的和更常用的是MT19937, 32位字長。 還有一個變種是64位版的MT19937-64。 對于一個k位的長度,Mersenne Twister會在[0,2^k-1]的區間之間生成離散型均勻分布的隨機數。
Java實現有2個版本
一個是快速版本,非線程安全。MersenneTwisterFast.java,java.util.Random比它慢1/3。
一個是普通版本MersenneTwister.java
,線程安全,但比java.util.Random慢1/3。
-
ThreadLocalRandom
java.util.concurrent.ThreadLocalRandom這個新的API綜合了其他兩種方法的優點:單一實例/靜態訪問,就像Math.random()一樣靈活。ThreadLocalRandom也比其他任何處理高并發的方法要更快。
經驗
Chris Marasti-Georg 指出:
Math.round(Math.random() * 10)
使分布不平衡,例如:0.0 - 0.499999將四舍五入為0,而0.5至1.499999將四舍五入為1。那么如何使用舊式語法來實現正確的均衡分布,如下:
Math.floor(Math.random() * 11)
幸運的是,如果我們使用java.util.Random或java.util.concurrent.ThreadLocalRandom就不用擔心上述問題了。
Java實戰項目里面介紹了一些不正確使用java.util.Random API的危害。這個教訓告訴我們不要使用:
Math.abs(new Random().nextInt())%n
而使用:
new Random().nextInt(n)
其他請參考:http://blog.csdn.net/jianhua0902/article/details/8487005