Java Random源码剖析

Java中,对随机最基本的支持是Math类中的静态方法random(),它生成一个0~1的随机数,类型为double,包括0但不包括1。

System.out.println(Math.random());

可以发现输出结果每次都会不一样。

那么Math.random()是如何实现的呢?我们来看相关代码

private static Random randomNumberGenerator;private static synchronized Random initRNG() {Random rnd = randomNumberGenerator;return (rnd == null) ? (randomNumberGenerator = new Random()) : rnd;}public static double random() {Random rnd = randomNumberGenerator;if (rnd == null) rnd = initRNG();return rnd.nextDouble();
}

内部使用了一个Random类型的静态变量randomNumberGenerator,调用random()就是调用该变量的nextDouble()方法,这个Random变量只有在第一次使用的时候才创建。

下面我们来仔细看看Random类,位于java.util下。

Random类提供了更为丰富的随机方法,它的方法不是静态方法,使用Random,先要创建一个
Random实例。

Random r = new Random();
System.out.println(r.nextInt());
System.out.println(r.nextInt(100));

nextInt()产生一个随机的int,可能为正数,也可能为负数,nextInt(100)产生一个随机int范围是0~100,包括0不包括100。除了nextInt,还有一些别的方法:

public long nextLong()//随机生成一个long
public boolean nextBoolean() //随机生成一个boolean
public void nextBytes(byte[] bytes)//产生随机字节,字节个数就是bytes的长度
public float nextFloat()//随机浮点数,从0到1,包括0不包括1
public double nextDouble()//随机浮点数,从0到1,包括0不包括1

除了默认构造方法,Random类还有一个构造方法,可以接受一个long类型的种子参数:

public Random(long seed)

种子决定了随机产生的序列,种子相同,产生的随机数序列就是相同的。所以说Random产生的随机数不是真正的随机数,相反,它产生的随机数一般称为伪随机数。真正的随机数比较难以产生,计算机程序中的随机数一般都是伪随机数。
伪随机数都是基于一个种子数的,然后每需要一个随机数,都是对当前种子进行一些数学运算,得到一个数,基于这个数得到需要的随机数和新的种子。数学运算是固定的,所以种子确定后,产生的随机数序列就是确定的,确定的数字序列当然不是真正的随机数,但种子不同,序列就不同,每个序列中数字的分布也都是比较随机和均匀的,所以称之为伪随机数。
Random的默认构造方法中没有传递种子,它会自动生成一个种子,这个种子数是一个真正的随机数,如下所示

