Package java.util.random
这些类和接口支持“随机生成器”的定义和使用,这个术语涵盖了传统上被称为“随机数生成器”的内容,以及生成其他类型的随机选择值(例如布尔值)的生成器。这些类和接口不仅涵盖确定性(伪随机)算法,还包括使用一些“真正随机”物理来源的值生成器(例如可能利用热噪声或量子力学效应的随机算法)。
主要接口是RandomGenerator
,它提供了请求从均匀分布中伪随机选择的int
、long
、float
、double
或boolean
类型的单个值的方法;请求从正态分布或指数分布中伪随机选择的double
类型的值的方法;以及创建从均匀分布中伪随机选择的int
、long
或double
类型的值流的方法(这些流基于spliterator,允许对其元素进行并行处理)。还有用于根据名称创建特定随机数生成器算法实例的静态工厂方法。
主要支持类是RandomGeneratorFactory
。这可用于为特定算法生成多个随机数生成器。RandomGeneratorFactory
还提供了用于选择随机数生成器算法的方法。RandomGeneratorFactory使用服务提供程序API注册RandomGenerator
接口的实现。
一个重要的辅助接口是RandomGenerator.StreamableGenerator
,它提供了创建基于spliterator的RandomGenerator
对象流的方法,允许使用多个线程并行处理这些对象。与Random
不同,大多数RandomGenerator
的实现不是线程安全的。意图是实例不应在线程之间共享;相反,每个线程应该有自己的随机生成器来使用。此包提供的各种伪随机算法设计成多个实例将(极高概率下)表现得统计独立。
对于许多用途,这些是消费伪随机值的唯一两个接口。还有一些更专业的接口,描述了更专业的随机数生成器类别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
接口的初始生成器,然后将其视为其独占使用的初始线程;然后,每当任何线程需要分叉新线程时,它首先使用自己生成器的split()
方法创建一个新生成器,然后将该新生成器传递给新创建的线程以供该新线程独占使用。
选择随机数生成器算法
Java提供了三组随机数生成器算法:传统组、LXM组和Xoroshiro/Xoshiro组。
传统组包括JDK 17之前存在的随机数生成器:Random、ThreadLocalRandom、SplittableRandom和SecureRandom。Random(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
可能是一个不错的选择。
对于在启动计算时分配许多线程的应用程序,可以使用“可跳转”生成器,如Xoroshiro128PlusPlus
或Xoshiro256PlusPlus
,或者使用“可分割”生成器,如L64X128MixRandom
或L64X256MixRandom
。
对于动态创建许多线程的应用程序,可能通过使用生成器如L64X128MixRandom
或L64X256MixRandom
来推荐使用“可分割”生成器。如果动态创建的生成器数量可能非常大(数百万或更多),则使用像L128X128MixRandom
或L128X256MixRandom
这样使用128位参数而不是64位参数的生成器,将大大降低两个实例使用相同状态周期的可能性。
对于使用连续生成值的元组的应用程序,可能希望使用一个k-均匀分布的生成器,其中k至少与生成的元组长度一样大。生成器L64X256MixRandom
被证明是4-均匀分布的,而L64X1024MixRandom
被证明是16-均匀分布的。
对于生成大量排列的应用程序,最好使用周期远大于可能的排列总数的生成器;否则将无法生成一些预期的排列。例如,如果目标是洗牌一副52张牌的牌组,可能的排列数是52!(52的阶乘),大于2225(但小于2226),因此最好使用周期至少为2256的生成器,如L64X256MixRandom
或L64X1024MixRandom
或L128X256MixRandom
或L128X1024MixRandom
。(当然,在初始化生成器时还需要提供足够多的种子位,否则仍将无法生成一些预期的排列。)
可用的随机数生成器算法
这些算法[在下表中]必须在当前版本的Java SE中找到。特定的JDK实现可能识别附加算法;请查看JDK的文档以获取详细信息。Java SE所需的算法集可能会随着Java SE规范的更改而更新。随着时间的推移,可能会添加新的算法并删除旧的算法。此外,作为另一个生命周期阶段,算法可能会被弃用。不建议使用已弃用的算法。如果一个必需的算法被弃用,它可能会在将来的版本中被移除。由于随机数生成器算法开发和分析的进步,一个算法可能在特定Java SE版本的生命周期内被弃用。更改算法的弃用状态不是规范更改。
算法 | 组 | 周期 | 状态位数 | 均匀分布 |
---|---|---|---|---|
L128X1024MixRandom | LXM | BigInteger.ONE.shiftLeft(1024).subtract(BigInteger.ONE).shiftLeft(128) | 1152 | 1 |
L128X128MixRandom | LXM | BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE).shiftLeft(128) | 256 | 1 |
L128X256MixRandom | LXM | BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE).shiftLeft(128) | 384 | 1 |
L32X64MixRandom | LXM | BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE).shiftLeft(32) | 96 | 1 |
L64X1024MixRandom | LXM | BigInteger.ONE.shiftLeft(1024).subtract(BigInteger.ONE).shiftLeft(64) | 1088 | 16 |
L64X128MixRandom | LXM | BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE).shiftLeft(64) | 192 | 2 |
L64X128StarStarRandom | LXM | BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE).shiftLeft(64) | 192 | 2 |
L64X256MixRandom | LXM | BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE).shiftLeft(64) | 320 | 4 |
Random | Legacy | BigInteger.ONE.shiftLeft(48) | 48 | 0 |
SplittableRandom | Legacy | BigInteger.ONE.shiftLeft(64) | 64 | 1 |
ThreadLocalRandom * | Legacy | BigInteger.ONE.shiftLeft(64) | 64 | 1 |
Xoroshiro128PlusPlus | Xoroshiro | BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE) | 128 | 1 |
Xoshiro256PlusPlus | Xoshiro | 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”和“L128X1024MixRandom”;请注意,类SplittableRandom
也实现了此接口。
LXM系列随机数生成器算法
LXM算法的中心nextLong(或nextInt)方法的结构遵循Sebastiano Vigna在2017年12月的建议,即使用一个线性同余生成器(LCG)作为第一个子生成器,使用一个基于异或的生成器(XBG)作为第二个子生成器(而不是使用两个LCG子生成器)将提供更长的周期、更好的均匀分布、可扩展性和更好的质量。这里的每个具体实现都结合了目前已知最好的XBG算法(由Blackman和Vigna在“Scrambled Linear Pseudorandom Number Generators”中描述的xoroshiro128或xoshiro256,2021年,《ACM Transactions on Mathematical Software》)与使用目前已知最好的乘法器的LCG相结合(2019年由Steele和Vigna在“Computationally Easy, Spectrally Good Multipliers for Congruential Pseudorandom Number Generators”中找到,2021年,《Software: Practice and Experience》(doi:10.1002/spe.3030)),然后应用Doug Lea识别的混合函数或Blackman和Vigna提出的简单混合器。测试已经确认,LXM算法在质量上远远优于SplittableRandom
使用的SplitMix算法(2014年)(请参见Steele和Vigna,“LXM: Better Splittable Pseudorandom Number Generators (and Almost as Fast)”,Proc. 2021 ACM OOPSLA Conference)。每个类的名称形式为L
pX
qSomethingRandom
使用LXM系列随机数算法的特定成员;“LXM”代表“LCG,XBG,Mixer”。每个LXM生成器都有两个子生成器;一个是LCG(线性同余生成器),另一个是XBG(基于异或的生成器)。每个LXM生成器的每个输出都是使用LCG的状态与XBG的状态结合使用混合函数的结果(然后LCG的状态和XBG的状态会被推进)。
LCG子生成器的更新步骤形式为s = m*s + a
,其中s
、m
和a
都是相同大小的二进制整数,每个都有p位;s
是可变状态,乘数m
是固定的(对于类的所有实例都相同),而加数a
是一个参数(实例的一个final字段)。参数a
必须是奇数(这允许LCG具有最大周期,即2p);因此有2p−1个不同的参数选择。(当s
的大小为128位时,我们使用“sh
”来指代s
的高半部分,即s
的高64位。)
XBG子生成器原则上可以是各种XBG算法中的任何一种;在此包中,它始终是xoroshiro128
、xoshiro256
或xoroshiro1024
,在每种情况下都没有任何最终的混合器(例如"+"或"**"),因为LXM在处理过程中稍后使用单独的混合器。XBG状态由一些固定数量的int
或long
字段组成,通常命名为x0
、x1
等等,这些字段可以取任何值,只要它们不全为零。这些字段的总大小为q位;因此,此子生成器的周期为2q−1。
由于两个子生成器的周期2p和2q−1是互质的,任何单个LXM算法实例的周期(在重复之前生成的值系列的长度)是子生成器周期的乘积,即2p(2q−1),略小于2(p+q)。此外,如果同一LXM算法的两个不同实例具有不同的a
参数,则它们生成的值循环将不同。
一般来说,在"L
pX
q"生成器中,每个实例所需的内存为2p+q位。(如果q为1024或更大,则XBG状态表示为数组,因此数组对象头需要额外的位,另外还需要32位用于数组索引。)
p值较大意味着两个不同实例遍历相同状态循环的概率较低,q值较大意味着生成器在更多维度上均匀分布(当p为64时,这是可以证明的,当p为128时,这被推测为近似成立)。名称中带有"Mix
"的类使用具有出色雪崩特性的相当强大的混合函数;名称中带有"StarStar
"的类使用较弱但更快的混合函数。
此包中使用的特定LXM算法都是经过精心选择的,以便由nextLong()
方法生成的64位值完全均匀分布(例如,对于"L64X128MixRandom"的任何特定实例,在其循环过程中,每个可能的long
值将被生成2128−1次)。nextInt()
、nextFloat()
和nextDouble()
方法生成的值同样完全均匀分布。某些算法还提供了对某些大于1的k的进一步均匀分布的保证,这意味着由nextLong()
方法生成的连续非重叠的k元组的概率完全均匀分布(等可能发生)。
下表列出了此包中使用的特定LXM算法的周期、状态大小(以位为单位)、参数大小(以位为单位,包括始终需要为1位的低位)和均匀分布属性。
实现 | 周期 | 状态大小 | 参数大小 | 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位值不是完全均匀分布的。
对于上述名称以L64
或L128
开头的算法,nextLong()
方法生成的64位值是完全均匀分布的:每个实例,在其循环过程中,将完全相同次数地生成每个可能的64位值。例如,"L64X256MixRandom"的任何特定实例,在其循环过程中,每个可能的64位值将被生成2256−1次。nextInt()
、nextFloat()
和nextDouble()
方法生成的值同样是完全均匀分布的。
此外,对于上述名称以L64
开头的算法,nextLong()
方法生成的64位值是k-均匀分布的(但不是完全k-均匀分布)。具体而言,以"L64X256MixRandom"为例:对于"L64X256MixRandom"的任何特定实例,考虑由nextLong()
方法生成的64位值循环的(重叠的)长度为4的子序列(假设没有调用其他会影响状态的方法)。有264(2256−1)个这样的子序列,每个子序列由4个64位值组成,可以有2256个值。在这2256个子序列值中,几乎所有(2256−264)子序列值在整个循环过程中出现264次,另外的264子序列值仅出现264−1次。因此,获取任何较不常见子序列值的概率与获取任何较常见子序列值的概率之比为1−2-64。(请注意,264个较不常见子序列值的集合将因"L64X256MixRandom"的每个实例的LCG的加法参数而异。)nextInt()
、nextFloat()
和nextDouble()
方法生成的值同样是4-均匀分布的(但不是完全4-均匀分布)。
下表给出了LCG乘数值、使用的特定XBG算法的名称、该XBG算法的特定数值参数以及此包中使用的特定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
-
ClassDescription
RandomGenerator
接口旨在为生成随机或(更常见的是)伪随机数字(或布尔值)序列的对象提供一个通用协议。该接口旨在为生成伪随机值序列的对象提供一个通用协议,并且可以轻松地向前“跳转”到状态周期中的一个远处点。该接口旨在为生成伪随机值的对象提供一个通用协议,并且可以轻松地向前“跳转”到状态周期中的一个适度距离(例如264)。该接口旨在为生成伪随机值序列的对象提供一个通用协议,并且不仅可以轻松地向前“跳转”,还可以向前“跃进”到状态周期中的一个非常遥远的点(例如2128)。该接口旨在为生成伪随机值序列的对象提供一个通用协议,并且可以被“分割”为两个对象(原始对象和一个新对象),每个对象都遵循相同的协议(因此可以无限递归地分割)。RandomGeneratorFactory<T extends RandomGenerator>这是一个用于生成特定算法的多个随机数生成器的工厂类。