java伪随机数

来源:互联网 发布:文明5 mac mod文件夹 编辑:程序博客网 时间:2024/05/29 14:47

首先,Math中的random方法其实是RandomnextDouble方法。

同余法(Congruential method)是很常用的一种随机数生成方法,在很多编程语言中有应用,最明显的就是java了,java.util.Random类中用的就是同余法中的一种——线性同余法(Linear congruential method),除此之外还有乘同余法(Multiplicative congruential method)和混合同余法(Mixed congruential method)。好了,现在我们就打开java的源代码,看一看线性同余法的真面目!

Eclipse中输入java.util.Random,按F3转到Random类的源代码。这个类的一个实现是用来生成一串伪随机数。这个类用了一个48位的种子,被线性同余公式修改用来生成随机数。我们找到它的构造函数与相关的几个方法,里面包含了获得48位种子的过程:

private final static long multiplier = 0x5DEECE66DL;private final static long addend = 0xBL;private final static long mask = (1L << 48) - 1;public Random() { this(++seedUniquifier + System.nanoTime()); }private static volatile long seedUniquifier = 8682522807148012L;public Random(long seed) {    this.seed = new AtomicLong(0L);    setSeed(seed);}synchronized public void setSeed(long seed) {    seed = (seed ^ multiplier) & mask;    this.seed.set(seed);    haveNextNextGaussian = false;}

  

这里分两种情况:1.采用默认种子,将seedUniquifier值(系统默认的8682522807148012L)与当前时间组合,生成一个新的种子。2.采用自定义种子,这时传什么值,种子就是多少。

再往下看,就是我们常用的得到随机数的方法了,我首先找到了最常用的nextInt()函数,代码如下:

public int nextInt() {    return next(32);}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));}


这就是线性同余了,线性同余法是一个很古老的随机数生成算法,它的数学形式如下:Xn+1 = (a*Xn+c)(mod m) 

其中,m>0,0<a<m,0<c<m

这里Xn这个序列生成一系列的随机数,X0是种子。随机数产生的质量与mac三个参数的选取有很大关系。这些随机数并不是真正的随机,而是满足在某一周期内随机分布,这个周期的最长为m。根据Hull-Dobell Theorem,当且仅当:

1. cm互素;

2. a-1可被所有m的质因数整除;

3. m4的整数倍,a-1也是4的整数倍;

4. aX0c都比m;

5.ac都是正整数

周期为m。所以m一般都设置的很大,以延长周期。

现在我们回过头来看刚才的程序,注意这行代码:nextseed = (oldseed * multiplier + addend) & mask。和Xn+1=(a*Xn+c)(mod m)的形式很像有木有!其中multiplieraddend分别代表公式中的ac,很好理解,但mask代表什么呢?其实,x & [(1L << 48)–1] xmod 2^48)等价。我们将x2^N取余操作希望达到的目的可以理解为:所有比2^N位(包括2^N那一位)全都为0,所有比2^N低的位保持原样。因此, x & ((2^N-1)与xmod 2^N)运算等价。讲明白了这个与运算的含义,我想上面那行代码的含义应该很明了了,就是线性同余公式的直接套用,其中a = 0x5DEECE66DL, c = 0xBL, m = 2^48,就可以得到一个48位的随机数,而且这个谨慎的工程师进行了迭代,增加结果的随机性。再把结果移位,就可以得到指定位数的随机数。

nextInt的另一种情况:

public int nextInt(int n) {    if (n <= 0)        throw new IllegalArgumentException("n must be positive");    if ((n & -n) == n)  // i.e., n is a power of 2        return (int)((n * (long)next(31)) >> 31);    int bits, val;    do {        bits = next(31);        val = bits % n;    } while (bits - val + (n-1) < 0);    return val;}


可知范围是[0,n)

0 0