目录
3.2. 使用java.security.SecureRandom生成随机数
一、Random
1. 简介
java Random类位于java.util包下,主要用来生成随机数。
Random类中实现的随机算法是伪随机,也就是有规则的随机。
在进行随机时,随机算法的起源数字称为种子数(seed),在种子数的基础上进行一定的变换,从而产生需要的随机
数字。
相同种子数的Random对象,相同次数生成的随机数字是完全相同的。
也就是说,两个种子数相同的Random对象,第一次生成的随机数字完全相同,第二次生成的随机数字也完全相
同。这点在生成多个随机数字时需要特别注意。
2. 什么是种子
种子就是一个用于初始化一个随机数字生成器的数字,无参构造方法使用当前已经逝去的时间作为种子,创建一
个Random对象。
如果两个Random对象有相同的种子,就会产生相同的数列!
创建一个Random对象的时候,必须指定一个种子或者使用默认的种子。
3. 相关方法
4. Random对象的生成
Random类包含两个构造方法,下面依次进行介绍:
public Random():该构造方法使用一个和当前系统时间对应的相对时间有关的数字作为种子数,然后使用这个种子数构造Random对象。
public Random(long seed):该构造方法可以通过制定一个种子数进行创建。
演示:
Random r = new Random();
Random r1 = new Random(10);
再次强调:种子数只是随机算法的起源数字,和生成的随机数字的区间无关。
5. Random类中的常用方法
Random类中的方法比较简单,每个方法的功能也很容易理解。
需要说明的是,Random类中各方法生成的随机数字都是均匀分布的,也就是说区间内部的数字生成的几率是均
等的。下面对这些方法做一下基本的介绍:
public boolean nextBoolean():该方法的作用是生成一个随机的boolean值,生成true和false的值几率相等,也就是都是50%的几率。
public double nextDouble():该方法的作用是生成一个随机的double值,数值介于[0,1.0)之间。
public int nextInt():该方法的作用是生成一个随机的int值,该值介于int的区间,也就是-231到231-1之间。
public int nextInt(int n):该方法的作用是生成一个随机的int值,该值介于[0,n)的区间,也就是0到n之间的随机int值,包含0而不包含n。
public void setSeed(long seed):该方法的作用是重新设置Random对象中的种子数。设置完种子数以后的Random对象和相同种子数使用new关键字创建出的Random对象相同。
6. 使用
使用Random类,一般是生成指定区间的随机数字,下面就一一介绍如何生成对应区间的随机数字。
6.1. 创建对象
Random r = new Random();
6.2. 生成[0,1.0)区间的小数
double d1 = r.nextDouble();
直接使用nextDouble方法获得。
6.3. 生成[0,5.0)区间的小数
double d2 = r.nextDouble() * 5;
因为nextDouble方法生成的数字区间是[0,1.0),将该区间扩大5倍即是要求的区间。
同理,生成[0,d)区间的随机小数,d为任意正的小数,则只需要将nextDouble方法的返回值乘以d即可。
6.4. 生成[1,2.5)区间的小数
double d3 = r.nextDouble() * 1.5 + 1;
生成[1,2.5)区间的随机小数,则只需要首先生成[0,1.5)区间的随机数字,然后将生成的随机数区间加1即可。
同理,生成任意非从0开始的小数区间[d1,d2)范围的随机数字(其中d1不等于0),
则只需要首先生成[0,d2-d1)区间的随机数字,然后将生成的随机数字区间加上d1即可。
6.5. 生成任意整数
int n1 = r.nextInt();
直接使用nextInt方法即可
6.6. 生成[0,10)区间的整数
int n2 = r.nextInt(10);
n2 = Math.abs(r.nextInt() % 10);
以上两行代码均可生成[0,10)区间的整数。
第一种实现使用Random类中的nextInt(int n)方法直接实现。
第二种实现中,首先调用nextInt()方法生成一个任意的int数字,
该数字和10取余以后生成的数字区间为(-10,10),然后再对该区间求绝对值,则得到的区间就是[0,10)了。
6.7. 生成任意[0,n)区间的随机整数
int n2 = r.nextInt(n);
n2 = Math.abs(r.nextInt() % n);
6.8. 生成[0,10]区间的整数
int n3 = r.nextInt(11);
n3 = Math.abs(r.nextInt() % 11);
相对于整数区间,[0,10]区间和[0,11)区间等价,所以即生成[0,11)区间的整数。
6.9. 生成[-3,15)区间的整数
int n4 = r.nextInt(18) - 3;
n4 = Math.abs(r.nextInt() % 18) - 3;
生成非从0开始区间的随机整数,可以参看上面非从0开始的小数区间实现原理的说明。
6.10. 几率实现
按照一定的几率实现程序逻辑也是随机处理可以解决的一个问题。
下面以一个简单的示例演示如何使用随机数字实现几率的逻辑。
在前面的方法介绍中,nextInt(int n)方法中生成的数字是均匀的,也就是说该区间内部的每个数字生成的几率是
相同的。
那么如果生成一个[0,100)区间的随机整数,则每个数字生成的几率应该是相同的,而且由于该区间中总计有100
个整数,所以每个数字的几率都是1%。
按照这个理论,可以实现程序中的几率问题。
示例:随机生成一个整数,该整数以55%的几率生成1,以40%的几率生成2,以5%的几率生成3。
代码如下:
int n5 = r.nextInt(100);
int m; //结果数字
if(n5 < 55){ //55个数字的区间,55%的几率
m = 1;
}else if(n5 < 95){//[55,95),40个数字的区间,40%的几率
m = 2;
}else{
m = 3;
}
因为每个数字的几率都是1%,则任意55个数字的区间的几率就是55%,
为了代码方便书写,这里使用[0,55)区间的所有整数,后续的原理一样。
当然,这里的代码可以简化,因为几率都是5%的倍数,所以只要以5%为基础来控制几率即可,下面是简化的代码
实现:
int n6 = r.nextInt(20);
int m1;
if(n6 < 11){
m1 = 1;
}else if(n6 < 19){
m1 = 2;
}else{
m1 = 3;
}
在程序内部,几率的逻辑就可以按照上面的说明进行实现。
7. 其它问题
7.1. 相同种子数Random对象问题
前面介绍过,相同种子数的Random对象,相同次数生成的随机数字是完全相同的,下面是测试的代码:
代码如下:
Random r1 = new Random(10);
Random r2 = new Random(10);
for(int i = 0;i < 2;i++){
System.out.println(r1.nextInt());
System.out.println(r2.nextInt());
}
在该代码中,对象r1和r2使用的种子数都是10,则这两个对象相同次数生成的随机数是完全相同的。
如果想避免出现随机数字相同的情况,则需要注意,无论项目中需要生成多少个随机数字,都只使用一个、
Random对象即可。
7.2. 关于Math类中的random方法
其实在Math类中也有一个random方法,该random方法的工作是生成一个[0,1.0)区间的随机小数。
通过阅读Math类的源代码可以发现,Math类中的random方法就是直接调用Random类中的nextDouble方法实
现的。只是random方法的调用比较简单,所以很多程序员都习惯使用Math类的random方法来生成随机数字。
8. 演示案例
8.1. 产生随机数
package com.zheng.d2_random;
import java.util.Random;
/*
Random
作用:
用于产生一个随机数
使用步骤:
1:导包
import java.util.Random;
2:创建对象
Random r = new Random();
3:获取随机数
int number = r.nextInt(10);
获取数据的范围:[0,10) 包括0,不包括10
*/
public class RandomDemo {
public static void main(String[] args) {
//创建对象
Random r = new Random();
//用循环获取10个随机数
for(int i=0; i<10; i++) {
//获取随机数
int number = r.nextInt(10);
System.out.println("number:" + number);
}
//需求:获取一个1-100之间的随机数
int x = r.nextInt(100) + 1;
System.out.println(x);
}
}
8.2. 猜数字
package com.zheng.d2_random;
import java.util.Random;
import java.util.Scanner;
/*
猜数字
需求:
程序自动生成一个1-100之间的数字,使用程序实现猜出这个数字是多少?
当猜错的时候根据不同情况给出相应的提示
如果猜的数字比真实数字大,提示你猜的数据大了
如果猜的数字比真实数字小,提示你猜的数据小了
如果猜的数字与真实数字相等,提示恭喜你猜中了
*/
public class RandomTest {
public static void main(String[] args) {
//要完成猜数字的游戏,首先需要有一个要猜的数字,使用随机数生成该数字,范围1到100
Random r = new Random();
int number = r.nextInt(100) + 1;
while(true) {
//使用程序实现猜数字,每次均要输入猜测的数字值,需要使用键盘录入实现
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要猜的数字:");
int guessNumber = sc.nextInt();
//比较输入的数字和系统产生的数据,需要使用分支语句。这里使用if..else..if..格式,根据不同情况进行猜测结果显示
if(guessNumber > number) {
System.out.println("你猜的数字" + guessNumber + "大了");
} else if(guessNumber < number) {
System.out.println("你猜的数字" + guessNumber + "小了");
} else {
System.out.println("恭喜你猜中了");
break;
}
}
}
}
二、SecureRandom
1. 概述
java.security.SecureRandom类:该类提供了一个加密强随机数生成器(RNG)。加密强随机数最低限度地符合
FIPS 140-2(加密模块的安全要求,第4.9.1节)中规定的统计随机数生成器测试。此外,SecureRandom必须产生
不确定的输出。因此,传递给SecureRandom对象的任何种子材料必须是不可预测的,而且所有SecureRandom
输出序列必须具有强加密能力。
java.util.Random类:Random中定义的类在密码学上不是强的,选择的数字也不是完全随机的,因为使用了确定
的数学算法(基于Donald E. Knuth的减法随机数生成器算法)来选择它们。因此,对于需要较高安全性的任务(如创
建随机密码等)使用该类是不安全的。
2. Random vs SecureRandom
- 大小:一个Random类只有48位,而SecureRandom最多可以有128位。所以在SecureRandom中重复的机会较小。
- 种子生成:随机使用系统时钟作为种子/或生成种子。因此,如果攻击者知道种子产生的时间,它们就可以很容易地繁殖。但SecureRandom从您的OS中获取随机数据(它们可以是按键之间的间隔等-大多数OS收集这些数据并将它们存储在文件中- /dev/ Random和/dev/urandom在linux/solaris的情况下),并将其用作种子。
- 解码:在随机的情况下,只需要2^48次的尝试,在今天先进的cpu,它是有可能在实际时间打破它。但是为了安全起见,随机的2^128次尝试是必须的,这将花费许多年的时间,以今天的先进机器来实现收支平衡。
- 生成函数:标准的Oracle JDK 7实现使用所谓的线性同余生成器在java.util.Random中生成随机值。
Secure Random实现了SHA1PRNG算法,使用SHA1算法生成伪随机数。该算法在一个真正的随机数上计算SHA-1哈希值(使用一个熵源),然后将其与一个64位计数器连接,该计数器在每次操作中递增1。 - 安全性:因此,java.util。不能将随机类用于安全性关键的应用程序或保护敏感数据。
3. 使用
3.1. 使用java.util.Random生成随机数
// A Java program to demonstrate
// random number generation
// using java.util.Random;
import java.util.Random;
public class generateRandom {
public static void main(String args[]){
// create instance of Random class
Random rand = new Random();
// Generate random integers in range 0 to 999
int rand_int1 = rand.nextInt(1000);
int rand_int2 = rand.nextInt(1000);
// Print random integers
System.out.println("Random Integers: " + rand_int1);
System.out.println("Random Integers: " + rand_int2);
}
}
输出:
Random Integers: 956
Random Integers: 678
3.2. 使用java.security.SecureRandom生成随机数
// A Java program to demonstrate secure
// random number generation
// using java.security.SecureRandom
import java.security.SecureRandom;
public class generateRandom {
public static void main(String args[]){
// create instance of SecureRandom class
SecureRandom rand = new SecureRandom();
// Generate random integers in range 0 to 999
int rand_int1 = rand.nextInt(1000);
int rand_int2 = rand.nextInt(1000);
// Print random integers
System.out.println("Random Integers: " + rand_int1);
System.out.println("Random Integers: " + rand_int2);
}
}
输出:
Random Integers: 817
Random Integers: 500