Java中常用的三种产生随机数的方法及其原理详解(currentTimeMills,random,Math.random)
这学期笔者开始学习Java,由于以前有了C做基础,学Java明显可以轻松许多,但是这几天有个问题很苦恼,有几道题目要产生随机数,我百度了一下,发现了多种使用的方法,但大多知识是零星的。于是今天我打算给大家分享一下我总结了的三种在Java里面很常用的方法。如果有任何不恰当的地方,欢迎大家在评论区不吝指出或者是私信我,我会尽力改正,给大家呈现更优质的内容。 |
一.使用currentTimeMills
currentTimeMills返回的是从1970年一月一日零时零分零秒到现在总共的毫秒数,我们可以用这个毫秒数来产生随机数。如何产生呢?
如果要是产生[0,n-1]范围内的随机数,可以这样写:
int randomNum1 = (int)(System.currentTimeMillis()%10);
//用当前时间获得[0,9]之间的随机数
这个函数就能够产生[0,9]范围内的随机数啦!
那我们要是想产生多个随机数怎么办,有的小伙伴可能会想,我写两个不就好了,那我们看看写两个到底行不行:
int randomNum1 = (int)System.currentTimeMillis()%10;
//用当前时间获得[0,9]之间的随机数
int randomNum2 = (int)System.currentTimeMillis()%10;
System.out.println("用当前时间获得的随机数:"+randomNum1+" "+randomNum2);
//输出第一次:用当前时间获得的随机数:-1 -1
//输出第二次:用当前时间获得的随机数:-1 -1
//输出第三次:用当前时间获得的随机数:-9 -9
我把程序运行了三次,但是奇怪的是,程序并没有按照我们所想的那样输出,这是为什么呢?细心地小伙伴可能发现了,上面第一个代码块和第二个代码块有一点细微的差别,第一个代码块System.currentTimeMillis()%10整体打上了括号,而第二个没有,出现这个问题的原因我细细说来。
最最重要的一点,我们把随机数定义成了int型,而System.currentTimeMillis返回的是一个long型的毫秒数,所以我用了强制类型转换(int)要知道,强制类型转换()可是优先级很高的,*,%的优先级都比它低,所以如果不打括号,系统就会把一个long型的值强制转换为int型,因为这个long型的数据的值太大了,以至于超过了int的范围,这个过程中当然会出错了,口说无凭,上代码:
int randomNum1 = (int)(System.currentTimeMillis()%10);
//用当前时间获得[0,9]之间的随机数
int randomNum2 = (int)System.currentTimeMillis()%10;
long randomNum3 = System.currentTimeMillis();
int randomNum4 = (int)System.currentTimeMillis();
System.out.println("打括号:"+randomNum1+"\n不打括号"
+randomNum2+"\n函数返回值"+randomNum3+"\n强制转换后得值"+randomNum4);
/*
打括号:1
不打括号-3
函数返回值1582714328081
强制转换后得值-2128604143
打括号:8
不打括号-6
函数返回值1582714367538
强制转换后得值-2128564686
打括号:3
不打括号-1
函数返回值1582714395723
强制转换后得值-2128536501
*/
上面我三次运行了程序,相信小伙伴们应该知道错误的原因了吧!int的取值是在-2147483648~2147483647之间,显然这个返回的毫秒值完全超过了它的范围!于是我把代码改成这样:
int randomNum1 = (int)(System.currentTimeMillis()%10);
//用当前时间获得[0,9]之间的随机数
int randomNum2 = (int)(System.currentTimeMillis()%10);
//long randomNum3 = System.currentTimeMillis();
//int randomNum4 = (int)System.currentTimeMillis();
System.out.println("用当前时间获得[0,9]之间的随机数:"+randomNum1+" "+randomNum2);
//用当前时间获得[0,9]之间的随机数:5 5
//用当前时间获得[0,9]之间的随机数:7 7
//用当前时间获得[0,9]之间的随机数:8 8
上面的程序同样运行了三次,又出现了奇奇怪怪的效果,我本来是想产生两个随机数,但是这两个随机数怎么老是一样啊!
当我们分析产生随机数的原理之后,很容易想到,因为是对时间进行运算,程序运行所需要的时间是很快的,两条间隔的语句获得时间的返回值相差的时间不到一毫秒,这样就导致了产生的随机数相同,只要把其中一个做一点改动就行,比如把返回值乘以一个数之后再取模,这样就能避免这种情况,上代码:
int randomNum1 = (int)(System.currentTimeMillis()%10);
int randomNum2 = (int)(System.currentTimeMillis()*3%10);
//long randomNum3 = System.currentTimeMillis();
//int randomNum4 = (int)System.currentTimeMillis();
System.out.println("用当前时间获得[0,9]之间的随机数:"+randomNum1+" "+randomNum2);
//用当前时间获得[0,9]之间的随机数:4 2
//用当前时间获得[0,9]之间的随机数:1 3
//用当前时间获得[0,9]之间的随机数:6 8
这样问题就能很好的解决了,至于怎么产生[a.b],范围内的随机数,让我们用数学来算:
(int)(System.currentTimeMillis()%n
原来的范围:[0,n-1] 两边同时加m
(int)(System.currentTimeMillis()%n+m
变化后的范围:[m,m+n-1]
解方程组:a=m; b=m+n-1;
可以知道产生[a,b]范围内的随机数,用(int)(System.currentTimeMillis()%n+m;其中m=a;n=b-a+1;
第一个终于分享完了
二.使用random
用法如下,直接砸代码:
Random random = new Random();
int a = random.nextInt(10);//产生[0,9]的随机数
int b = random.nextInt(10);
int c = random.nextInt(10);
System.out.println("用产生种子的方法获得的随机数:"+a+" "+b+" "+c);
//用产生种子的方法获得的随机数:1 0 4
//用产生种子的方法获得的随机数:5 4 4
//用产生种子的方法获得的随机数:0 5 1
照样,也运行了三次,产生的结果还是蛮好的,我们来解释一下
Random random = new Random();
这一句的作用是产生种子值()里面是可以带数字的,里面的数字就是种子值
如果不带数字的话,那么种子值默认为当前时间的毫秒数
int a = random.nextInt(10);
这一句括号里面的10,意思是产生[0,9]范围内的随机数
上面的程序演示的是不带种子值,那么我们要是带上种子值呢?看代码:
Random random = new Random(1);
int a = random.nextInt(10);//产生[0,9]的随机数
int b = random.nextInt(10);
int c = random.nextInt(10);
System.out.println("种子值为1获得的随机数:"+a+" "+b+" "+c);
//种子值为1获得的随机数:5 8 7
//种子值为1获得的随机数:5 8 7
//种子值为1获得的随机数:5 8 7
Random random = new Random(2);
int a = random.nextInt(10);//产生[0,9]的随机数
int b = random.nextInt(10);
int c = random.nextInt(10);
System.out.println("种子值为2获得的随机数:"+a+" "+b+" "+c);
//种子值为2获得的随机数:8 2 0
//种子值为2获得的随机数:8 2 0
//种子值为2获得的随机数:8 2 0
Random random = new Random(2002);
int a = random.nextInt(10);//产生[0,9]的随机数
int b = random.nextInt(10);
int c = random.nextInt(10);
System.out.println("种子值为2002获得的随机数:"+a+" "+b+" "+c);
//种子值为2002获得的随机数:8 3 7
//种子值为2002获得的随机数:8 3 7
//种子值为2002获得的随机数:8 3 7
三次改变种子值,每次输出三次,每个种子值得到的结果相同,这是因为一旦确定了种子值,在范围内的随机数其实就已经指定了,如果要生成多个或者是多次随机数,最好还是不要指定种子值。
下面要看看怎么产生[a,b]范围内的随机数,照样,我们来解方程:
int a = random.nextInt(n);
原来的范围:[0,n-1] 两边同时加m
int a = random.nextInt(n)+m;
变化后的范围:[m,m+n-1]
解方程组:a=m; b=m+n-1;
可以知道产生[a,b]范围内的随机数,用int a = random.nextInt(n)+m;其中m=a;n=b-a+1;这个范围的计算和第一种方法是一样的!
三.使用Math.random()
Math.random()返回一个double类型的随机数d,其中d的范围为[0,1)
这个函数是这样使用的:
int d = (int)(9 *Math.random())+1;
//产生[1,10)的整数
int e = (int)(9 *Math.random())+1;
System.out.println("用Math.random获得的随机数:"+d+" "+e);
//用Math.random获得的随机数:2 8
//用Math.random获得的随机数:4 6
//用Math.random获得的随机数:2 6
照样运行了三次,下面来解释一下:
int d = (int)(9 *Math.random())+1;
把得到的double类型的数强制转换为int型的数并赋值给int型的变量
至于为什么范围是得到的产生[1,10)的整数的整数,老规矩
数学代换走起
int d = (int)((y-x)*Math.random())+x;
[0,1.0) 同乘y-x
[0,y-x) 两边+x
[x,y)
这样第三个产生随机数的问题我们也解决啦!!!
最后,给大家看看这三种方式放在一起:
public class Test_fot_CSDN {
public static void main(String[] args){
int randomNum1 = (int)(System.currentTimeMillis()%10);
int randomNum2 = (int)(System.currentTimeMillis()*3%10);
//long randomNum3 = System.currentTimeMillis();
//int randomNum4 = (int)System.currentTimeMillis();
System.out.println("用当前时间获得[0,9]之间的随机数:"+randomNum1+" "+randomNum2);
Random random = new Random(2002);
int a = random.nextInt(10);//产生[0,9]的随机数
int b = random.nextInt(10);
int c = random.nextInt(10);
System.out.println("用产生种子的方法获得的随机数:"+a+" "+b+" "+c);
int d = (int)(9 *Math.random())+1;//产生[1,10)的整数
int e = (int)(9 *Math.random())+1;
System.out.println("用Math.random获得的随机数:"+d+" "+e);
}
}
/*
用当前时间获得[0,9]之间的随机数:8 4
用产生种子的方法获得的随机数:8 3 7
用Math.random获得的随机数:6 7
*/
那么,这次的分享就到这啦,希望各位看到我的文章能更加优秀,小伙伴们有什么好的想法,欢迎一起交流哦!