AnyMath 文档

随机数

Julia中的随机数生成使用https://prng.di.unimi.it/[Xoshiro256++]算法默认情况下,与per-任务 状态。 其他RNG类型可以通过继承插入 [医]抽象 类型;然后它们可以用于获得随机数的多个流。

随机的 包装是:

* N.任务,任务:表示使用当前活动任务本地流的标记,从父任务确定种子,或由 随机装置 (具有系统随机性)在程序启动时 * Xoshiro:使用Xoshiro256生成具有小状态向量和高性能的高质量随机数流++ 算法 * 随机装置:对于OS提供的熵。 这可能用于密码安全随机数(CS(P)RNG)。 * 梅尔森特威斯特:一个替代的高质量PRNG,这是Julia的旧版本中的默认值,并且速度也相当快,但需要更多的空间来存储状态向量并生成随机序列。

大多数与随机生成相关的函数都接受一个可选的 [医]抽象 对象作为第一个参数。 有些还接受尺寸规格 dims。.. (也可以作为元组给出)来生成随机值的数组。 在多线程程序中,您通常应该使用来自不同线程或任务的不同RNG对象,以便线程安全。 但是,从Julia1.3开始,默认的RNG是线程安全的(使用每个线程RNG直到版本1.6,此后每个任务)。

提供的RNGs可以生成以下类型的统一随机数: 漂浮物16, 漂浮物32, 漂浮64, [医大块头], 布尔, Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128, 比金特(或那些类型的复数)。 随机浮点数在 . 作为 比金特 表示无界整数,必须指定间隔(例如 兰德(大。(1:6))).

此外,正态分布和指数分布实现了一些 [医]抽象浮综合体 类型,请参阅 兰德恩兰德克斯普有关详情。

要从其他分布生成随机数,请参阅https://juliastats.org/Distributions.jl/stable/[Distributions.jl]包。

警告由于随机数生成的精确方式被视为实现细节,因此错误修复和速度改进可能会改变版本更改后生成的数字流。 因此,不鼓励在单元测试期间依赖特定的种子或生成的数字流-考虑测试所讨论方法的属性。

随机数模块

Random

支持生成随机数。 提供 兰德, 兰德恩, [医抽象], Xoshiro, 梅尔森特威斯特,和 随机装置.

随机生成函数

rand([rng=default_rng()], [S], [dims...])

从由 S; S 可以是

*可索引的集合(例如 1:9('x',"y",:z)) *安 摘要;摘要抽象集,抽象集 对象 *字符串(视为字符的集合),或 *以下列表中的类型,对应于指定的值集 **混凝土整数类型样本 typemin(S):typemax(S) (除外 比金特不支持) **混凝土浮点类型样本 [0, 1) **混凝土复杂类型 复杂{T} 如果 T 是一个可采样类型取它们的实部和虚部独立于对应于 T,但不支持,如果 T 是不可采样的。 **全部 <:AbstractChar 从一组有效的Unicode标量中提取的类型示例 **用户定义的类型和值集;有关实施指南,请参阅 钩入 随机的 空气污染指数 **已知大小的元组类型,每个参数 S 本身是一个可采样的类型;返回一个类型的值 S. 请注意,元组类型如 元组{Vararg{T}} (未知尺寸)和 元组{1:2} 不支持(用值参数化) **一个 类型,例如 对{X, Y} 这样, 兰德 被定义为 XY,在这种情况下产生随机对。 S 默认值为 漂浮64. 当除了可选项之外只传递一个参数时 [医]rng 并且是一个 元组,它被解释为值的集合(S)而不是作为 暗淡无光.

请参阅 兰德恩对于正态分布的数字,以及 兰德!兰德!为就地等价物。

兼容性

Julia1.1支持 S 作为元组至少需要Julia1.1。

兼容性

Julia1.11支持 S 作为一个 元组 类型至少需要Julia1.11。

*例子*

julia> rand(Int, 2)
2-element Vector{Int64}:
 1339893410598768192
 1575814717733606317

julia> using Random

julia> rand(Xoshiro(0), Dict(1=>2, 3=>4))
3 => 4

julia> rand((2, 3))
3

