网站首页 > java教程 正文
伪随机(preundorandom):通过算法产生的随机数都是伪随机!!
只有通过真实的随机事件产生的随机数才是真随机!!比如,通过机器的硬件噪声产生随机数、通过大气噪声产生随机数
Random生成的随机数都是伪随机数!!!
是由可确定的函数(常用线性同余),通过一个种子(常用时钟),产生的伪随机数。这意味着:如果知道了种子,或者已经产生的随机数,都可能获得接下来随机数序列的信息(可预测性)
Random类拥有两个构造方法,用于实现随机数生成器:
- Random( ) 构造一个随机数生成器,种子是 与nanoTime异或后的值。每遍输出的多个序列均不同。随机性更强。
- Random(long seed) 用种子seed构造一个随机数生成器,种子是给定的。每遍输出的多个序列均相同。
源码:
/**
* Creates a new random number generator. This constructor sets
* the seed of the random number generator to a value very likely
* to be distinct from any other invocation of this constructor.
*/
public Random() {
this(seedUniquifier() ^ System.nanoTime());//与System.nanoTime()异或
//这里System.nanoTime();并不是以纳秒为单位的系统时间,只用来计算花费多少时间用的。与时间的概念无关。
}
private static long seedUniquifier() {
// L'Ecuyer, "Tables of Linear Congruential Generators of
// Different Sizes and Good Lattice Structure", 1999
for (;;) {
long current = seedUniquifier.get();
long next = current * 181783497276652981L;
if (seedUniquifier.compareAndSet(current, next))
return next;
}
}
private static final AtomicLong seedUniquifier
= new AtomicLong(8682522807148012L); //种子分配器
/**
* Creates a new random number generator using a single {@code long} seed.
* The seed is the initial value of the internal state of the pseudorandom
* number generator which is maintained by method {@link #next}.
*
* <p>The invocation {@code new Random(seed)} is equivalent to:
* <pre> {@code
* Random rnd = new Random();
* rnd.setSeed(seed);}</pre>
*
* @param seed the initial seed
* @see #setSeed(long)
*/
public Random(long seed) {//无参构造,有参构造,均是调用这个方法
if (getClass() == Random.class)// Random random1 = new Random();或者 Random random2 = new Random(100); 均是调用这个if
this.seed = new AtomicLong(initialScramble(seed));//处理后,seed值没变
else {
// subclass might have overriden setSeed
this.seed = new AtomicLong();
setSeed(seed);
}
}
private static long initialScramble(long seed) {
return (seed ^ multiplier) & mask;
}
一、无参构造方法(不设置种子)
虽然表面上看我们未设置种子,但Random构造方法里有一套自己的种子生成机制,源码如上。
生成种子过程:(参考解密随机数生成器(二)——从java源码看线性同余算法)
- 获得一个长整形数作为“初始种子”(系统默认的是8682522807148012L)
- 不断与一个变态的数——181783497276652981L相乘(天知道这些数是不是工程师随便滚键盘滚出来的-.-)得到一个不能预测的值,直到 能把这个不能事先预期的值 赋给Random对象的静态常量seedUniquifier 。因为多线程环境下赋值操作可能失败,就for(;;)来保证一定要赋值成功
- 与系统随机出来的nanotime值作异或运算,得到最终的种子
nanotime算是一个随机性比较强的参数,用于描述代码的执行时间。源码中关于nanotime的描述(部分):
并不是以纳秒为单位的系统时间,只用来计算花费多少时间用的。与时间的概念无关。
/**
* Returns the current value of the running Java Virtual Machine's
* high-resolution time source, in nanoseconds.
*
* <p>This method can only be used to measure elapsed time and is
* not related to any other notion of system or wall-clock time.
* The value returned represents nanoseconds since some fixed but
* arbitrary <i>origin</i> time (perhaps in the future, so values
* may be negative). The same origin is used by all invocations of
* this method in an instance of a Java virtual machine; other
* virtual machine instances are likely to use a different origin.
*
* <p>This method provides nanosecond precision, but not necessarily
* nanosecond resolution (that is, how frequently the value changes)
* - no guarantees are made except that the resolution is at least as
* good as that of {@link #currentTimeMillis()}.
*
* <p>Differences in successive calls that span greater than
* approximately 292 years (2<sup>63</sup> nanoseconds) will not
* correctly compute elapsed time due to numerical overflow.
*
* <p>The values returned by this method become meaningful only when
* the difference between two such values, obtained within the same
* instance of a Java virtual machine, is computed.
*
* <p> For example, to measure how long some code takes to execute:
* <pre> {@code
* long startTime = System.nanoTime();
* // ... the code being measured ...
* long estimatedTime = System.nanoTime() - startTime;}</pre>
*
* <p>To compare two nanoTime values
* <pre> {@code
* long t0 = System.nanoTime();
* ...
* long t1 = System.nanoTime();}</pre>
*
* one should use {@code t1 - t0 < 0}, not {@code t1 < t0},
* because of the possibility of numerical overflow.
*
* @return the current value of the running Java Virtual Machine's
* high-resolution time source, in nanoseconds
* @since 1.5
*/
public static native long nanoTime();
二、有参构造方法(设置种子)
语法:Random ran = Random(long seed)
有参构造方法的源码如上。
其中的multiplier和mask都是定值:
private static final long multiplier = 0x5DEECE66DL;
private static final long mask = (1L << 48) - 1;
三、代码测试
分别采用有参和无参两种方法,生成[0, 100)内的随机整数,各生成五组,每组十个随机数:
import java.util.Random;
public class RandomTest {
public static void main(String[] args) {
RandomTest rt = new RandomTest();
rt.testRandom();
}
public void testRandom(){
System.out.println("Random不设置种子:");
for (int i = 0; i < 5; i++) {
Random random = new Random();
for (int j = 0; j < 10; j++) {
System.out.print(" " + random.nextInt(100) + ", ");
}
System.out.println("");
}
System.out.println("");
System.out.println("Random设置种子:");
for (int i = 0; i < 5; i++) {
Random random = new Random();
random.setSeed(100);
for (int j = 0; j < 10; j++) {
System.out.print(" " + random.nextInt(100) + ", ");
}
System.out.println("");
}
}
}
运行结果如下:
结论:
虽然二者都是伪随机,但是,无参数构造方法(不设置种子)具有更强的随机性,能够满足一般统计上的随机数要求。使用有参的构造方法(设置种子)无论你生成多少次,每次生成的随机序列都相同,名副其实的伪随机!!
四、为何要使用种子?
种子就是生成随机数的根,就是产生随机数的基础。计算机的随机数都是伪随机数,以一个真随机数(种子)作为初始条件,然后用一定的算法不停迭代产生随机数。Java项目中通常是通过Math.random方法和Random类来获得随机数。
Random类中不含参构造方法每次都使用当前时间作为种子,而含参构造方法是以一个固定值作为种子。
随机数是种子经过计算生成的。
不含参的构造函数每次都使用当前时间作为种子,随机性更强
随机数的生成是从种子值开始。 如果反复使用同一个种子,就会生成相同的数字系列,产生不同序列的一种方法是使种子值与时间相关。
五、System.nanoTime()
System.out.println(System.nanoTime());//并不是以纳秒为单位的系统时间
System.out.println(System.nanoTime());
System.out.println(System.nanoTime());
System.out.println(System.currentTimeMillis());
// 第一遍运行:
// 281092180853691
// 281092180911666
// 281092180936920
// 1610767387212 => 2021-01-16 11:23:07
// 第二遍运行:
// 281207286912695
// 281207286952070
// 281207286970745
// 1610767502318
以上运行、源码基于jdk1.8
end
猜你喜欢
- 2024-09-08 JavaWeb项目各种随机数主键ID的代码范例供大家参考学习
- 2024-09-08 Java中List集合有哪些特性?Java开发常见集合
- 2024-09-08 这么一篇.Java性能权威指南.不需要好好的了解一下吗?
- 2024-09-08 一篇文章彻底弄懂CAS实现SSO单点登录原理
- 2024-09-08 Java练习:输出并统计水仙花数、猜数字小游戏
- 2024-09-08 Javaweb代码创建JESESSION32位随机数及session最大不活动时间
- 2024-09-08 Java 中生成一组不重复随机整数的简便方法
- 2024-09-08 JDK 17 - Java 17 的新特性速览(java的什么特性实现了软件开发人员一次编写)
- 2024-09-08 Java教学:Integer、日期类、数字类、随机数、枚举,一次搞定!
- 2024-09-08 Java程序员面试宝典:用这100个问答搞定面试官
你 发表评论:
欢迎- 最近发表
-
- 多种负载均衡算法及其Java代码实现
- 输入www.baidu.com背后经历了啥?说清楚这个,已经超过90%的人了
- 优化MySQL:为什么你应该用 UNSIGNED INT 存储IP地址
- 实模式下CPU如何获取数据及指令(实模式寻址方式)
- java基础都在这了,小主们拿去吧(java基础是指什么)
- 盘点爬虫语言为何选择Python而不是Java
- 搭载Dubbo+Zookeeper踩了这么多坑,我终于决定写下这篇
- 网络协议之TCP/IP协议(面试必考内容) - javaEE初阶 - 细节狂魔
- 深夜报警!10亿次请求暴击,如何用Redis找出最热IP?
- VPN技术(IPsec/L2TP/SSLVPN/PPTP)学习笔记
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)