JAVA Random随机数发生器在创建时有两种方法:
- 不指定随机数的构造方法Random()。
- 指定随机数的构造方法Random(seed)。
在第一种情况下:
import java.util.Random;
public class test {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Random ra = new Random();
System.out.println(ra.nextInt(10));
}
}
运行三次
产生的结果分别是2,2,8。说明Random在每次运行过程中都会赋可能相同也可能不同随机种子值。
import java.util.Random;
public class test {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Random ra = new Random(10);
System.out.println(ra.nextInt(10));
}
}
运行三次产生的结果分别为3,3,3。说明在设置相同的随机种子值的情况下,产生的随机数是相同的。因为Random类采用的是线性同余算法产生随机数,当确定唯一的种子时,会产生唯一的随机数。
为了产生不同的随机数可以将随机种子改为系统时间,程序如下:
import java.util.Random;
public class test {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Random ra = new Random(System.currentTimeMillis());
System.out.println(ra.nextInt(10));
}
}
但是
当指定随机种子,循环生成10个随机数时,产生的结果却不同:
import java.util.Random;
public class test {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Random ra = new Random(2);
for (int i = 0; i < 10; i++) {
System.out.println(ra.nextInt(10));
}
}
}
result0:
8
2
0
7
9
0
6
9
7
8
result1:
8
2
0
7
9
0
6
9
7
8
虽然两次运行的结果相同,但是,在指定随机种子的情况下,为什么每次循环生成的随机数却不同?
而将随机种子放在for循环中,则产生相同的结果:
import java.util.Random;
public class test {
public static void main(String[] args) {
// TODO 自动生成的方法存根
// Random ra = new Random(2);
for (int i = 0; i < 10; i++) {
Random ra = new Random(2);
System.out.println(ra.nextInt(10));
}
}
}
result:
8
8
8
8
8
8
8
8
8
8
其中的原因为:
在Java的API帮助文档中,总结了一下对这个Random()函数功能的描述:
1、java.util.Random类中实现的随机算法是伪随机,也就是有规则的随机,所谓有规则的就是在给定种(seed)的区间内随机生成数字;
2、相同种子数的Random对象,相同次数生成的随机数字是完全相同的;
3、Random类中各方法生成的随机数字都是均匀分布的,也就是说区间内部的数字生成的几率均等;
下面Random()的两种构造方法
1.Random():创建一个新的随机数生成器。
2.Random(long seed):使用单个 long 种子创建一个新的随机数生成器。
//获取当前时间的毫秒数作为随机数种子
long t = System.currentTimeMillis();
种子数只是随机算法的起源数字
value的值在变化,但是不同的循环,变化的值相同。
其中的到底什么和value变化有关?
大概可以从这个说法得出一些结论:
下面看这样一个C程序:
//rand01.c
#include<dos.h>
static unsigned int RAND_SEED;
unsigned int random(void)
{
RAND_SEED=(RAND_SEED*123+59)%65536;
return(RAND_SEED);
}
void random_start(void)
{
int temp[2];
movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);
RAND_SEED=temp[0];
}
main()
{
unsigned int i,n;
random_start();
for(i=0;i<10;i++)
printf("%u\t",random());
printf("\n");
}
这个程序(rand01.c)完整地阐述了随机数产生的过程:
首先,主程序调用random_start()方法,random_start()方法中的这一句我很感兴趣:
movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);
这个函数用来移动内存数据,其中FP_SEG(far pointer to segment)是取temp数组段地址的函数,FP_OFF(far pointer to offset)是取temp数组相对地址的函数,movedata函数的作用是把位于0040:006CH存储单元中的双字放到数组temp的声明的两个存储单元中。这样可以通过temp数组把0040:006CH处的一个16位的数送给RAND_SEED。
random用来根据随机种子RAND_SEED的值计算得出随机数,其中这一句:
RAND_SEED=(RAND_SEED*123+59)%65536;
是用来计算随机数的方法,随机数的计算方法在不同的计算机中是不同的,即使在相同的计算机中安装的不同的操作系统中也是不同的。我在linux和windows下分别试过,相同的随机种子在这两种操作系统中生成的随机数是不同的,这说明它们的计算方法不同。
现在,我们明白随机种子是从哪儿获得的,而且知道随机数是怎样通过随机种子计算出来的了。那么,随机种子为什么要在内存的0040:006CH处取?0040:006CH处存放的是什么?
学过《计算机组成原理与接口技术》这门课的人可能会记得在编制ROM BIOS时钟中断服务程序时会用到Intel 8253定时/计数器,它与Intel 8259中断芯片的通信使得中断服务程序得以运转,主板每秒产生的18.2次中断正是处理器根据定时/记数器值控制中断芯片产生的。在我们计算机的主机板上都会有这样一个定时/记数器用来计算当前系统时间,每过一个时钟信号周期都会使记数器加一,而这个记数器的值存放在哪儿呢?没错,就在内存的0040:006CH处,其实这一段内存空间是这样定义的:
TIMER_LOW DW ? ;地址为 0040:006CH
TIMER_HIGH DW ? ;地址为 0040:006EH
TIMER_OFT DB ? ;地址为 0040:0070H
时钟中断服务程序中,每当TIMER_LOW转满时,此时,记数器也会转满,记数器的值归零,即TIMER_LOW处的16位二进制归零,而TIMER_HIGH加一。rand01.c中的
movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);
正是把TIMER_LOW和TIMER_HIGH两个16位二进制数放进temp数组,再送往RAND_SEED,从而获得了“随机种子”。
虽然java和C的运行机制不同,但底层可能运作的结果相似,在每次运行时,种子所产生的随机数位置会发生改变,产生不同的值,但是每当重新运行程序时,种子源位置又是相似的,所以地址的变换也是相似的,因此产生的序列是相同的。
import java.util.Random;
public class test {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Random ra = new Random(2);
// Random rb = new Random(System.currentTimeMillis());
for (int i = 0; i < 20; i++) {
System.out.println(ra.nextInt(10));
}
}
}
结果:
8
2
0
7
9
0
6
9
7
8
4
6
4
4
4
6
7
9
0
1
可以看出将循环次数改为20,前10个随机数和循环次数为10时是相同的。