Java的随机数原理

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)作与运算,就是取余的运算。

你可能感兴趣的:(java)