模块 java.base

包 java.util.random


java.util.random
此包包含支持用于生成随机数的通用 API 的类和接口。

这些类和接口支持“随机生成器”的定义和使用,该术语涵盖传统上称为“随机数生成器”的术语以及其他种类的随机选择值(例如boolean)的生成器。这些类和接口不仅涵盖确定性(伪随机)算法,还涵盖使用某些“真正随机”物理源的值生成器(例如,可能利用热噪声或量子力学效应的随机算法)。

主要接口是 RandomGenerator ,它提供了请求从均匀分布中伪随机选择的 intlongfloatdoubleboolean 类型的单个值的方法;请求从正态分布或指数分布中伪随机选择的double类型值的方法;以及创建从均匀分布中伪随机选择的 intlongdouble 类型的值流的方法(此类流基于拆分器,允许对其元素进行并行处理)。还有一些静态工厂方法可以根据名称创建特定随机数生成器算法的实例。

主要支持类是RandomGeneratorFactory 。这可用于为特定算法生成多个随机数生成器。 RandomGeneratorFactory 还提供了选择随机数生成器算法的方法。 RandomGeneratorFactory 使用服务提供商 API 注册 RandomGenerator 接口的实现。

一个重要的辅助接口是 RandomGenerator.StreamableGenerator ,它提供了创建基于拆分器的 RandomGenerator 对象流的方法,允许使用多个线程并行处理这些对象。与 Random 不同,RandomGenerator 的大多数实现是not线程安全的。目的是实例不应该在线程之间共享;相反,每个线程都应该有自己的随机生成器来使用。这个包提供的各种伪随机算法被设计成多个实例(以非常高的概率)表现得好像在统计上是独立的。

对于许多用途,这些是伪随机值的消费者将需要的仅有的两个接口。还有一些更专业的接口描述更专业的随机数生成器类别 SplittableGenerator JumpableGenerator LeapableGenerator ArbitrarilyJumpableGenerator 具有创建统计独立实例的特定策略。

使用随机数生成器接口

首先,应用程序应该首先创建一个生成器类的实例。假设已经导入包java.util.random 的内容:
import java.util.random.*;
然后可以通过将生成器算法的名称提供给静态方法 RandomGenerator.of(java.lang.String) 来选择特定的实现,在这种情况下,将使用该实现的无参数构造函数:
RandomGenerator g = RandomGenerator.of("L64X128MixRandom");
对于单线程应用程序,这就是所需要的。然后可以调用 g 的方法,例如 nextLong() nextInt() nextFloat() nextDouble() nextBoolean() 以生成单独的随机选择值。还可以使用方法 ints() longs() doubles() 创建随机选择值的流。 nextGaussian() nextExponential() 方法从非均匀分布中提取浮点值。

对于多线程应用程序,可以重复前面的步骤来创建额外的 RandomGenerators ,但通常最好使用一个最初创建的生成器的方法来创建其他类似的生成器。 (一个原因是一些生成器算法,如果要求一次创建一组新的生成器,可以做出特殊努力以确保新生成器在统计上是独立的。)如果初始生成器实现接口 RandomGenerator.StreamableGenerator ,则方法rngs() 可用于创建生成器流。如果这是一个并行流,那么通过在流上使用map() 方法很容易得到并行执行。

对于动态派生新线程的多线程应用程序,另一种方法是使用实现接口 RandomGenerator.SplittableGenerator 的初始生成器,然后将其视为“属于”初始线程供其独占使用;然后每当任何线程需要fork一个新线程时,它首先使用自己的生成器的split() 方法创建一个新的生成器,然后传递给新创建的线程供该新线程独占使用。

选择随机数生成器算法

Java中提供了三组随机数生成器算法:Legacy组、LXM组和Xoroshiro/Xoshiro组。

