在Julia中检索RNG种子

10
在Julia中,全局随机数生成器的种子可以使用以下方式设置:
srand(SEED)

我该如何检索全局随机数生成器的种子或当前状态,以便稍后再次使用?

目标是在任何给定时间点获取RNG的状态,并在不知道最初的种子或中间发生的所有调用RNG的情况下,在不同会话中重新创建它。

例如,R允许通过以下方式访问当前种子

.Random.seed

我希望在 Julia 中存在一种等效的方法。


请阅读精彩的手册:http://docs.julialang.org/en/release-0.3/stdlib/base/#random-numbers。 - Jørgen R
2
@jurgemaister 这部分涉及到设置种子。我找不到任何关于如何取回它的提及。 - Julian
@jurgemaister:你似乎没有理解设置种子和后来找出种子设置值之间的区别。 - DSM
@jurgemaister 我的问题与如何设置种子或生成随机数无关。它是关于如何在特定时间点检索RNG的状态,以便我可以在不同的会话中重新创建相同的状态。 - Julian
2
正确阅读问题也很重要。 - Jørgen R
1
获取随机种子的当前方法(Julia 1.7/1.8)是什么?这里发布的解决方案都不起作用:Random.default_rng().seed 显示 type TaskLocalRNG has no field seed;对于 Random.GLOBAL_RNG.seed 同样如此;在调用 rand() 后,Random.GLOBAL_SEED 没有更新,因此似乎还有更多隐藏的随机状态。 - ForceBru
6个回答

6

Base.Random.RANDOM_SEED是获取种子的好帮手:

julia> srand(10)

julia> Base.Random.RANDOM_SEED
1-element Array{Uint32,1}:
 0x0000000a

julia> srand(1)

julia> Base.Random.RANDOM_SEED
1-element Array{Uint32,1}:
 0x00000001

julia> srand(0xF)

julia> Base.Random.RANDOM_SEED
1-element Array{Uint32,1}:
 0x0000000f

这个没有正式的文档,但是源代码很容易阅读。我不确定如何获取当前RNG的状态,但它可能在dSFMT模块中。


5
现在可以通过 Base.Random.GLOBAL_RNG.seed 访问,而不是 Base.Random.RANDOM_SEED - Chris
3
@Chris,这些在Julia 1.7/1.8中都不再适用:Base.Random现在是自己的包,但Random.GLOBAL_RNG没有seed字段,而Random.RANDOM_SEED未定义。 - ForceBru

4
您应该像这样获取种子
reinterpret(Int32, Base.Random.GLOBAL_RNG.seed)

测试:

julia> srand(123456789);

julia> reinterpret(Int32, Base.Random.GLOBAL_RNG.seed)
1-element Array{Int32,1}:
 123456789

为了保存和恢复完整的rng状态,您可以简单地存储整个Base.Random.GLOBAL_RNG对象。一种简单的方法是使用JLD包。
在我的私人包中,我手动将rng状态保存/读取到HDF5中,请参见这里
编辑:这当然是@IainDunning答案的更明确版本。

2

使用专门的MersenneTwister算法,使用显式变量(而不是默认的隐式全局变量)提供您所需的功能:

newmt = Base.Random.MersenneTwister(123)
randvec = [rand(newmt) for i=1:100]
# save state now
savestate = deepcopy(newmt.state)
randvec2 = [rand(newmt) for i=1:20]
# rewind state to old state
newmt.state = savestate
randvec3 = [rand(newmt) for i=1:20]
if randvec2==randvec3
    println("works!")
end

deepcopy让我有点困惑。另外,要访问全局随机生成器状态可能更容易,但可能需要使用ccall调用libdSFMT库(请参见Base中的random.jldSFMT.jl)。


0

为了在函数和一般情况下更好地控制随机生成器,

RND = srand(0)
function coolfun()
    println(RND.idx)
    output = srand(RND, 100)
    ...
end

0
一个显而易见的解决方案是在调用 srand(seed) 之前保存种子值。
另外,如果你知道正在使用的 RNG 不是加密安全的,那么你可以计算出它产生的伪随机数的值。例如,请参阅 破解线性同余生成器

Julia中的随机数生成使用Mersenne Twister库。 - Jørgen R

0

由于此问题的其他答案涉及旧版本的Julia并且不再起作用,因此我决定发布一个新答案。

在Julia版本>= 1.0中,字段Base.Random.GLOBAL_RNG不再存在。在Julia 1.0中,Random包中有一个字段Random.GLOBAL_RNG(不再是Base的一部分)。从那里检索种子并像https://dev59.com/s18d5IYBdhLWcg3wzEz4#47900426中描述的设置它不再起作用。

在Julia 1.4和1.5中,有函数Random.default_rng()。我成功地使用以下代码获得了保存和恢复随机生成器状态的期望行为:

julia> import Random

julia> Random.seed!(1);

julia> rand(Int)
6592875438459116351

julia> rng = copy(Random.default_rng());

julia> rand(Int)
1969495256574935408

julia> Random.seed!(2);

julia> rand(Int)
6712802529292398593

julia> copy!(Random.default_rng(), rng);

julia> rand(Int)
1969495256574935408

在Julia 1.0中,可以使用 Random.GLOBAL_RNG 代替 Random.default_rng()以获得相同的结果。
经过查看Julia源代码后,我想出了这个解决方案,因此我不知道这个解决方案在版本之间会有多稳定。 (函数 Random.default_rng()未被导出或记录。)
我没有成功地提取仅作为单个数字的种子,该数字可用于调用 Random.seed!以恢复正在运行的随机数生成器的状态。我也对建议或讨论是否可能感兴趣。

1
在 Julia 1.6 中,Random.seed!(Random.default_rng().seed) 是有效的,而在 Julia 1.7 中,这些信息存储在 Random.GLOBAL_SEED 中。 - Lufu
我提出的解决方案可以跨版本使用,也适用于 Julia 1.7。 - esel
@Lufu,调用rand()后,Random.GLOBAL_SEED没有更新,但是在后续的调用中,rand()产生了不同的输出,这表明它正在更新某个_不同的_内部状态。 - ForceBru
没错,种子仅用于定义随机数流,随机数的抽取取决于随机数生成器的状态。 - Lufu
@Lufu,那么如何获取RNG的状态呢?在Julia 1.7中Random.default_rng().seed无法运行,因为没有seed属性。 - ForceBru
你需要它做什么?如果你需要相同的状态,你可以传递RNG本身,复制它或序列化它。 - Lufu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接