julia> rand(Float64, (2, 3))
2×3 Matrix{Float64}:
 0.999717  0.0143835  0.540787
 0.696556  0.783855   0.938235

请注意 兰德(rng,s::Union{AbstractDict,AbstractSet}) 是线性的长度 s,除非具有恒定复杂性的优化方法是可用的,这对于 Dict,Dict, 套装 和密集 比特集s.对于多个呼叫,使用 兰德(rng,收集(s)) 相反,或者 兰德(rng,Dict(s))兰德(Rng,Set(s)) 视情况而定。

rand!([rng=default_rng()], A, [S=eltype(A)])

填充数组 A 具有随机值。 如果 S 被指定(S 可以是一个类型或一个集合,cf。 兰德有关详细信息),这些值是从 S. 这相当于 收到!(A,rand(rng,S,size(A))) 但没有分配一个新的数组。

*例子*

julia> rand!(Xoshiro(123), zeros(5))
5-element Vector{Float64}:
 0.521213795535383
 0.5868067574533484
 0.8908786980927811
 0.19090669902576285
 0.5256623915420473
bitrand([rng=default_rng()], [dims...])

生成一个 比特阵列 随机布尔值。

*例子*

julia> bitrand(Xoshiro(123), 10)
10-element BitVector:
 0
 1
 0
 1
 0
 1
 0
 0
 1
 1
randn([rng=default_rng()], [T=Float64], [dims...])

生成类型的正态分布随机数 T 均值为0,标准差为1。 鉴于可选 暗淡无光 参数,生成一个大小数组 暗淡无光 这样的数字。 Julia的标准库支持 兰德恩 对于实现的任何浮点类型 兰德,例如 基地 类别 漂浮物16, 漂浮物32, 漂浮64(默认值),以及 [医大块头],连同他们的 综合体对应物。

(当 T 是复数的,这些值是从方差1的圆对称复正态分布中得出的,对应于具有均值零和方差的独立正态分布的实部和虚部 1/2).

请参阅 兰德!就地行动。

*例子*

生成单个随机数(默认为 漂浮64 类型):

julia> randn()
-0.942481877315864

生成正常随机数矩阵(默认为 漂浮64 类型):

julia> randn(2,3)
2×3 Matrix{Float64}:
  1.18786   -0.678616   1.49463
 -0.342792  -0.134299  -1.45005

随机数发生器的设置 [医]rng 使用用户定义的种子(用于可重复的数字)并使用它来生成随机的 漂浮物32 数或矩阵的 复杂的32 随机数:

julia> using Random

julia> rng = Xoshiro(123);

julia> randn(rng, Float32)
-0.6457307f0

julia> randn(rng, ComplexF32, (2, 3))
2×3 Matrix{ComplexF32}:
  -1.03467-1.14806im  0.693657+0.056538im   0.291442+0.419454im
 -0.153912+0.34807im    1.0954-0.948661im  -0.543347-0.0538589im
randn!([rng=default_rng()], A::AbstractArray) -> A

填充数组 A 使用正态分布(平均值0,标准差1)随机数。 另请参阅 兰德功能。

*例子*

julia> randn!(Xoshiro(123), zeros(5))
5-element Vector{Float64}:
 -0.6457306721039767
 -1.4632513788889214
 -1.6236037455860806
 -0.21766510678354617
  0.4922456865251828
randexp([rng=default_rng()], [T=Float64], [dims...])

生成类型的随机数 T 根据尺度为1的指数分布。 可选地生成这样的随机数的数组。 该 基地 模块目前为类型提供了一个实现 漂浮物16, 漂浮物32,和 漂浮64(默认值)。

*例子*

julia> rng = Xoshiro(123);

julia> randexp(rng, Float32)
1.1757717f0

julia> randexp(rng, 3, 3)
3×3 Matrix{Float64}:
 1.37766  0.456653  0.236418
 3.40007  0.229917  0.0684921
 0.48096  0.577481  0.71835
randexp!([rng=default_rng()], A::AbstractArray) -> A

填充数组 A 随机数遵循指数分布(尺度为1)。

*例子*

