Ruby';兰德?

Ruby';兰德?,ruby,random,mersenne-twister,random-seed,Ruby,Random,Mersenne Twister,Random Seed,Ruby将PRNGs实现为“一个周期为2**19937-1的改良Mersenne捻线器” 我对MT的理解是,它在2^32个不同的种子上运行。让我困惑的是,Random.new(seed)接受任意大的数字,比如Random.new(2**100) 但是,我无法找到(逻辑)冲突: Random.new(1).rand(10**5) == Random.new(2**32-1).rand(10**5) => false Random.new(1).rand(10**5) == Random.ne

Ruby将PRNGs实现为“一个周期为2**19937-1的改良Mersenne捻线器”

我对MT的理解是,它在2^32个不同的种子上运行。让我困惑的是,
Random.new(seed)
接受任意大的数字,比如
Random.new(2**100)

但是,我无法找到(逻辑)冲突:

Random.new(1).rand(10**5) == Random.new(2**32-1).rand(10**5) => false
Random.new(1).rand(10**5) == Random.new(2**32).rand(10**5) => false
Random.new(1).rand(10**5) == Random.new(2**32+1).rand(10**5) => false
鉴于我们希望利用MT的最大种子范围,即我们希望使用尽可能多的不同种子,同时避免与两个不同种子发生碰撞,那么什么种子范围实现了这一点


我试图理解Ruby的random实现中发生了什么,但没有走得太远

Mersenne捻线器序列长度为
2**(624*32-1)-1
,种子值用于设置PRNG的内部状态,该状态与该序列中的位置直接相关

最容易找到的repeat似乎是every
2**(624*32)
,可以显示为这样工作:

 repeat_every =  2 ** ( 624 * 32 )

 start_value = 5024214421  # Try any value

 r1 = Random.new( start_value )

 r2 = Random.new( start_value + repeat_every )

 r17 = Random.new( start_value + 17 * repeat_every )

 r23 = Random.new( start_value + 23 * repeat_every )

 r1.rand == r2.rand  
 # true

 r17.rand == r23.rand  
 # true
或者试试这个:

 repeat_every =  2 ** ( 624 * 32 )

 start_value = 5024214421  # Try any value

 r1 = Random.new( start_value )

 r2 = Random.new( start_value + repeat_every )

 Array.new(10) { r1.rand(100) }
 # => [84, 86, 8, 58, 5, 21, 79, 10, 17, 50]

 Array.new(10) { r2.rand(100) }
 # => [84, 86, 8, 58, 5, 21, 79, 10, 17, 50]
重复值与Mersenne捻线机的工作方式有关。MT的内部状态是624个32位无符号整数的数组。您链接的Ruby源代码将Ruby Fixnum打包到一个数组中-magic命令是

rb_integer_pack(seed、buf、len、sizeof(uint32_t)、0、,
整数_PACK_LSWORD_FIRST |整数_PACK_NATIVE_BYTE_ORDER);
然而,这并不容易处理,它是在
internal.h
中定义的,所以只有在使用Ruby解释器时才真正可以访问。无法从普通C扩展中访问此函数

压缩整数然后通过函数
init\u by\u array
加载到MT的内部状态。这是一个看起来相当复杂的函数-压缩种子值并不是直接写入状态,而是逐项生成状态,使用各种XOR、加法和交叉引用前面的值(这里的Ruby源代码也添加到压缩数组的索引位置,注释)“非线性”,我认为这是对标准MT的参考修改之一)


请注意,MT序列的大小小于
2**(624*32)
-我在这里显示的
repeat\u every
值一次跳过2个序列,但最容易找到重复种子值,因为很容易看到它如何将内部状态设置为完全相同(因为种子的数组表示中的前624项是相同的,这就是以后使用的全部内容)。其他种子值也将产生相同的内部状态,但这种关系是一种复杂的映射,将19938位空间中的每个整数与另一个整数配对,从而为MT创建相同的状态。

它在内部使用624个32位整数的向量(我认为-至少这是MT的默认实现将使用的)。您链接的代码将大整数拆分为一个32位整数数组,该数组为初始状态向量提供数据。注意624*32=19968…种子也是的“状态”MT@NeilSlater:等等。这是否意味着Random.new(1)将在某个点开始生成与Random.new(1000)相同的序列?我想知道
rb\u to\u int(vseed)
是否进行了一些规范化?是的,有一个重复的单一序列,种子只是在不同的位置拾取。但是,这些状态通常不像小种子那样紧密。在实践中,您不太可能看到冲突-如果您只是将生成器设置为从
srand(1)
vs
srand(1000)运行
并且每秒得到10亿个结果,当序列之间出现重叠时,我们都早就死了。可用空间很大。这与知道“我在序列中的什么位置”是不同的问题,即看到足够的变化来识别状态。