谈到随机性,这大概是一个令人困惑哲学问题吧。随机行为精确地说究竟指的是什么,最好是有定量的定义。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
网友评论