遗留组包括 JDK 17 之前存在的随机数生成器:Random、ThreadLocalRandom、SplittableRandom 和 SecureRandom。随机 (LCG) 是可用算法中最弱的,建议用户迁移到较新的算法。如果应用程序需要加密安全的随机数生成器算法,那么它应该继续使用类 SecureRandom 的实例。

LXM 组中的算法彼此相似。每个算法的参数可以在算法名称中找到。 “L”后的数字表示LCG子发生器的状态比特数,“X”后的数字表示XBG子发生器的状态比特数。 “Mix”表示该算法使用了一个8操作位混合函数; “StarStar”表示使用 3 操作位扰码器。

Xoroshiro/Xoshiro 组中的算法是更传统的算法(参见 David Blackman 和 Sebastiano Vigna,“Scrambled Linear Pseudorandom Number Generators”,ACM Transactions on Mathematical Software,2021);名称中的数字表示状态位数。

对于不需要加密安全算法的应用程序(例如物理模拟、机器学习和游戏),此包提供接口 RandomGenerator 的多个实现,这些实现在速度、空间、周期、偶然相关性和等分布属性之间进行权衡。

对于没有特殊要求的应用,L64X128MixRandom在速度、空间、周期上有很好的平衡,使用得当(每个线程单独实例),单线程和多线程应用都适用。

如果应用程序只使用单线程,那么 Xoroshiro128PlusPlus 会更小更快,当然也有足够长的周期。

对于在 32 位硬件环境中运行且仅使用一个线程或少量线程的应用程序,L32X64MixRandom 可能是一个不错的选择。

对于使用在计算开始时在一批中分配的多个线程的应用程序,可以使用诸如 Xoroshiro128PlusPlusXoshiro256PlusPlus 之类的“可跳转”生成器,或者可以使用诸如 L64X128MixRandomL64X256MixRandom 之类的“可拆分”生成器。

对于可能通过使用拆分器动态创建多个线程的应用程序,建议使用“可拆分”生成器,例如 L64X128MixRandomL64X256MixRandom。如果动态创建的生成器的数量可能非常大(数百万或更多),那么使用 L128X128MixRandomL128X256MixRandom 等生成器,它们的 LCG 子生成器使用 128 位参数而不是 64 位参数,将会大大减少可能两个实例使用相同的状态周期。

对于使用连续生成值的元组的应用程序,可能需要使用一个生成器,它是k- 等分布使得k至少与正在生成的元组的长度一样大。生成器 L64X256MixRandom 可证明是 4 等分布的,而 L64X1024MixRandom 可证明是 16 等分布的。

对于生成大排列的应用程序,最好使用周期远大于可能排列总数的生成器;否则将无法生成某些预期的排列。例如,如果目标是洗一副 52 张牌,则可能的排列数为 52! (52 阶乘),大于 2225(但小于 2226), 因此最好使用周期至少为 2256,例如 L64X256MixRandomL64X1024MixRandomL128X256MixRandomL128X1024MixRandom 。 (当然也有必要在初始化生成器时提供足够多的种子位,否则仍然无法生成一些预期的排列。)

可用的随机数生成器算法

这些算法 [在下表中] 必须在当前版本的 Java SE 中找到。特定的 JDK 实现可能会识别其他算法;查看 JDK 的文档以获取详细信息。 Java SE 所需的算法集可能会随着 Java SE 规范的更改而更新。随着时间的推移,可能会添加新算法,也可能会删除旧算法。

此外,作为另一个生命周期阶段,算法可能是弃用。不建议使用已弃用的算法。如果弃用了所需的算法,则可能会在未来的版本中将其删除。由于随机数生成器算法开发和分析的进步,算法可能会在特定 Java SE 版本的生命周期内被弃用。更改算法的弃用状态是not规范更改。