julia> randexp!(Xoshiro(123), zeros(5))
5-element Vector{Float64}:
 1.1757716836348473
 1.758884569451514
 1.0083623637301151
 0.3510644315565272
 0.6348266443720407
randstring([rng=default_rng()], [chars], [len=8])

创建长度的随机字符串 ,由来自 字符,默认为大小写字母和数字0-9的集合。 可选项 [医]rng 参数指定随机数生成器,请参阅 随机数

*例子*

julia> Random.seed!(3); randstring()
"Lxz5hUwn"

julia> randstring(Xoshiro(3), 'a':'z', 6)
"iyzcsm"

julia> randstring("ACGT")
"TGCTCCTC"

字符 可以是任何类型的字符集合 查尔UInt8 (更高效),提供 兰德可以从中随机挑选字符。

子序列、排列和洗牌

randsubseq([rng=default_rng(),] A, p) -> Vector

返回由给定数组的随机子序列组成的向量 A,其中每个元素 A 以独立概率包括(按顺序) p. (复杂性是线性的 p*长度(A),所以这个功能是有效的,即使 p 是小和 A 是大的。)从技术上讲,这个过程被称为"伯努利抽样" A.

*例子*

julia> randsubseq(Xoshiro(123), 1:8, 0.3)
2-element Vector{Int64}:
 4
 7
randsubseq!([rng=default_rng(),] S, A, p)

兰德苏布塞克,但结果存储在 S (根据需要调整大小)。

*例子*

julia> S = Int64[];

julia> randsubseq!(Xoshiro(123), S, 1:8, 0.3)
2-element Vector{Int64}:
 4
 7

朱莉娅>S
2元素向量{Int64}:
 4
 7
randperm([rng=default_rng(),] n::Integer)

构造长度的随机排列 n. 可选项 [医]rng 参数指定随机数生成器(参见 随机数)。 结果的元素类型与 n.

要随机排列任意向量,请参阅 随机播放洗牌!.

兼容性

朱莉娅1.1在朱莉娅1.1 兰德珀姆 返回向量 veltype(v)==类型(n) 而在朱莉娅1.0 eltype(v)==Int.

*例子*

julia> randperm(Xoshiro(123), 4)
4-element Vector{Int64}:
 1
 4
 2
 3
randperm!([rng=default_rng(),] A::Array{<:Integer})

建造于 A 长度的随机排列 长度(A). 可选项 [医]rng 参数指定随机数生成器(参见 随机数)。 要随机排列任意向量,请参阅 随机播放洗牌!.

*例子*

julia> randperm!(Xoshiro(123), Vector{Int}(undef, 4))
4-element Vector{Int64}:
 1
 4
 2
 3
randcycle([rng=default_rng(),] n::Integer)

构造长度的随机循环置换 n. 可选项 [医]rng 参数指定随机数生成器,请参阅 随机数。 结果的元素类型与 n.

在这里,"循环置换"意味着所有元素都在一个周期内。 如果 n>0,有 可能的循环排列,其被均匀地采样。 如果 n==0, 兰德自行车 返回一个空向量。

兰德自行车!是该函数的就地变体。

兼容性

Julia1.1在Julia1.1及以上, 兰德自行车 返回向量 veltype(v)==类型(n) 而在朱莉娅1.0 eltype(v)==Int.

*例子*

julia> randcycle(Xoshiro(123), 6)
6-element Vector{Int64}:
 5
 4
 2
 6
 3
 1
randcycle!([rng=default_rng(),] A::Array{<:Integer})

建造于 A 长度的随机循环置换 n=长度(A). 可选项 [医]rng 参数指定随机数生成器,请参阅 随机数

在这里,"循环置换"意味着所有元素都在一个周期内。 如果 A 是nonempty(n>0),有 可能的循环排列,其被均匀地采样。 如果 A 是空的, 兰德自行车! 保持不变。

兰德自行车是这个函数的一个变体,它分配了一个新的向量。

*例子*

julia> randcycle!(Xoshiro(123), Vector{Int}(undef, 6))
6-element Vector{Int64}:
 5
 4
 2
 6
 3
 1
shuffle([rng=default_rng(),] v::AbstractArray)

