查看源代码 rand (stdlib v6.2)
伪随机数生成
此模块提供伪随机数生成,并实现了一些基本的生成器算法。大多数算法通过插件框架提供,该框架为基本生成器添加了功能。
在此模块文档的末尾,有一些不使用此模块的正常插件框架的小众算法。它们可能适用于特殊目的,例如在质量不重要时缩短生成时间、为其他生成器播种等。
插件框架
- 在进程字典中操作生成器状态。
- 自动播种。
- 手动播种支持,以避免常见的陷阱。
- 生成任意范围内的整数,具有均匀分布,没有明显的偏差。
- 生成任意范围内的整数,大于基本生成器的范围,具有均匀分布。
- 生成具有均匀分布的浮点数。
- 生成具有正态分布的浮点数。
- 生成任意数量的字节。
基本生成器算法实现了 Sebastiano Vigna 的 Xoroshiro 和 Xorshift 算法。在迭代过程中,它们生成一个大整数(至少 58 位),并操作多个大整数的状态。
为了生成具有正态分布的数字,在基本生成器的输出上使用了 Marsaglia 和 Tsang 的 Ziggurat 方法。
对于大多数算法,提供了跳跃函数来生成不重叠的序列。跳跃函数执行的计算相当于大量重复的状态迭代,但执行时间大致相当于每个生成器位一次常规迭代。
exsss
,默认算法(自 OTP 22.0 起)
Xorshift116**,58 位精度,周期为 2^116-1跳跃函数:相当于 2^64 次调用
这是 Xorshift116 生成器与 David Blackman 和 Sebastiano Vigna 在 2018 年的论文中的 StarStar 扰码器相结合:扰码线性伪随机数生成器
生成器不使用 58 位旋转,因此比 Xoroshiro116 生成器更快,并且与 StarStar 扰码器结合使用时,它不像
exrop
(Xoroshiro116+) 那样有任何弱低位。唉,这种组合比
exrop
慢约 10%,但尽管如此,由于其统计特性,它是默认算法。exro928ss
(自 OTP 22.0 起)
Xoroshiro928**,58 位精度,周期为 2^928-1跳跃函数:相当于 2^512 次调用
这是 Xoroshiro1024** 的 58 位版本,来自 David Blackman 和 Sebastiano Vigna 在 2018 年的论文:扰码线性伪随机数生成器,在 64 位 Erlang 系统上执行速度仅比默认
exsss
算法 慢约 40%,但周期更长,统计特性更好,但另一方面,状态更大。非常感谢 Sebastiano Vigna 在 58 位适配方面的帮助。
exrop
(自 OTP 20.0 起)
Xoroshiro116+,58 位精度,周期为 2^116-1跳跃函数:相当于 2^64 次调用
exs1024s
(自 OTP 20.0 起)
Xorshift1024*,64 位精度,周期为 2^1024-1跳跃函数:相当于 2^512 次调用
exsp
(自 OTP 20.0 起)
Xorshift116+,58 位精度,周期为 2^116-1跳跃函数:相当于 2^64 次调用
这是先前默认算法(
exsplus
,已弃用)的更正版本,该算法已被 Xoroshiro116+ (exrop
) 取代。由于此算法不使用旋转,因此它的执行速度比exrop
稍快(例如 < 15%)(后者必须进行 58 位旋转,但没有本机指令)。请参阅算法主页。
默认算法
当前的默认算法是exsss
(Xorshift116**)。如果需要特定的算法,请务必始终使用seed/1
来初始化状态。
默认的算法在 Erlang/OTP 版本之间可能会发生变化,并且选择的算法应具有高速度、小状态和“足够好”的统计特性。
旧算法
未记录的(旧)算法已弃用,但仍会实现,因此依赖它们的旧代码将生成与以前相同的伪随机序列。
注意
现在未记录的算法的实现中存在许多问题,这就是它们被弃用的原因。新算法速度稍慢,但没有这些问题
均匀整数范围在概率分布中存在偏差,对于小范围来说不明显,但对于小于生成器精度的范围,生成低数字的概率可能是高数字的两倍。
大于或等于生成器精度的均匀整数范围使用了浮点回退,该回退仅计算 52 位,这小于请求的范围,因此甚至不可能生成请求范围内的所有数字。
均匀浮点数的密度不均匀,因此例如小于 0.5 的小值间隔会随着生成的值接近 0.0 而减小,尽管对于足够大的子范围仍均匀分布。新算法生成的形式为
N * 2.0^(-53)
的均匀分布的浮点数,因此它们是等间距的。
生成器状态
每次生成随机数时,都会使用一个状态来计算它,从而产生新的状态。状态可以是隐式的,也可以是显式的参数和返回值。
具有隐式状态的函数在进程字典中以键 rand_seed
存储的状态上运行。如果调用函数时该键不存在,则会自动使用默认算法调用seed/1
,并创建一个合理不可预测的种子。
具有显式状态的函数不使用进程字典。
示例
简单用法;如果尚未完成,则使用非固定种子创建并播种默认算法,并生成两个均匀分布的浮点数。
R0 = rand:uniform(),
R1 = rand:uniform(),
使用指定的算法
_ = rand:seed(exro928ss),
R2 = rand:uniform(),
使用具有固定种子的指定算法
_ = rand:seed(exro928ss, {123, 123534, 345345}),
R3 = rand:uniform(),
使用具有非固定种子的函数式 API
S0 = rand:seed_s(exsss),
{R4, S1} = rand:uniform_s(S0),
生成一个教科书式的基本 Box-Muller 标准正态分布数
R5 = rand:uniform_real(),
R6 = rand:uniform(),
SND0 = math:sqrt(-2 * math:log(R5)) * math:cos(math:pi() * R6)
生成一个标准正态分布数
{SND1, S2} = rand:normal_s(S1),
生成一个均值为 -3 且方差为 0.5 的正态分布数
{ND0, S3} = rand:normal_s(-3, 0.5, S2),
生成的数字的质量
注意
内置随机数生成器算法不是密码学上安全的。如果需要密码学上安全的随机数生成器,请使用类似
crypto:rand_seed/0
的方法。
对于除 exro928ss
和 exsss
之外的所有这些生成器,最低位(或几个位)的行为比所有其他位的随机性稍差。 exrop
(和 exsp
) 为 1 位,exs1024s
为 3 位。例如,请参阅 Xoroshiro128+ 生成器源代码中的此说明
除了通过 BigCrush 之外,此生成器还通过了 PractRand 测试套件,最高可达(并包括)16TB,但二进制秩测试除外,二进制秩测试由于最低位是 LFSR 而失败;所有其他位都通过所有测试。我们建议使用符号测试来提取随机布尔值。
如果这是一个问题;要使用这些算法生成布尔值,请使用如下方法
(rand:uniform(256) > 128) % -> boolean()
((rand:uniform(256) - 1) bsr 7) % -> 0 | 1
对于一般范围,exrop
为 N = 1
,exs1024s
为 N = 3
(((rand:uniform(Range bsl N) - 1) bsr N) + 1)
此模块中的浮点生成函数在从整数转换时会浪费最低位,因此它们避免了此问题。
小众算法
小众算法 API 包含特殊用途的算法,这些算法不使用插件框架,主要出于性能原因。
由于这些算法缺少插件框架支持,因此生成范围不是基本生成器范围的数字可能会成为问题。
假设Range
小于生成器的范围,则至少有四种方法可以执行此操作
取模
要生成范围为0..Range-1
的数字V
生成数字
X
。
使用V = X rem Range
作为您的值。此方法使用
rem
,即整数除法的余数,这是一个缓慢的操作。生成器的低位直接传播到生成的值,因此如果生成器的低位有缺陷,此方法也会传播它们。
如果
Range
不是生成器范围的除数,则生成的数字会产生偏差。例如假设生成器生成一个字节,即生成器的范围是
0..255
,而期望的范围是0..99
(范围 = 100
)。那么,有 3 个生成器输出可以产生值0
,它们是0
、100
和200
。但是只有 2 个生成器输出可以产生值99
,它们是99
和199
。因此,对于0..55
范围内的值V
,其概率是其他值56..99
的概率的 3/2 倍。如果
范围
比生成器范围小得多,那么这种偏差就很难检测到。经验法则是,如果范围
小于生成器范围的平方根,则偏差就足够小。例如:当
范围 = 20
时,一个字节生成器。生成最大数字的可能性有 12 个 (256 div 20
),而生成数字V < 16
(256 rem 20
) 的可能性多一个。因此,较低数字的概率与较高数字的概率之比为 13/12。要以某种信心检测到这种差异,您需要生成的数字远多于生成器范围,在这个小例子中是256
。
截断乘法
要在范围0..范围-1
中生成数字V
,当您有一个范围为 2 的幂的生成器时 (0..2^位-1
)生成数字
X
。
使用V = X * 范围 bsr 位
作为您的值。如果乘法
X * 范围
创建了一个大数,则此方法会变得非常慢。来自生成器的高位会传播到生成的值,因此,如果生成器的高位存在弱点,此方法也会传播这些弱点。
如果
范围
不是生成器范围的除数,则生成的数字会存在偏差,与上面的 模 方法非常相似。
移位或掩码
要在 2 的幂范围内 (0..2^R位-1
) 生成一个数字,当您有一个 2 的幂范围的生成器时 (0..2^位
)生成数字
X
。
使用V = X band ((1 bsl R位)-1)
或V = X bsr (位-R位)
作为您的值。使用
band
进行掩码会保留低位,而使用bsr
进行右移会保留高位,因此如果生成器的高位或低位存在弱点,请选择正确的运算符。如果生成器的范围不是 2 的幂,但仍然使用了此方法,它会引入偏差,其方式与上面的 模 方法相同。
拒绝
生成数字
X
。
如果X
在范围内,则将其用作您的值,否则拒绝它并重复。理论上,不确定此方法是否会完成,但在实践中,您要确保拒绝的概率较低。然后,又一次迭代的概率呈指数下降,因此预期的平均迭代次数通常在 1 到 2 之间。此外,由于基本生成器是全长生成器,因此最终必须生成一个会打破循环的值。
这些方法可以组合使用,例如,使用 模 方法,并且仅当生成器值会产生偏差时才使用 拒绝。或者使用 移位或掩码 来减小生成器值的大小,以便 截断乘法 不会创建大数。
生成范围
0..1
(即0.0 =< V < 1.0
) 内的浮点数(具有 53 位尾数的 IEEE 745 双精度浮点数)的推荐方法是生成一个 53 位数字X
,然后使用V = X * (1.0/((1 bsl 53)))
作为您的值。这将创建一个 N*2^-53 形式的值,该范围内的每个可能的 N 具有相等的概率。
总结
类型
算法特定的内部状态
可以打印或保存到文件的算法相关状态。
算法特定的内部状态
算法特定的内部状态
算法特定的内部状态
算法特定的内部状态
算法特定的内部状态
1 .. (16#1ffb072 bsl 29) - 2
生成器种子值。
算法特定状态
算法相关状态。
0 .. (2^58 - 1)
0 .. (2^64 - 1)
插件框架 API
使用进程字典中的状态,生成随机字节作为 t:binary()
。
生成随机字节作为 t:binary()
。
导出种子值。
导出种子值。
将生成器状态向前跳转。
将生成器状态向前跳转。
生成具有标准正态分布的随机数。
生成具有指定正态分布 𝒩 (μ, σ²) 的随机数。
生成具有标准正态分布的随机数。
生成具有指定正态分布 𝒩 (μ, σ²) 的随机数。
为随机数生成器设置种子并选择算法。
为随机数生成器设置种子并选择算法。
为随机数生成器设置种子并选择算法。
为随机数生成器设置种子并选择算法。
使用进程字典中的状态,生成均匀分布的随机数 0.0 =< X < 1.0
。
使用进程字典中的状态,生成均匀分布的随机整数 1 =< X =< N
。
使用进程字典中的状态,生成均匀分布的随机数 0.0 < X < 1.0
。
生成均匀分布的随机数 0.0 < X < 1.0
。
生成均匀分布的随机数 0.0 =< X < 1.0
。
生成均匀分布的随机整数 1 =< X =< N
。
类型
-type alg() :: builtin_alg() | atom().
-type alg_handler() :: #{type := alg(), bits => non_neg_integer(), weak_low_bits => non_neg_integer(), max => non_neg_integer(), next := fun((alg_state()) -> {non_neg_integer(), alg_state()}), uniform => fun((state()) -> {float(), state()}), uniform_n => fun((pos_integer(), state()) -> {pos_integer(), state()}), jump => fun((state()) -> state())}.
-type alg_state() :: exsplus_state() | exro928_state() | exrop_state() | exs1024_state() | exs64_state() | dummy_state() | term().
-type builtin_alg() :: exsss | exro928ss | exrop | exs1024s | exsp | exs64 | exsplus | exs1024 | dummy.
-type dummy_state() :: uint58().
算法特定的内部状态
可以打印或保存到文件的算法相关状态。
-opaque exro928_state()
算法特定的内部状态
-opaque exrop_state()
算法特定的内部状态
-opaque exs64_state()
算法特定的内部状态
-opaque exs1024_state()
算法特定的内部状态
-opaque exsplus_state()
算法特定的内部状态
-type mwc59_state() :: 1..133850370 bsl 32 - 1 - 1.
1 .. (16#1ffb072 bsl 29) - 2
生成器种子值。
整数列表直接设置生成器的内部状态,在对值进行算法相关的检查并掩码到适当的字长之后。整数的数量必须等于生成器中的状态字数。
单个整数用作 SplitMix64 生成器的初始状态。然后,将其顺序输出值用于设置生成器的内部状态,在掩码到适当的字长之后,并在需要时避免零值。
传统的 3 元组整数种子通过算法相关的哈希函数传递,以创建生成器的初始状态。
-type splitmix64_state() :: uint64().
算法特定状态
-type state() :: {alg_handler(), alg_state()}.
算法相关状态。
-type uint58() :: 0..1 bsl 58 - 1.
0 .. (2^58 - 1)
-type uint64() :: 0..1 bsl 64 - 1.
0 .. (2^64 - 1)
插件框架 API
-spec bytes(N :: non_neg_integer()) -> Bytes :: binary().
使用进程字典中的状态,生成随机字节作为 t:binary()
。
-spec bytes_s(N :: non_neg_integer(), State :: state()) -> {Bytes :: binary(), NewState :: state()}.
生成随机字节作为 t:binary()
。
对于指定的整数 N >= 0
,生成一个具有该数量的随机字节的 binary/0
。
-spec export_seed() -> undefined | export_state().
导出种子值。
以外部格式返回随机数状态。用于 seed/1
。
-spec export_seed_s(State :: state()) -> export_state().
导出种子值。
以外部格式返回随机数生成器状态。用于 seed/1
。
-spec jump() -> NewState :: state().
将生成器状态向前跳转。
将生成器状态向前跳转。
执行一种算法特定的 State
跳跃计算,等效于大量状态迭代。请参阅此模块的算法列表。
返回 NewState
。
此功能可用于从一个起始状态创建许多不重叠的随机数序列。
如果该State
的算法没有实现跳跃函数,则此函数会引发 not_implemented
错误异常。
-spec normal() -> X :: float().
生成具有标准正态分布的随机数。
类似于 normal_s/1
,但操作的是存储在进程字典中的状态。返回生成的数字 X
。
生成具有指定正态分布 𝒩 (μ, σ²) 的随机数。
类似于 normal_s/3
,但操作的是存储在进程字典中的状态。返回生成的数字 X
。
生成具有标准正态分布的随机数。
从指定的 State
,生成一个符合标准正态分布的随机数 X ::
float/0
,即均值为 0.0
,方差为 1.0
。
-spec normal_s(Mean, Variance, State) -> {X :: float(), NewState :: state()} when Mean :: number(), Variance :: number(), State :: state().
生成具有指定正态分布 𝒩 (μ, σ²) 的随机数。
从指定的 State
,生成一个符合正态分布 𝒩 (μ, σ²) 的随机数 X ::
float/0
,即 𝒩 (均值, 方差),其中 Variance >= 0.0
。
为随机数生成器设置种子并选择算法。
与 seed_s(Alg_or_State)
相同,但也会将生成的状态存储在进程字典中。
参数 default
是已实现的默认算法的别名(自 OTP 24.0 起)。
为随机数生成器设置种子并选择算法。
与 seed_s(Alg, Seed)
相同,但也会将生成的状态存储在进程字典中。
Alg = default
是已实现的默认算法的别名(自 OTP 24.0 起)。
-spec seed_s(Alg | State) -> state() when Alg :: builtin_alg() | default, State :: state() | export_state().
为随机数生成器设置种子并选择算法。
使用参数 Alg
,选择该算法并使用合理不可预测的时间相关数据为随机数生成播种。
Alg = default
是默认算法的别名(自 OTP 24.0 起)。
使用参数 State
,重新创建状态并返回它。另请参见 export_seed/0
。
-spec seed_s(Alg, Seed) -> state() when Alg :: builtin_alg() | default, Seed :: seed().
为随机数生成器设置种子并选择算法。
根据指定的 seed/0
整数,为指定算法创建并返回生成器状态。
Alg = default
是自 OTP 24.0 起已实现的默认算法的别名。
-spec uniform() -> X :: float().
使用进程字典中的状态,生成均匀分布的随机数 0.0 =< X < 1.0
。
类似于 uniform_s/1
,但操作的是存储在进程字典中的状态。返回生成的数字 X
。
-spec uniform(N :: pos_integer()) -> X :: pos_integer().
使用进程字典中的状态,生成均匀分布的随机整数 1 =< X =< N
。
类似于 uniform_s/2
,但操作的是存储在进程字典中的状态。返回生成的数字 X
。
-spec uniform_real() -> X :: float().
使用进程字典中的状态,生成均匀分布的随机数 0.0 < X < 1.0
。
类似于 uniform_real_s/1
,但操作的是存储在进程字典中的状态。返回生成的数字 X
。
请参阅 uniform_real_s/1
。
生成均匀分布的随机数 0.0 < X < 1.0
。
从指定状态,生成一个随机浮点数,均匀分布在值范围 DBL_MIN =< X < 1.0
中。
从概念上讲,随机实数 R
是从区间 0.0 =< R < 1.0
生成的,然后返回 IEEE 754 双精度格式中最接近的向下舍入的非零规范化数。
注意
此函数生成的数字对于小数字具有比常规
uniform_s/1
更好的粒度,因为尾数中的所有位都是随机的。此属性与从不返回零的事实相结合,对于执行例如1.0 / X
或math:log(X)
的算法很有用。
这个概念意味着获得零的概率极低;低到这个函数实际上从不返回 0.0
。它可能返回的最小数字是 DBL_MIN
,即 2.0^(-1022)
。
此函数描述顶部声明的值范围在技术上是正确的,但 0.0 =< X < 1.0
更好地描述了生成数字的统计分布,并且此函数永远不会返回 0.0
这一点是无法观察到的。
对于所有子范围 N*2.0^(-53) =< X < (N+1)*2.0^(-53)
,其中 0 =< integer(N) < 2.0^53
,生成该范围内数字的概率是相同的。 与 uniform_s/1
生成的数字进行比较。
为偶尔的小数字生成额外的随机位会稍微降低性能。此函数比常规 uniform_s/1
慢大约 20%。
生成均匀分布的随机数 0.0 =< X < 1.0
。
从指定的 State
,生成一个随机数 X ::
float/0
,均匀分布在值范围 0.0 =< X < 1.0
中。返回数字 X
和更新后的 NewState
。
生成的数字的形式为 N * 2.0^(-53)
,即在区间中等距分布。
警告
此函数可能会返回
0.0
,这对于某些应用程序可能是致命的。如果不需要这样,您可以使用(1.0 - rand:uniform())
来获取区间0.0 < X =< 1.0
,或者改用uniform_real/0
。如果不需要任何一个端点,您可以使用如下测试和重试来获得
0.0 < X < 1.0
的范围my_uniform() -> case rand:uniform() of X when 0.0 < X -> X; _ -> my_uniform() end.
-spec uniform_s(N :: pos_integer(), State :: state()) -> {X :: pos_integer(), NewState :: state()}.
生成均匀分布的随机整数 1 =< X =< N
。
从指定的 State
,生成一个随机数 X ::
integer/0
,均匀分布在指定的范围 1 =< X =< N
中。返回数字 X
和更新后的 NewState
。
利基算法 API
-spec exsp_jump(AlgState :: exsplus_state()) -> NewAlgState :: exsplus_state().
将生成器状态向前跳转。
执行一个 State
跳跃计算,等效于 2^64 次状态迭代。
返回 NewState
。
此功能可用于从一个起始状态创建许多不重叠的随机数序列。
请参阅此模块描述顶部的跳跃函数描述。
关于为什么公开此内部实现函数,请参阅 exsp_next/1
。
-spec exsp_next(AlgState :: exsplus_state()) -> {X :: uint58(), NewAlgState :: exsplus_state()}.
生成 Xorshift116+ 随机整数和新的算法状态。
根据 Xorshift116+ 算法,从指定的 AlgState
生成一个随机 58 位整数 X
和一个新的算法状态 NewAlgState
。
这是一个 API 函数,公开了 exsp
算法的内部实现,使其可以在不使用插件框架开销的情况下使用,这对于时间敏感的应用程序可能很有用。在典型的 64 位 Erlang VM 上,这种方法执行速度仅为通过此模块的常规插件框架使用默认算法所需时间的略高于 30% (1/3)。
要为此生成器设定种子,请使用 {_, AlgState} = rand:seed_s(exsp)
或 {_, AlgState} = rand:seed_s(exsp, Seed)
,并指定一个 Seed
。
注意
此函数不提供任何帮助来生成选定范围内的数字,也不提供生成浮点数的功能。在执行这两项操作时,很容易意外地破坏此生成器的统计特性或失去性能优势。请参阅此 Niche 算法 API 描述开头的食谱。
另请注意此生成器存在低位弱点的警告。
导出此形式的生成器主要是出于性能原因。
-spec mwc59(CX0 :: mwc59_state()) -> CX1 :: mwc59_state().
生成新的 MWC59 状态。
根据乘法进位生成器,从指定的生成器状态 CX0
生成一个新的状态 CX1
,它是带有 2 次幂乘数和素数模数的乘法同余生成器的高效实现。
此生成器使用乘数 2^32
和模数 16#7fa6502 * 2^32 - 1
,它们是与 Sebastiano Vigna 合作选定的,以避免大数运算并仍然获得良好的统计质量。它被命名为 "MWC59",可以写成
C = CX0 bsr 32
X = CX0 band ((1 bsl 32)-1))
CX1 = 16#7fa6502 * X + C
由于生成器使用 2 的幂的乘数,因此在 2 维和 3 维碰撞测试和生日间距测试中会出现统计缺陷,即使仅查看 MWC“数字”,即生成器状态的低 32 位(乘数),这些警告也适用。状态的较高位更差。
通过使用加扰器,而不是仅仅取低位,可以大大提高输出值的质量。函数 mwc59_value32
是一个快速加扰器,返回一个像样的 32 位数字。速度稍慢的 mwc59_value
加扰器返回质量非常好的 59 位数字,而 mwc59_float
返回质量非常好的 float/0
。
基础生成器的低位出奇地好,因此最低 16 位实际上通过了相当严格的 PRNG 测试,尽管生成器的弱点在于 32 位 MWC“数字”的高位。建议对生成器状态使用 rem
,或使用位掩码提取最低位以生成 16 位或更小范围内的数字。请参阅此 Niche 算法 API 描述开头的食谱。
在典型的 64 位 Erlang VM 上,此生成器的执行时间低于此模块的 插件框架 API 中默认算法的 8% (1/13)。使用 mwc59_value32
加扰器,总时间变为 16% (1/6),而使用 mwc59_value
,则变为默认算法的 20% (1/5)。使用 mwc59_float
,生成 float/0
的总时间为默认算法的 60%。
注意
此生成器是用于高速应用的利基生成器。它的周期比默认生成器短得多,这本身就是一个质量问题,尽管当与值加扰器一起使用时,它可以通过严格的 PRNG 测试。此生成器比
exsp_next/1
快得多,但质量稍低且周期短得多。
-spec mwc59_float(CX :: mwc59_state()) -> V :: float().
从生成器状态 CX
返回一个值 V ::
float/0
,其范围为 0.0 =< V < 1.0
,例如 uniform_s/1
。
在转换为 float/0
之前,生成器状态会像 mwc59_value/1
一样进行加扰。
-spec mwc59_seed() -> CX :: mwc59_state().
创建 MWC59 生成器状态。
与 mwc59_seed/1
类似,但它会哈希 seed_s(atom())
的默认种子值。
-spec mwc59_seed(S :: 0..1 bsl 58 - 1) -> CX :: mwc59_state().
创建 MWC59 生成器状态。
返回生成器状态 CX
。对 58 位种子值 S
进行哈希处理以创建生成器状态,以避免相似的种子创建相似的序列。
-spec mwc59_value32(CX :: mwc59_state()) -> V :: 0..1 bsl 32 - 1.
从 MWC59 状态 计算一个 32 位加扰值。
从生成器状态 CX
返回一个 32 位值 V
。生成器状态使用 8 位异或移位进行加扰,这足以掩盖基础生成器 mwc59
的统计缺陷,从而生成质量不错的数字。但是,在 2 维和 3 维生日间距和碰撞测试中仍然会暴露出一些问题。
当使用此加扰器时,通常最好使用该值的高位而不是低位。最低 8 位质量良好,直接从基础生成器传递。它们与异或移位中的下一个 8 位组合在一起,使低 16 位具有良好的质量,但在 16..31 位范围内,存在较弱的位,这些位不应成为生成值的高位。
因此,通常更安全的方法是移出低位。请参阅此 Niche 算法 API 描述开头的食谱。
对于小于约 16 位的非 2 次幂范围(为了避免过多偏差和避免大数),可以使用截断乘法,即:(Range*V) bsr 32
,这比使用 rem
快得多。
-spec mwc59_value(CX :: mwc59_state()) -> V :: 0..1 bsl 59 - 1.
从 MWC59 状态 计算一个 59 位加扰值。
从生成器状态 CX
返回一个 59 位值 V
。生成器状态使用一个 4 位,然后是一个 27 位异或移位进行加扰,这足以掩盖 MWC59 基础生成器的统计缺陷,从而使所有 59 位都具有非常好的质量。
在处理值 V
时,请小心不要意外地创建大数。
通常,使用此加扰器的高位比低位更好。请参阅此 Niche 算法 API 描述开头的食谱。
对于小于约 29 位的非 2 次幂范围(为了避免过多偏差和避免大数),可以使用截断乘法,这比使用 rem
快得多。例如,对于范围 1'000'000'000;该范围为 30 位,我们使用生成器的 29 位,加起来为 59 位,这不是大数(在 64 位 VM 上): (1000000000 * (V bsr (59-29))) bsr 29
。
-spec splitmix64_next(AlgState :: integer()) -> {X :: uint64(), NewAlgState :: splitmix64_state()}.
生成 SplitMix64 随机 64 位整数和新的算法状态。
根据 SplitMix64 算法,从指定的 AlgState
生成一个随机 64 位整数 X
和一个新的生成器状态 NewAlgState
。
此生成器在 rand
模块中内部用于为其他生成器设定种子,因为它是一种完全不同的类型,可减少创建意外坏种子的可能性。