可用算法
Algorithm 团体 Period StateBits 平均分配
L128X1024混装随机 LXM BigInteger.ONE.shiftLeft(1024).subtract(BigInteger.ONE).shiftLeft(128) 1152 1
L128X128混装随机 LXM BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE).shiftLeft(128) 256 1
L128X256混装随机 LXM BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE).shiftLeft(128) 384 1
L32X64混装随机 LXM BigInteger.ONE.shiftLeft(64).减法(BigInteger.ONE).shiftLeft(32) 96 1
L64X1024混装随机 LXM BigInteger.ONE.shiftLeft(1024).subtract(BigInteger.ONE).shiftLeft(64) 1088 16
L64X128混装随机 LXM BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE).shiftLeft(64) 192 2
L64X128星星随机 LXM BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE).shiftLeft(64) 192 2
L64X256混装随机 LXM BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE).shiftLeft(64) 320 4
Random 遗产 BigInteger.ONE.shiftLeft(48) 48 0
SplittableRandom 遗产 BigInteger.ONE.shiftLeft(64) 64 1
ThreadLocalRandom * 遗产 BigInteger.ONE.shiftLeft(64) 64 1
Xoroshiro128PlusPlus Xoroshiro BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE) 128 1
Xoshiro256PlusPlus 细四郎 BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE) 256 3

*ThreadLocalRandom 只能通过 ThreadLocalRandom.current() 访问。

随机数生成算法的分类

从历史上看,大多数伪随机生成器算法都基于某种具有单个大循环状态的有限状态机;当需要让多个线程同时使用相同的算法时,通常的技术是安排每个线程遍历状态循环的不同区域。这些区域可以分配给线程,方法是从一个初始状态开始,然后使用一个“跳转函数”在循环中移动很长的距离(可能是 264步骤或更多);跳转函数被重复和顺序应用,以识别间隔较宽的状态,然后将这些状态分配给每个线程,作为该线程使用的生成器的初始状态。该策略由接口 RandomGenerator.JumpableGenerator 支持。有时需要支持两个级别的跳跃(长距离和真的长距离);该策略由接口 RandomGenerator.LeapableGenerator 支持。在此包中,此接口的实现包括“Xoroshiro128PlusPlus”和“Xoshiro256PlusPlus”。还有一个接口 RandomGenerator.ArbitrarilyJumpableGenerator 用于允许沿状态循环跳转任何用户指定距离的算法;目前这个包中没有这个接口的实现。

最近一类“可拆分”伪随机生成器算法使用了大量的状态循环,并尝试确保不同的实例使用不同的状态循环;但即使两个实例“意外地”使用相同的状态循环,它们也很有可能遍历该共享状态循环的不同区域。该策略由接口 RandomGenerator.SplittableGenerator 支持。在此包中,此接口的实现包括“L32X64MixRandom”、“L64X128StarStarRandom”、“L64X128MixRandom”、“L64X256MixRandom”、“L64X1024MixRandom”、“L128X128MixRandom”、“L128X256MixRandom”和“L128X1024M” ix随机";请注意,SplittableRandom 类也实现了此接口。

LXM 系列随机数生成器算法

LXM 算法的中心 nextLong(或 nextInt)方法的结构遵循 Sebastiano Vigna 在 2017 年 12 月提出的建议,即使用一个线性同余生成器(LCG)作为第一个子生成器,一个基于 Xor 的生成器(XBG)作为第二个子生成器(而不是使用两个 LCG 子发电机)将提供更长的周期、卓越的均衡分布、可扩展性和更好的质量。这里的每个具体实现都结合了当前最知名的 XBG 算法之一(xoroshiro128 或 xoshiro256,Blackman 和 Vigna 在“Scrambled Linear Pseudorandom Number Generators”中描述,ACM 数学软件汇刊2021) 的 LCG 使用目前最知名的乘数之一(由 Steele 和 Vigna 在 2019 年搜索更好的乘数时发现,在“同余伪随机数生成器的计算简单、频谱良好的乘数”中有所描述),软件:实践与经验(2021), doi:10.1002/spe.3030),然后应用 Doug Lea 确定的混合函数或 Blackman 和 Vigna 提出的简单扰码器。测试证实,LXM 算法在质量上远优于 SplittableRandom 使用的 SplitMix 算法 (2014)(参见 Steele 和 Vigna,“LXM:更好的可拆分伪随机数生成器(和几乎一样快)”,过程。 2021 ACM OOPSLA 会议).每个类的名称形式为 LpX qSomethingRandom 使用 LXM 随机数算法家族的一些特定成员; “LXM”是“LCG、XBG、Mixer”的缩写。每个 LXM 生成器都有两个子生成器;一个是 LCG(线性同余生成器),另一个是 XBG(异或生成器)。 LXM 生成器的每个输出都是使用混合函数将来自 LCG 的状态与来自 XBG 的状态组合的结果(然后 LCG 的状态和 XBG 的状态被推进)。