返回随机排列的 v. 可选项 [医]rng 参数指定随机数生成器(参见 随机数)。 排列 v 就地,见 洗牌!. 要获得随机排列的指数,请参阅 兰德珀姆.

*例子*

julia> shuffle(Xoshiro(123), Vector(1:10))
10-element Vector{Int64}:
  5
  4
  2
  3
  6
 10
  8
  1
  9
  7
shuffle!([rng=default_rng(),] v::AbstractArray)

就地版本 随机播放:随机排列 v 就地,可选地提供随机数发生器 [医]rng.

*例子*

julia> shuffle!(Xoshiro(123), Vector(1:10))
10-element Vector{Int64}:
  5
  4
  2
  3
  6
 10
  8
  1
  9
  7

生成器(创建和播种)

Random.default_rng() -> rng

返回默认的全局随机数生成器(RNG),由 兰德-未提供显式RNG时的相关函数。

随机的 模块已加载,默认RNG为_randomly_seeded,通过 随机的。种子!():这意味着每次启动新的julia会话时,第一个调用 兰德() 产生不同的结果,除非 种子!(种子) 被称为第一。

它是线程安全的:不同的线程可以安全地调用 兰德-有关的功能 default_rng() 同时进行,例如 兰德(default_rng()).

注意默认RNG的类型是实现细节。 在不同版本的Julia中,您不应该期望默认的RNG总是具有相同的类型,也不应该期望它会为给定的种子产生相同的随机数流。

兼容性

Julia1.3这个函数是在Julia1.3中引入的。

seed!([rng=default_rng()], seed) -> rng
seed!([rng=default_rng()]) -> rng

重新设置随机数生成器: [医]rng 将给出一个可重复的数字序列当且仅当一个 种子 被提供。 有些RNGs不接受种子,比如 随机装置. 电话后 种子!, [医]rng 等效于用相同种子初始化的新创建对象。 接受种子的类型取决于 [医]rng,但总的来说,整数种子应该起作用。

如果 [医]rng 未指定,它默认为播种共享任务本地生成器的状态。

*例子*

julia> Random.seed!(1234);

julia> x1 = rand(2)
2-element Vector{Float64}:
 0.32597672886359486
 0.5490511363155669

julia> Random.seed!(1234);

julia> x2 = rand(2)
2-element Vector{Float64}:
 0.32597672886359486
 0.5490511363155669

julia> x1 == x2
true

julia> rng = Xoshiro(1234); rand(rng, 2) == x1
true

julia> Xoshiro(1) == Random.seed!(rng, 1)
true

julia> rand(Random.seed!(rng), Bool) # not reproducible
true

julia> rand(Random.seed!(rng), Bool) # not reproducible either
false

julia> rand(Xoshiro(), Bool) # not reproducible either
true
AbstractRNG

随机数生成器的超类型,如 梅尔森特威斯特随机装置.

TaskLocalRNG

N.任务,任务 具有对其任务本地的状态,而不是其线程。 它是在任务创建时从其父任务的状态播种的,但不推进父任务的RNG状态。

作为一个好处, N.任务,任务 非常快,并且允许可重复的多线程模拟(禁止竞争条件),独立于调度程序决策。 只要线程的数量不用于对任务创建做出决策,仿真结果也与可用线程/Cpu的数量无关。 随机流不应依赖于硬件细节,直到字节大小和可能的字大小。

使用或播种任何其他任务的RNG,而不是由 当前任务() 是未定义行为:它大部分时间都可以工作,有时可能会默默地失败。

播种时 TaskLocalRNG()种子!,传递的种子,如果有的话,可以是任何整数。

兼容性

朱莉娅1.11播种 TaskLocalRNG() 用负整数种子至少需要Julia1.11。

兼容性

从Julia1.10开始,Julia1.10任务创建不再提升父任务的RNG状态。

Xoshiro(seed::Union{Integer, AbstractString})
Xoshiro()

Xoshiro256++ 是由David Blackman和Sebastiano Vigna在"Scrambled Linear Pseudorandom Number Generators",ACM Trans中描述的快速伪随机数发生器。 数学。 软件。, 2021. 参考实施可在https://prng.di.unimi.it

