java的随机数我们用的很多,但是java随机数的发生器是什么原理呢?一直没有搞明白,最近搞了一下贴出来看看
概念
1、随机数:新的随机数与上一个数无关,不可追溯,不可预测
2、随机数发生器:产生随机数的方法
3、真正的随机数:真正的随机数一般都是由物理现象产生的
4、伪随机数:是由固定的、可以重复计算的算法产出
伪随机数发生器的评判标准
1.符合统计学的平均性---均匀性
2.相同序列的概率非常低---随机性
3.不应该能够从一段序列猜测出随机数发生器的工作状态或者下一个随机数---不可预测
4.不应该从随机数发生器的状态能猜测出随机数发生器以前的工作状态---不可回溯
一般用的随机数发生器满足1和2就可以了,对密码学上使用的随机数发生器还需要满足3和4
用的比较多的是线性同余法
线性同余方法(LCG)
同余:两个整数a、b,若它们除以整数m所得的余数相等,则称a与b对于模m同余
数学表示为:a ≡ b (mod m)
LCG:根据递归公式
xn+1≡ (axn+ c ) (mod m) a,c,m是常数
给定x0产生的随机数列{xi}称为LCG{a,c,m,x0}序列
当c ≠ 0时为混合同余法(MLCG)
当c = 0时为乘同余法(PMLCG)
JAVA API中用的是混合同余法
LCG序列的周期性
对xn+1≡ (7xn+ 7 ) (mod 10) 在x0=7时得到
的序列是:7,6,9,0,7,6,9,0,7…..
从列子中可以看出LCG序列是有周期性的,我们把xk=x0时的最小正整数k称为序列的周期d,LCG序列的最大周期不可能大于除数m,当d=m时,称该序列达到满周期(最大周期)
LCG达到满周期的条件
要令LCG达到满周期,应符合以下条件:
1.c,m互质
2.m的所有质因子的积能整除a-1
3.若m是4的倍数,a-1也是
4.a,c,x0都比m小
5.a,c是正整数
达到满周期说明LCG序列是均匀的,并且不易重复(取决于m的大小),因为达到满周期说明一个循环里每一个数据都被取到,是均匀的,并且在一个周期内都不会重复
LCG的效能
效能:具有满周期的线性同余序列的效能被定义使得(a-1)s≡ 0 (mod m)的最小整数s
当满足满周期的条件时,s总是存在的,因为b是m的每个素数的倍数
根据经验和实际检验的效果当效能达到5时才能保证随机性
证明~~请参看《计算机程序设计艺术(第二卷)》的3.2.1.3节
JAVA的随机数
常用的随机数类java.util.Random(Math.random也使用此类),使用混合线性同余法生成随机数
混合线性同余法主要的4个初始化参数:
a=25214903917(乘数multiplier)
c=11(增量addend)
m=2^48(模数mask)
x0=种子seed
其中a、c、m满足满周期的条件,同时效能是24,说明均匀性和随机性都很好
因为 a,c,m 的参数都一样,所以相同的种子就会产生相同的随机序列默认的种子是用时间+增长序列,同一个jvm里random的初始种子几乎不会重复,请看源码:
public Random() { this(++seedUniquifier + System.nanoTime()); }
private static volatile long seedUniquifier = 8682522807148012L;
这个类中最重要的方法是
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));
}
从源码中我们可以看出,random只产生int型的随机数,并且保证了同一个对象即使在多线程情况下也不会产生一样的随机数,产生的int随机数是从高位开始截取的,也就是保留高位,去掉低位
nextInt方法
public int nextInt() {
return next(32);
}
nextLong方法是两个nextInt加起来的
public long nextLong() {
return ((long)(next(32)) << 32) + next(32);
}
nextDouble()方法是两个27位的随机数相加,然后除以2^54
public double nextDouble() {
return (((long)(next(26)) << 27) + next(27)) / (double)(1L << 53);
}