LCG 子生成器具有 s = m*s + a 形式的更新步骤,其中 sma 都是相同大小的二进制整数,每个都有p位; s 是可变状态,乘数 m 是固定的(对于类的所有实例都相同),加数 a 是参数(实例的最终字段)。参数 a 要求为奇数(这允许 LCG 具有最大周期,即 2p);因此有 2p− 1不同的参数选择。 (当s的大小为128位时,那么我们在下面使用名称“sh”来指代s的高半部分,即s的高64位。)

XBG 子生成器原则上可以是多种 XBG 算法中的任何一种;在此包中,它始终是 xoroshiro128xoshiro256xoroshiro1024 ,在每种情况下都没有任何最终扰码器(例如“+”或“**”),因为 LXM 在稍后的过程中使用单独的混音器。 XBG 状态由一些固定数量的 intlong 字段组成,通常命名为 x0x1 等,它们可以取任何值,前提是它们不全为零。这些字段的总大小是q位;因此这个子发电机的周期是 2q- 1。

因为时期 2p和 2q两个子生成器中的 −1 是互质的,LXM 算法的任何单个实例的period(生成值序列在重复之前的长度)是子生成器周期的乘积,即 2p(2q− 1), 略小于 2(p+q).此外,如果同一 LXM 算法的两个不同实例具有不同的a参数,那么它们产生的值的周期将不同。

一般来说,在“LpX q" 生成器,一个实例所需的内存是 2p+q位。 (如果q为 1024 或更大,XBG 状态表示为数组,因此数组对象头需要额外的位,另外 32 位用于数组索引。)

较大的值p意味着两个不同的实例将遍历相同状态循环的概率较低,并且较大的值q暗示生成器在更大的维度上是均匀分布的(当p是 64,并且推测当p是 128)。名称中带有“Mix”的类使用了相当强大的混合功能,具有优良的雪崩特性;名称中带有“StarStar”的类使用较弱但速度较快的混合功能。

此包中使用的特定 LXM 算法都是经过选择的,以便 nextLong() 方法生成的 64 位值完全均匀分布(例如,对于“L64X128MixRandom”的任何特定实例,在其循环过程中,每个 264将产生可能的 long 值 2128−1 次)。 nextInt() nextFloat() nextDouble() 方法生成的值同样完全等分布。一些算法提供了进一步的保证k- 一些人的平均分配k大于1,表示连续不重叠k-由 nextLong() 方法生成的 64 位值的元组完全是等分布的(同样可能发生)。

下表给出了周期、状态大小(以位为单位)、参数大小(以位为单位,包括需要始终为 1 位的低位)以及每个特定 LXM 算法中使用的等分布属性这个包。