除了高速之外,Xoshiro还具有较小的内存占用空间,使其适用于需要长时间保持许多不同随机状态的应用程序。

Julia的Xoshiro实现具有批量生成模式;这从父级种子新的虚拟Prng,并使用SIMD并行生成(即批量流由多个交错的xoshiro实例组成)。 一旦批量请求被服务(并且应该导致没有堆分配),虚拟Prng就会被丢弃。

如果没有提供种子,则会创建一个随机生成的种子(使用系统中的熵)。 查看 种子!重播已有的功能 Xoshiro 对象。

兼容性

Julia1.11传递负整数种子至少需要Julia1.11。

*例子*

julia> using Random

julia> rng = Xoshiro(1234);

julia> x1 = rand(rng, 2)
2-element Vector{Float64}:
 0.32597672886359486
 0.5490511363155669

julia> rng = Xoshiro(1234);

julia> x2 = rand(rng, 2)
2-element Vector{Float64}:
 0.32597672886359486
 0.5490511363155669

julia> x1 == x2
true
MersenneTwister(seed)
MersenneTwister()

创建一个 梅尔森特威斯特 RNG对象。 不同的RNG对象可以有自己的种子,这对于生成随机数的不同流可能是有用的。 该 种子 可以是整数、字符串或 UInt32 整数。 如果没有提供种子,则会创建一个随机生成的种子(使用系统中的熵)。 查看 种子!重播已有的功能 梅尔森特威斯特 对象。

兼容性

Julia1.11传递负整数种子至少需要Julia1.11。

*例子*

julia> rng = MersenneTwister(123);

julia> x1 = rand(rng, 2)
2-element Vector{Float64}:
 0.37453777969575874
 0.8735343642013971

julia> x2 = rand(MersenneTwister(123), 2)
2-element Vector{Float64}:
 0.37453777969575874
 0.8735343642013971

julia> x1 == x2
true
RandomDevice()

创建一个 随机装置 RNG对象。 两个这样的对象将始终生成不同的随机数流。 熵是从操作系统中获得的。

钩入 随机的 空气污染指数

有两种主要正交的扩展方式 随机的 功能:

  1. 生成自定义类型的随机值

  2. 创建新的生成器

1)的API功能相当强大,但相对较新,因此它可能仍然需要在 随机的 模块。 例如,实现一个通常就足够了 兰德 方法,以便使所有其他常用方法自动工作。

2)的API仍然是基本的,并且可能需要比实施者严格需要的更多的工作,以支持通常类型的生成值。

生成自定义类型的随机值

为某些分布生成随机值可能涉及各种权衡。 _pre-computed_值,例如https://en.wikipedia.org/wiki/Alias_method[别名表]用于离散分布,或https://en.wikipedia.org/wiki/Rejection_sampling["`挤压,挤压`"函数]对于单变量分布,可以大大加快采样速度。 应该预先计算多少信息取决于我们计划从分布中提取的值的数量。 此外,一些随机数生成器可以具有各种算法可能想要利用的某些属性。

随机的 模块定义了一个可自定义的框架,用于获取可以解决这些问题的随机值。 每次调用 兰德 通过将方法添加到 取样器,这反过来可以在随机数发生器,表征分布的对象以及重复次数的建议上进行调度。 目前,对于后者, 瓦尔{1} (对于单个样品)和 瓦尔{Inf} (对于任意数量)使用,与 随机的。重复 两者的别名。

返回的对象 取样器 然后用于生成随机值。 实现值的随机生成接口时 X 可以从中采样,实现者应该定义方法

rand(rng, sampler)

对于特定 取样器 返回者 采样器(rng,X,重复).

采样器可以是实现的任意值 兰德(rng,取样器),但对于大多数应用程序,以下预定义的采样器可能就足够了:

  1. 采样类型{T}() 可用于实现从类型绘制的采样器 T (例如 兰德(Int)). 这是默认返回的 取样器 为_types_。

  2. SamplerTrivial(自我) 是一个简单的包装 自我,可用 []. 当不需要预先计算的信息(例如: 兰德(1:3)),并且是默认返回的 取样器 为_values_。

  3. SamplerSimple(自我,数据) 还包含额外的 数据资料 字段,可用于存储任意预先计算的值,这些值应在 取样器.