private static final AtomicLong seedUniquifier= new AtomicLong(8682522807148012L);public Random() {this(seedUniquifier() ^ System.nanoTime());}private static long seedUniquifier() {for(;;) {long current = seedUniquifier.get();long next = current * 181783497276652981L;if(seedUniquifier.compareAndSet(current, next))
} 

种子是seedUniquifier()与System.nanoTime()按位异或的结果,System.nanoTime()返回一个更高精度(纳秒)的当前时间,seedUniquifier()返回当前seedUniquifier(current变量)与一个常数181783497276652981L相乘的结果(next变量),然后,设置seedUniquifier的值为next,使用循环和compareAndSet都是为了确保在多线程的环境下不会有两次调用返回相同的值,保证随机性。

有了种子数之后,其他数是怎么生成的呢?我们来看下面的代码:

public int nextInt() {return next(32);}public long nextLong() {return ((long)(next(32)) << 32) + next(32);}public float nextFloat() {return next(24) / ((float)(1 << 24));}public boolean nextBoolean() {return next(1) != 0;}

它们都调用了next(intbits),生成指定位数的随机数,我们来看下它的代码:

private static final long multiplier = 0x5DEECE66DL;private static final long addend = 0xBL;private static final long mask = (1L << 48) - 1;protected int next(int bits) {long oldseed, nextseed;AtomicLong seed = this.seed;do {oldseed = seed.get();nextseed = (oldseed * multiplier + addend) & mask;} while (!seed.compareAndSet(oldseed, nextseed));return (int)(nextseed >>> (48 - bits));
}

简单地说,就是使用了如下公式:nextseed = (oldseed * multiplier + addend) & mask

旧的种子(oldseed)乘以一个数(multiplier),加上一个数addend,然后取低48位作为结果(mask相与)。

这个方法的名称叫线性同余随机数生成器。通过一个递推公式生成一个序列的数字,看起来像是随机分布的数列。该公式的形式为:Xn+1 = (a * Xn + c) mod m。这种生成器的特点是周期性,经过一定数量的迭代之后,生成的序列会开始重复。因此,在使用线性同余随机数生成器时,需要选择合适的参数(a、c、m)来确保生成的随机数序列具有较长的周期性。

总之,Java中随机数基于一个种子,种子固定,随机数序列就固定。而在默认构造方法中,种子是一个真正的随机数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/687700.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】进程的初步认识(一)

进程的初步认识 基本概念描述进程task_struct-PCB的一种task_stuct内容分类 查看进程通过系统调用获取进程标识符 基本概念 要了解进程&#xff0c;首先我们要知道两点 我们可以同时启动多个程序&#xff0c;也就意味着我们可以将多个.exe文件加载到内存操作系统如何去管理这些…

Open CASCADE学习|布尔运算后消除内部拓扑

在CAD建模中&#xff0c;布尔运算是一种逻辑运算方法&#xff0c;通过这种方法&#xff0c;可以创建、修改或组合几何对象。布尔运算主要包括并集&#xff08;UNION&#xff09;、交集&#xff08;INTERSECT&#xff09;和差集&#xff08;SUBTRACT&#xff09;三种运算。 并集…

【Java前端技术栈】ES6-ECMAScript6.0

一、ES6基本介绍 1. ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准&#xff0c; 2015 年 6 月发布。 2. ES6 设计目标&#xff1a;达到 JavaScript 语言可以用来编写复杂的大型程序&#xff0c;成为企业级开发语 言 3. ECMAScript 和 JavaScript 的关系&…

Java多线程系列——概述

简介 在计算机编程中&#xff0c;多线程是一种重要的概念&#xff0c;允许程序同时执行多个任务&#xff0c;提高程序的效率和性能。Java作为一门广泛应用于软件开发的编程语言&#xff0c;也提供了丰富的多线程支持。本文将简要介绍Java多线程的基本概念、使用方法以及相关特…

71从零开始学Java之Properties配置类怎么用?

作者:孙玉昌,昵称【一一哥】,另外【壹壹哥】也是我哦 CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 在前面的几篇文章中,壹哥给大家讲解了Java里的List、Set和Map等集合。所以现在我们知道,Map集合作为一个双列集合,有key和value两个值,这两个值共同组成…

数据所在,计算随行:Databend 的 2023 年度总结

2023 年是 Databend 为用户和客户全面交付 Data Cloud 数据云平台的一年&#xff0c;真正实现了「数据所在&#xff0c;计算随行」的理念&#xff0c;即将计算力带至数据之处&#xff0c;致力于为用户交付更澎湃的算力。 Databend 自 2021 年开始研发&#xff0c;「三年之期已…

Redis篇----第五篇

系列文章目录 文章目录 系列文章目录前言一、redis的过期策略以及内存淘汰机制二、Redis 常见性能问题和解决方案?三、为什么Redis的操作是原子性的,怎么保证原子性的?四、Redis事务前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家…

每日OJ题_二叉树dfs①_力扣2331. 计算布尔二叉树的值

目录 力扣2331. 计算布尔二叉树的值 解析代码 力扣2331. 计算布尔二叉树的值 2331. 计算布尔二叉树的值 难度 简单 给你一棵 完整二叉树 的根&#xff0c;这棵树有以下特征&#xff1a; 叶子节点 要么值为 0 要么值为 1 &#xff0c;其中 0 表示 False &#xff0c;1 表示…

京东电商API接口|Python爬虫实战 | 批量爬取网页信息

爬虫爬取网页有时需要模拟网页行为&#xff0c;比如京东、淘宝详情页面&#xff0c;图片加载随着滚动自动加载的。这种情况我们就要进行浏览器模拟操作才能获取要爬取的数据。 Selenium 是一个用于自动化浏览器操作的开源框架&#xff0c;主要用于网页测试&#xff0c;支持多种…

C++ STL:deque使用及源码剖析

Deque是一种双向开口的连续线性空间。能在头尾两端分别做元素的插入和删除&#xff0c;而且是在常数的时间内完成。虽然Vector也可以在首端进行元素的插入和删除&#xff08;利用insert和erase&#xff09;&#xff0c;但效率差&#xff08;涉及到整个数组的移动&#xff09;&a…

JS进阶——JS闭包

JavaScript 闭包 (w3school.com.cn) JavaScript中的闭包&#xff08;Closure&#xff09;是一个非常重要的概念&#xff0c;它涉及到函数作用域和变量引用的深入理解。 闭包的形成主要依赖于两个特性&#xff1a;函数嵌套和函数内部的变量引用。当一个内部函数引用了其外部函…

【Day44】代码随想录之动态规划完全背包_518. 零钱兑换 II_377. 组合总和 Ⅳ

文章目录 动态规划理论基础动规五部曲&#xff1a;出现结果不正确&#xff1a; 518. 零钱兑换 II377. 组合总和 Ⅳ 动态规划理论基础 动规五部曲&#xff1a; 确定dp数组 下标及dp[i] 的含义。递推公式&#xff1a;比如斐波那契数列 dp[i] dp[i-1] dp[i-2]。初始化dp数组。…

代码随想录 Leetcode56. 合并区间

题目&#xff1a; 代码(首刷自解 2024年2月18日&#xff09;&#xff1a; 这题与气球扎针&#xff0c;删除重复的大体逻辑相似。需要额外定义些变量来存储头尾 class Solution { private:const static bool cmp(vector<int>& a, vector<int>& b) {return …

001 QGIS介绍

Quantum GIS&#xff08;QGIS&#xff09;是开源地理信息系统桌面软件&#xff0c;使用GNU&#xff08;General Public License&#xff09;授权&#xff0c; 属于 Open Source eospatial Foundation&#xff08;OSGeo&#xff09;的官方计划。在 GNU 授权下&#xff0c;开发者…

Postman路径修改

默认安装好Postman之后&#xff0c;默认路径在&#xff1a;C:\Users\用户名\AppData\Local\Postman。 修改路径只需要将整个文件夹拷贝到需要移动的位置即可&#xff0c;然后重新创建一个快捷方式。再删除原来路径的文件夹。

使用消息中间件实现系统间的异步通信和解耦

​​​​​​​目录 引言 一. 选择合适的消息中间件 二. 定义消息格式和通信协议 1. 定义消息格式 消息头 消息体 2. 定义通信协议 发送消息 接收消息 消息处理 3. 示例代码 定义消息格式 发送消息 接收消息 三、发布-订阅模式 1. 定义发布-订阅模式 2. 示例代…

C++ //练习 7.29 修改你的Screen类,令move、set和display函数返回Screen并检查程序的运行结果,在上一个练习中你的推测正确吗?

C Primer&#xff08;第5版&#xff09; 练习 7.29 练习 7.29 修改你的Screen类&#xff0c;令move、set和display函数返回Screen并检查程序的运行结果&#xff0c;在上一个练习中你的推测正确吗&#xff1f; 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; …

RIP协议详解

​RIP是最早的动态路由协议&#xff0c;虽然已经过时并且很少使用&#xff0c;但是可以通过学习RIP并且和ospf等现在正在使用的路由协议对比&#xff0c;了解其工作原理和过时原因&#xff0c;具有很强的学习性。 一、RIP协议简介 RIP&#xff08;Routing Information Protoc…

(OpenCV)图片拼接

前言 图片拼接在许多领域都有广泛的应用&#xff0c;包括但不限于以下几个方面&#xff1a; 全景摄影&#xff1a;在摄影中&#xff0c;通过将多张照片拼接在一起可以实现全景照片的效果。这在旅游景点、房地产展示等领域有着广泛的应用&#xff0c;能够提供更加生动、真实的视…

Bpmn-js 属性控制

我们可以通过bpmn-js来访问对应的BPMN图例的属性信息。对应的流程图中的每个图例元素&#xff08;如开始、结束、中间/边界事件等都通过businessObject属性存储对基础BPMN元素的引用。业务对象是从BPMN 2.0 XML导入并在导出过程中序列化的实际元素。使用业务对象来读取和写入BP…