Java函数库中的Random函数如何生成随机数?
在Java中,Random是一个伪随机数生成器类。伪随机数生成器就是通过一种算法来生成一组看似随机的数字序列,而这组序列是可以重复的。因此,Random生成的随机数并不是真正的随机数,但在实际应用中已经足够满足需求。
Random类提供了多种生成不同类型随机数的方法,其中最常用的方法是nextDouble()和nextInt()。下面我们将分别介绍这两种方法的工作原理。
1. nextDouble()方法
在nextDouble()方法中,Random类将调用下面的方法来生成一个随机的double类型的数值:
protected int next(int bits) {
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
return (int)(seed >>> (48 - bits));
}
该方法中的seed是一个long整型数值,它的值是由Random类的构造函数随机生成的。每次调用next()方法时,seed都会被改变,因此生成的随机数是不同的。
next()方法的作用是通过数学运算改变seed的值,并返回一个int类型的值,该值的范围是0到2的bits次方减1。如果bits为16,则返回的值的范围是0到65535。根据位运算的原理,从seed中取得的值的前48位可以通过右移运算得到一个int类型的随机数,该随机数的范围是0到2的48次方减1。
由于double类型的数值取值范围很大,因此调用nextDouble()方法时,Random会先生成两个int类型的随机数,然后将它们拼接成一个64位的long类型数值。此后,再将该数值转换成0到1之间的double型随机数,如下所示:
public double nextDouble() {
return (((long)next(26) << 27) + next(27))
/ (double)(1L << 53);
}
该方法中的next(26)和next(27)分别提供了两个26位和27位的随机数,其中27位的随机数通过位运算处理。最后,将64位的数值除以一个大于0,小于1的double型的数值1L << 53,得到一个0到1的double型的随机数值。
2. nextInt()方法
nextInt()方法生成int类型的随机数与nextDouble()方法生成double类型的随机数的实现原理类似。在nextInt()方法中,Random会先生成一个64位的long类型的随机数,然后使用移位运算将其转换成一个0到2的31次方减1的随机数。
public int nextInt() {
return next(32);
}
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException("Bound must be positive");
if ((bound & -bound) ==
// i.e., bound is a power of 2
return (int)((bound * (long)next(31)) >> 31);
}
当使用nextInt(int bound)时,Random会先判断bound是否是2的n次幂。如果是2的n次幂,Random会调用next(31)方法,生成一个31位的随机数,然后使用移位运算将其转化为0到bound-1的随机数。如果bound不是2的n次幂,则会调用一个循环结构,每次调用next(31)方法生成一个31位的随机数,直到生成的数值小于bound-1为止,然后返回该数值作为随机数。
总的来说,Java的Random函数库是通过算法来生成的“伪随机数”,而非真正的随机数。由于其种子值是不稳定的,所以可以通过某种算法得到一些看起来随机的数字序列。但在实际应用中,它已经可以满足大多数应用程序的需求了。