我们为每个示例提供了示例。 我们在这里假设算法的选择独立于RNG,所以我们使用 [医]抽象 在我们的签名中。

Sampler(rng, x, repetition = Val(Inf))

返回一个采样器对象,该对象可用于从 [医]rngx.

何时 sp=采样器(rng,x,重复), 兰德(rng,sp) 将用于绘制随机值,并应相应地定义。

重复 可以是 瓦尔(1)瓦尔(Inf),并应作为决定预计算量(如适用)的建议。

随机的。采样类型随机的。采样/采样分别是_types_和_values_的默认回退。 随机的。采样器,采样器可用于存储预先计算的值,而无需为此目的定义额外的类型。

SamplerType{T}()

类型的采样器,不包含其他信息。 默认回退 取样器 用类型调用时。

SamplerTrivial(x)

创建一个只是包装给定值的采样器 x. 这是值的默认回退。 该 eltype,eltype 该采样器等于 eltype(x).

推荐的用例是从没有预计算数据的值中采样。

SamplerSimple(x, data)

创建包装给定值的采样器 x数据资料. 该 eltype,eltype 该采样器等于 eltype(x).

推荐的用例是从具有预计算数据的值中采样。

将预计算与实际生成值解耦是API的一部分,用户也可以使用。 作为一个例子,假设 兰德(rng,1:20) 必须在一个循环中重复调用:利用这种解耦的方法如下:

rng = Xoshiro()
sp = Random.Sampler(rng, 1:20) # or Random.Sampler(Xoshiro, 1:20)
for x in X
    n = rand(rng, sp) # similar to n = rand(rng, 1:20)
    # use n
end

这是标准库中也使用的机制,例如通过随机数组生成的默认实现(如 兰德(1:20,10)).

从类型生成值

给定一个类型 T,目前假设如果 兰德(T) 被定义,类型的对象 T 会产生。 采样类型 是types_的_default采样器。 为了定义类型的值的随机生成 T,的 rand(rng::AbstractRNG,::Random.采样类型{T}) 方法应该被定义,并且应该返回值什么 兰德(rng,T) 预计将返回。

让我们来看下面的例子:我们实现一个 死! 类型,具有可变数字 n 边,编号为 1n. 我们要 兰德(死) 产生一个 死! 具有最多20边的随机数(并且至少4):

struct Die
    nsides::Int # number of sides
end

Random.rand(rng::AbstractRNG, ::Random.SamplerType{Die}) = Die(rand(rng, 4:20))

# output

标量和数组方法 死! 现在按预期工作:

julia> rand(Die)
Die(5)

julia> rand(Xoshiro(0), Die)
Die(10)

朱莉娅>兰德(死,3)
3元素向量{Die}:
 模具(9)
 模具(15)
 模具(14)

julia>a=向量{Die}(undef,3);兰德!(一)
3元素向量{Die}:
 模具(19)
 模具(7)
 模具(17)

没有预先计算数据的简单采样器

这里我们定义一个集合的采样器。 如果不需要预先计算的数据,它可以用一个 采样/采样 采样器,实际上是values_的_default回退。

为了定义类型对象的随机生成 S,应定义以下方法: rand(rng::AbstractRNG,sp::Random.采样/采样{S}). 这里, sp 简单地包装类型的对象 S,可透过 sp[]. 继续 死! 例如,我们现在要定义 兰德(d::Die) 产生一个 Int型 对应于其中一个 d侧面:

julia> Random.rand(rng::AbstractRNG, d::Random.SamplerTrivial{Die}) = rand(rng, 1:d[].nsides);

julia> rand(Die(4))
1

julia> rand(Die(4), 3)
3-element Vector{Any}:
 2
 3
 3

给定一个集合类型 S,目前假设如果 兰德(::S) 被定义,类型的对象 eltype(S) 会产生。 在最后一个例子中,一个 向量{Any} 是产生的;原因是 eltype(Die)==任何. 补救办法是定义 基地。eltype(::类型{Die})=Int.

