Java生成随机数时,可以会使用以下方法
final int i = new Random().nextInt(int bound);
或者
final double v = Math.random();
初一看,以为是两种方式,其实第二种的底层也是使用了Random
的方法nextDouble()
,所以我们主要看Random
的方法即可。
原理
计算机生成随机数,有几种方式
1.物理方式
正在意义上的随机数生成器,无法预测且无周期,一般利用计算机的电阻、热噪声等得到随机数,不过物理方式生成随机数的效率不高,需要计算机高效的计算。
2.随机数表方式
事先生成好随机数,放在内存里,需要时从表里获取,这种需要内存,如果随机数量非常大,那将需要很大的内存。
3.伪随机数方式
使用“种子”来生成随机数序列,使用算法在种子基础上生成随机数,由于是算法生成的,伪随机数生成是可以预测且周期性的,并不算是真正意义上的随机,所以叫伪随机数。
同余法
同余公式(congruential formula)是一种常用的伪随机数算法。在Java中,线性同余算法就是同余算法的一种变种。Random类中,使用了48(bit)位作为种子,在线性同余算法中生成随机数。
使得结果小于某个数,一般使用取余的计算,结果都是同一除数的余数,所以叫做同余法。
nextInt(int bound)方法会返回一个伪随机的,均匀分布于0(包含)到bound(不包含)之间的int值。
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
int r = next(31);
int m = bound - 1;
if ((bound & m) == 0) // i.e., bound is a power of 2
r = (int)((bound * (long)r) >> 31);
else {
for (int u = r;
u - (r = u % bound) + m < 0;
u = next(31))
;
}
return r;
}
其中next(int bit)
方法源码
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
线性同余公式就是这一行代码,
nextseed = (oldseed * multiplier + addend) & mask;
这个和以下线性公式非常相似
Xn+1=(a*Xn+c)(mod m)
Xn刚开始时就是初始化时的“种子”,经过运算会得到新的“种子”nextseed作为下一次运算的“种子”oldseed。
或许,你觉得疑惑求余,没看到有取余的计算,可以看看multiplier
,addend
,mask
变量
private static final long multiplier = 0x5DEECE66DL;
private static final long addend = 0xBL;
private static final long mask = (1L << 48) - 1;
和mask(即是 (1L << 48) - 1)作与运算,就是取余的运算。