算法属性
执行 Period 状态大小 参数大小 nextLong() 值是
“L32X64MixRandom” 232(264− 1) 96位 32位
"L64X128StarStarRandom" 264(2128− 1) 192位 64位 2-等分布和完全等分布
“L64X128MixRandom” 264(2128− 1) 192位 64位 2-等分布和完全等分布
“L64X256MixRandom” 264(2256− 1) 320位 64位 4-等分布和完全等分布
“L64X1024MixRandom” 264(21024− 1) 1088位 64位 16-等分布和完全等分布
“L128X128MixRandom” 2128(2128− 1) 256位 128位 完全均匀分布
“L128X256MixRandom” 2128(2256− 1) 384位 128位 完全均匀分布
“L128X1024MixRandom” 2128(21024− 1) 1152位 128位 完全均匀分布
对于上面列出的名称以 L32 开头的算法, nextInt() 方法产生的 32 位值是完全等分布的,但是 nextLong() 方法产生的 64 位值不是完全等分布的。

对于上面列出的名称以 L64L128 开头的算法,nextLong() 方法生成的 64 位值是完全均匀分布:每个实例,在其循环过程中,都会产生 264可能long值完全相同的次数。例如,“L64X256MixRandom”的任何特定实例,在其循环过程中的每一个 264将产生可能的 long 值 2256−1 次。 nextInt() nextFloat() nextDouble() 方法生成的值同样完全等分布。

此外,对于上面列出的名称以 L64 开头的算法,由 nextLong() 方法生成的 64 位值是k- 等分布(但不完全是k等分布)。准确地说,以“L64X256MixRandom”为例:对于“L64X256MixRandom”的任何特定实例,考虑nextLong() 产生的64位值循环的(重叠)长度为4的子序列(假设没有调用其他方法会影响状态)。有2个64(2256− 1) 这样的子序列,每个子序列由 4 个 64 位值组成,可以有 2 个中的一个256值。其中2256子序列值,几乎所有的(2256− 264) 发生 264在整个周期的过程中多次,另外 264子序列值仅出现 264− 1 次。因此,获得任何特定的较不常见子序列值的概率与获得任何特定的较常见子序列值的概率之比为 1 − 2-64. (请注意,一组 264不太常见的子序列值将在“L64X256MixRandom”的一个实例与另一个实例之间有所不同,作为 LCG 的附加参数的函数。)nextInt() nextFloat() nextDouble() 方法产生的值同样是 4 等分布的(但不完全是4-等分布)。

下表给出了 LCG 乘数、所用特定 XBG 算法的名称、该 XBG 算法的特定数字参数,以及该包中使用的每个特定 LXM 算法的混合函数。 (请注意,用于 128 位 LCG 情况的乘法器是 65 位宽,因此表中显示的常量 0x1d605bbb58c8abbfdL 实际上不能在代码中使用;相反,源代码中仅表示 64 个低位 0xd605bbb58c8abbfdL,并且丢失的 1 位通过 LCG 中使用的乘加算法的特殊编码来处理。)

LXM 乘数
执行 LCG乘数m XBG算法 XBG参数 混合功能
“L32X64MixRandom” 0xadb4a92d xoroshiro64,版本 1.0 (26, 9, 13) mixLea32(s+x0)
"L64X128StarStarRandom" 0xd1342543de82ef95L xoroshiro128,版本 1.0 (24, 16, 37) Long.rotateLeft((s+x0)* 5, 7) * 9
“L64X128MixRandom” 0xd1342543de82ef95L xoroshiro128,版本 1.0 (24, 16, 37) mixLea64(s+x0)
“L64X256MixRandom” 0xd1342543de82ef95L xoshiro256,版本 1.0 (17, 45) mixLea64(s+x0)
“L64X1024MixRandom” 0xd1342543de82ef95L xoroshiro1024,版本 1.0 (25, 27, 36) mixLea64(s+x0)
“L128X128MixRandom” 0x1d605bbb58c8abbfdL xoroshiro128,版本 1.0 (24, 16, 37) mixLea64(sh+x0)
“L128X256MixRandom” 0x1d605bbb58c8abbfdL xoshiro256,版本 1.0 (17, 45) mixLea64(sh+x0)
“L128X1024MixRandom” 0x1d605bbb58c8abbfdL xoroshiro1024,版本 1.0 (25, 27, 36) mixLea64(sh+x0)
自从:
17