[医]抽象浮 类型

[医]抽象浮 类型是特殊类型的,因为默认情况下,随机值不会在整个类型域中产生,而是在 [0,1). 应为以下方法实现 T<:AbstractFloat: 随机。rand(::AbstractRNG,::Random.采样/采样{Random.CloseOpen01{T}})

具有预先计算数据的优化采样器

考虑离散分布,其中数字 1:n 用给定的概率总和为一绘制。 当从这个分布中需要许多值时,最快的方法是使用https://en.wikipedia.org/wiki/Alias_method[别名表]。 我们在这里没有提供构建这样一个表的算法,但是假设它在 make_alias_table(概率) 相反, draw_number(rng,alias_table) 可以用来从中得出一个随机数。

假设分布由

struct DiscreteDistribution{V <: AbstractVector}
    probabilities::V
end

并且我们_always_想要构建一个别名表,而不管需要多少值(我们在下面学习如何自定义)。 方法

Random.eltype(::Type{<:DiscreteDistribution}) = Int

function Random.Sampler(::Type{<:AbstractRNG}, distribution::DiscreteDistribution, ::Repetition)
    SamplerSimple(distribution, make_alias_table(distribution.probabilities))
end

应该定义为返回一个带有预先计算数据的采样器,然后

function rand(rng::AbstractRNG, sp::SamplerSimple{<:DiscreteDistribution})
    draw_number(rng, sp.data)
end

将用于绘制值。

自定义采样器类型

采样器,采样器 类型对于具有预计算数据的大多数用例来说是足够的。 但是,为了演示如何使用自定义采样器类型,我们在这里实现类似于 采样器,采样器.

回到我们的 死! 例子:: 兰德(::死) 使用范围内的随机生成,因此存在此优化的机会。 我们叫我们的自定义采样器 采样器.

import Random: Sampler, rand

struct SamplerDie <: Sampler{Int} # generates values of type Int
    die::Die
    sp::Sampler{Int} # this is an abstract type, so this could be improved
end

Sampler(RNG::Type{<:AbstractRNG}, die::Die, r::Random.Repetition) =
    SamplerDie(die, Sampler(RNG, 1:die.nsides, r))
# the `r` parameter will be explained later on

rand(rng::AbstractRNG, sp::SamplerDie) = rand(rng, sp.sp)

现在可以用 sp=采样器(rng,die),及使用 sp 而不是 死! 在任何 兰德 涉及电话 [医]rng. 在上面简单的例子中, 死! 不需要存储在 采样器 但在实践中往往是这种情况。

当然,这种模式是如此频繁,以至于上面使用的helper类型,即 随机的。采样器,采样器,是可用的,为我们节省了 采样器:我们本可以用:

Sampler(RNG::Type{<:AbstractRNG}, die::Die, r::Random.Repetition) =
    SamplerSimple(die, Sampler(RNG, 1:die.nsides, r))

rand(rng::AbstractRNG, sp::SamplerSimple{Die}) = rand(rng, sp.data)

这里, sp.数据资料 是指在调用的第二个参数 采样器,采样器 构造函数(在这种情况下等于 采样器(rng,1:die.nsides,r)),而 死! 对象可以通过 sp[].

采样器,任何自定义采样器必须是 取样器{T} 哪里 T 是生成值的类型。 请注意 SamplerSimple(x,data)isa采样器{eltype(x)},所以这限制了第一个参数的内容 采样器,采样器 可以(建议使用 采样器,采样器 就像在 死! 例如,在哪里 x 在定义一个 取样器 法)。 同样地, 采样器(x)isa采样器{eltype(x)}.

另一种帮助器类型目前可用于其他情况, 随机的。取样标签,但被视为内部API,并且可以在没有适当弃用的情况下随时中断。

使用不同的算法生成标量或数组

在某些情况下,是否希望只生成少数值或大量值会对算法的选择产生影响。 这是用第三个参数处理的 取样器 构造函数。 假设我们定义了两种帮助器类型 死!,说 采样器1 它应该用于生成只有几个随机值,以及 采样器,采样器 为许多值。 我们可以使用这些类型如下:

