java生成离散型随机数_几种随机数生成方式

谈到随机性,这大概是一个令人困惑哲学问题吧。随机行为精确地说究竟指的是什么,最好是有定量的定义。Kolmogorov曾提出一种判定随机性的方法:对于无穷的随机数序列,无法用其子序列描述。J.N.Franklin则认为:如果一个序列具有从一个一致同分布的随机变量中独立抽样获得的每个无限序列都有的性质,则是随机的。这些定义都不是很精确,有时甚至会导致矛盾。可见数学家在谈到这个问题时是多么的审慎。随机数生成器只是一种产生符合特定分布的随机数的算法。这些所谓的随机数序列实际上是周期性的。从实用的角度出发,随机数生成器如果能够在尽可能多的场合中产生正确的结果,那么它就是好的。但是这个愿望无法完全实现。因为每一个生成器都会在特定的场合失效,比如说可能无法达到随机数的均匀性或者随机数之间隐藏着关联。已经有大量的随机数生成器,但是找到好的、易移植的、达到工业水准的随机数生成器是一个难以实现的目标。生成非均匀分布的标准方法是先产生均匀分布随机数,然后将其转化为特定分布的随机数。

一个比较简单的方法,用随机数填充一个位图

a6dc17f0cb91

几种随机数生成方式填充的位图

这是每种方法生成 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.

*

*

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.

*

*

The method {@code nextDouble} is implemented by class {@code Random}

* as if by:

*

 {@code

* public double nextDouble() {

* return (((long)next(26) << 27) + next(27))

* / (double)(1L << 53);

* }}

*

*

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.

*

[In early versions of Java, the result was incorrectly calculated as:

*

 {@code

* return (((long)next(27) << 27) + next(27))

* / (double)(1L << 54);}

* 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.

*

*

When this method is first called, it creates a single new

* pseudorandom-number generator, exactly as if by the expression

*

*

{@code new java.util.Random()}

*

* This new pseudorandom-number generator is used thereafter for

* all calls to this method and is used nowhere else.

*

*

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值