随机数介绍
在程序开发中,我们经常会用到随机数。譬如数字签名、数据加密、以及一些取样的场景下。但需要注意的是,许多开发语言API 所提供的随机函数并非真正意义上的随机,而是伪随机,至于原因,本文最后会进行解释。
jdk中Random类
jdk中提供了Random类供我们使用
// within int range
System.out.println(new Random().nextInt());
// [0,23)
System.out.println(new Random().nextInt(23));
// [0.0,1.0)
System.out.println(new Random().nextDouble());
// [0.0,1.0)
System.out.println(new Random().nextDouble());
// generate true or false with equal probability
System.out.println(new Random().nextBoolean());
种子
值得注意的是Random类中提供了另外一个带参构造函数 Random(long seed)
我们输入下面代码
Random random1 = new Random(23);
for (int i = 0; i < 5; i++) {
System.out.println(random1.nextInt());
}
Random random2 = new Random(23);
for (int i = 0; i < 5; i++) {
System.out.println(random2.nextInt());
}
某次结果如下
-1150482841
1434614297
156591366
825130495
960144037
-------------
-1150482841
1434614297
156591366
825130495
960144037
有读者可能感到困惑,为什么两个Random 独立生成出的数字序列完全相同,对,不必疑惑!即使生成10000次,他们生成的随机数序列也完全相同! 这也是本文开头所说的伪随机。
大部分的开发语言中,随机数产生都采用了线性同余法
数学表达式如下
X(n+1) = (a * X(n) + c) % m
所以,随机数值是其实是通过公式算出来的。当你在构造不同的Random类时传入了相同的种子(Seed)时候,必然会得到相同的数字序列!
我们观察下jdk产生随机数的核心函数:
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));
}
其中(oldseed * multiplier + addend) & mask就是采用了线性同余法。只不过jdk开发者进行了一些处理而已。 而我们使用无参构造函数时,会默认使用时间作为种子,所以,即使定义了若干Random 类,他们产生的随机数序列也不会相同。 好了,今天学习就到这里,下次我们讲讲 Apache Commons Lang 中的 RandomUtils类
转载于:https://blog.51cto.com/codestorm/1831705