Sampler(RNG::Type{<:AbstractRNG}, die::Die, ::Val{1}) = SamplerDie1(...)
Sampler(RNG::Type{<:AbstractRNG}, die::Die, ::Val{Inf}) = SamplerDieMany(...)

當然。, 兰德 还必须在这些类型上定义(即 rand(::AbstractRNG,::SamplerDie1)rand(::AbstractRNG,::SamplerDieMany)). 请注意,像往常一样, 采样/采样采样器,采样器 如果不需要自定义类型,可以使用。

注: 取样器(rng,x) 只是一个简写 取样器(rng,x,Val(Inf)),而 随机的。重复 是一个别名 联盟{Val{1},瓦尔{Inf}}.

创建新的生成器

API尚未明确定义,但作为经验法则:

  1. 任何 兰德 生产"基本"类型的方法(等位类型 整数和浮动类型 基地)应该为这个特定的RNG定义,如果需要的话;

  2. 其他文件 兰德 接受 [医]抽象 应该开箱即用,(提供来自1的方法)所依赖的是实现的),但是如果有优化的空间,当然可以专门用于此RNG;

  3. 副本 对于伪RNGs应该返回一个独立的副本,该副本在以相同的方式调用时从该点生成与原始完全相同的随机序列。 当这不可行时(例如基于硬件的RNGs), 副本 不得实施。

关于1),a 兰德 方法可能会自动工作,但它没有正式支持,并且可能在随后的版本中没有警告就会中断。

要定义一个新的 兰德 假设的方法 没药,没药 生成器和值规范 s (例如 s==Int,或 s==1:10)类型 S==类型(s)S==类型{s} 如果 s 是一个类型,必须定义与我们之前看到的相同的两个方法:

  1. 采样器(::类型{MyRNG},::S,::重复),它返回一个类型为say的对象 取样器

  2. 兰德(Rng::MyRNG,sp::取样器)

这可能会发生 采样器(rng::AbstractRNG,::S,::重复) 已在 随机的 模块。 然后,在实践中可以跳过步骤1)(如果一个人想专门生成这个特定的RNG类型),但是相应的 取样器 类型被认为是内部细节,可以在没有警告的情况下更改.

专业阵列生成

在某些情况下,对于给定的RNG类型,使用专用方法生成随机值数组比仅使用之前解释的解耦技术更有效。 例如,对于 梅尔森特威斯特,它在数组中本机写入随机值。

为了实现这种专业化 没药,没药 而对于一个规范 s,产生类型的元素 S,可以定义如下方法: 兰德!(rng::MyRNG,a::AbstractArray{S},::采样器),在哪里 取样器 返回的采样器的类型是 取样器(MyRNG,s,Val(Inf)). 而不是 抽象阵列,可以仅为子类型实现功能,例如 数组{S}. 的非变异数组方法 兰德 会在内部自动调用这个特化。

再现性

通过使用给定种子初始化的RNG参数,您可以在多次运行程序时重现相同的伪随机数序列。 然而,Julia的一个小版本(例如1.3到1.4)_may change_从特定种子生成的伪随机数字序列。 (即使由低级函数产生的序列如 兰德不改变,更高级别的功能的输出像 兰德苏布塞克可能会因算法更新而改变。)理由:保证伪随机流永远不会改变禁止许多算法改进。

如果您需要保证随机数据的精确再现性,建议您简单地保存数据(例如作为科学出版物中的补充附件)。 (当然,您也可以指定特定的Julia版本和包清单,特别是如果您需要位再现性。)

依赖_specific_"随机"数据的软件测试通常也应该保存数据,将其嵌入到测试代码中,或者使用第三方软件包,例如https://github.com/JuliaRandom/StableRNGs.jl[StableRNGs.jl].另一方面,应该通过_most_随机数据的测试(例如测试 A\(A*x)≈x 对于随机矩阵 A=randn(n,n))可以使用具有固定种子的RNG,以确保简单地多次运行测试不会由于非常不可能的数据(例如极其病态的矩阵)而遇到失败。

从中抽取随机样本的统计_distribution_保证在任何次要Julia版本中都是相同的。