为什么说Java中的随机数都是伪随机数?
什么是伪随机数?
1.伪随机数是看似随机实质是固定的周期性序列,也就是有规则的随机。
2.只要这个随机数是由确定算法生成的,那就是伪随机,只能通过不断算法优化,使你的随机数更接近随机。
(随机这个属性和算法本身就是矛盾的)
3.通过真实随机事件取得的随机数才是真随机数。
Java随机数产生方式:
1,使用Math.random()方法来产生一个随机数,这个产生的随机数是0-1之间的一个double,我们可以把他乘以一定的数,比如说乘以100,他就是个100以内的随机。
System.out.println(Math.random());
2,在java.util这个包里面提供了一个Random的类,我们可以新建一个Random的对象来产生随机数,他可以产生随机整数、随机float、随机double,随机long。
Random random=new Random();
System.out.println(random.nextInt(1000));
System.out.println(random.nextDouble());
3,在我们的System类中有一个currentTimeMillis()方法,这个方法返回一个从1970年1月1号0点0分0秒到目前的一个毫秒数,返回类型是long,我们可以拿他作为一个随机数,我们可以拿他对一些数取模,就可以取得随机数,类似的System类还有nanoTime()方法,返回纳秒级时间。
System.out.println(System.currentTimeMillis());
System.out.println(System.nanoTime());
需要说明的是:
java.Math.Random()实际是在内部调用java.util.Random()的:
以下Math.class的源码,可见Math是基于一个java.util.Random单例的。
public static double random() {
return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
private static final class RandomNumberGeneratorHolder {
static final Random randomNumberGenerator = new Random();
}
Random有两种构造方法:
Random():使用一个和当前系统时间对应的相对时间有关的数字作为种子数。
Random(long seed):直接传入一个种子数。
种子的作用是什么?
种子就是产生随机数的第一次使用值,机制是通过一个函数,将这个种子的值转化为随机数空间中的某一个点上,并且产生的随机数均匀的散布在空间中。以后产生的随机数都与前一个随机数有关。
举例:
Random r1 = new Random();
Random r2 = new Random();
//无参构造使用的是参数作为种子数
Random r3 = new Random(100);
Random r4 = new Random(100);
//产生随机数调用nextXXX()方法
System.out.println(r1.nextInt(10));
System.out.println(r1.nextInt(10));
System.out.println(r2.nextInt(10));
System.out.println(r2.nextInt(10));
System.out.println("-----------------");
System.out.println(r3.nextInt(10));
System.out.println(r3.nextInt(10));
System.out.println(r4.nextInt(10));
System.out.println(r4.nextInt(10));
结果:
5
1
4
0
-----------------
5
0
5
0
r3,r4产生的随机数序列是相同的。
Java自带的随机数函数是很容易被黑客破解的,因为黑客可以通过获取一定长度的随机数序列来推出你的seed,然后就可以预测下一个随机数。程序员可以自己编写随机数生成算法并不断优化,安全性会得到提高。
这里给出一个自编的随机数生成方法:
static double rand(double[] seed){//注意传入参数是一个数组类型
double base=256.0;
double u=16.0;
double v=123.2;
double temp1,temp2,temp3;
temp1=u*seed[0]+v;
temp2=(int)(temp1/base);
temp3=temp1-temp2*base;
seed[0]=temp3;
double random=seed[0]/base;
return random;
}
public static void main(String[] args) {
double[] seed={5.0};//必须使用数组传入,因为必须要传地址,才能改变在内存中的值,如果只传入一个double类型,生成出来将始终是一个数
System.out.println(rand(seed));
System.out.println(rand(seed));
System.out.println(rand(seed));
System.out.println(rand(seed));
}
无论是JDK带有的随机数方法还是程序员自己编写的随机数方法,都是通过一个或简单或复杂的算法生成的。因此都是伪随机数,都具有不安全性。因此从这个意义,随机数可以分为一般随机数,逼真随机数和真实随机数。理论上计算机编程是不可能实现真实随机数的。我们能做的只有尽可能的逼真。
例如,使用每次生成随机数,使用系统时间作为seed,就可以相对简单地构建一个不易破解的随机数生成器。
public static int closeRand(int totalNum){
int random;
Random randomGenerator=new Random(System.nanoTime());
random=randomGenerator.nextInt(totalNum);
return random;
}
再比如JDK还提供了java.security.SecurityRandom类,它继承了java.util.Random类但重写了生成方法,可以产生强随机数:
static int secureRand(int n) {
final int offset = 123456; // offset为固定值,避免被猜到种子来源(和密码学中的加salt有点类似)
long seed = System.currentTimeMillis() + offset;
SecureRandom sr;
try {
sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
return sr.nextInt();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return 0;
}
毫无疑问,在降低效率的代价下提升了安全性。
参考资料:
http://www.cnblogs.com/greatfish/p/5845924.html
http://www.iteye.com/topic/872860
http://blog.csdn.net/ultrani/article/details/7818082