许多人都像这样给他们的Mersenne Twister引擎添加种子:与我们想要播种的19937位状态空间相比,它很小。的确,如果我找到生成的第一个数字,则我的PC(英特尔i7-4790K)仅需要大约10分钟即可搜索所有32位数字并找到使用过的种子。 (我知道MT并不是加密的RNG,但我只是这样做是为了了解这些天实际32位到底有多小。)

我正在尝试构建一个函数以正确地进行种子一个unsigned int并提出了这个建议:但是,我了解到RNG的问题通常很细微。

提供的mt19937在我的系统上产生良好的随机数据,代码是否为我提供了正确的种子std::random_device? />

评论

附带说明:当前,std :: random_device不能在每个平台上正常工作(我在看您的MinGW)。如果要使用Boost方式,则可以改用boost :: random_device。

#1 楼



首先,为什么对已知长度比较小的序列使用std::vector?原始数组或std::array足以满足需要,并且避免了任何动态分配。使用std::mt19937::state_size代替手动指定624。为什么要使用lambda?一个简单的std::ref(source)就足够了。使用计数和转换迭代器,如“序列迭代器?增强中没有一个吗?”中所述。
这并不简单,但可能更有效: >

#2 楼

我或多或少编写了完全相同的函数供自己使用,因此我认为它确实很棒。 ;-)

我将做两件事(仅样式,而不是安全性):


不要硬编码魔术数字624。 std::mersenne_twister_engine template class具有static constexpr成员word_size,您可以代替使用。同样,最好使用unsigned代替result_type
考虑将函数设置为template,以便它也可以用于std::mt19937_64(以及其他兼容的引擎)。


评论


\ $ \ begingroup \ $
对模板,类,静态和constexpr使用等宽标记是否可以提高您所说内容的清晰度?如果是这样,则您错过,使用和使用。
\ $ \ endgroup \ $
–律师
15年10月31日在6:47

\ $ \ begingroup \ $
@bcrist不,因为在我使用这些单词的地方,我使用它们的自然语言含义是指不引用C ++关键字。如果我谈论的是for循环或using指令,则可以通过使用适当的字体来明确说明。同样,如果我引用C ++数据类型,我会写int,如果我引用数字的数学概念,我会写“ integer”。
\ $ \ endgroup \ $
– 5gon12eder
15年10月31日在10:43

\ $ \ begingroup \ $
我试图幽默地指出的是,模板类,静态和constexpr不仅是对C ++关键字的引用,它们还是任何有能力的C ++开发人员词汇中的自然语言概念。重要的是概念,而不是代表它的令牌。此外,在描述mersenne_twister_engine的情况下,使用class代替class实际上是不正确的。该标准不强制其(或任何其他std类型)使用class关键字。如果需要,实现也可以轻松使用struct。
\ $ \ endgroup \ $
–律师
15年10月31日在16:25

\ $ \ begingroup \ $
@bcrist:通过更多的口语化,使方法更加精确,倍受赞誉。
\ $ \ endgroup \ $
–重复数据删除器
2015年11月1日在18:16

#3 楼

我可以提供正确初始化mt19937随机数生成器的另一种可能性: ,F。Panneton,P。L'Ecuyer和M. Matsumoto在AVM TOMS第32卷,2006年3月,第1-16页中),特别是第7章中的图4,即使在其扭转状态下,也可以得到高质量的梅森捻线机初始化变量只有几位设置为非零值。在初始化阶段期间/之后,您需要执行大约700000个随机数生成(或扭曲)操作。当然,它的速度要慢得多,但是随机数生成器初始化应该始终仅在每个二进制文件中触发一次,因此在许多情况下这可以忽略不计。重现结果,因为不需要保存624个数字,而只需保存一个。原始解决方案不能保证此属性,而是取决于源随机数生成器的质量及其与mt19937的交互。

评论


\ $ \ begingroup \ $
感谢您撰写评论。这也是我的第一个想法,可以使用std :: discard_block_engine :: discard()轻松实现。
\ $ \ endgroup \ $
–爱德华
2015年11月2日,12:46

\ $ \ begingroup \ $
幸运的是,您理解了我的意思(std :: mersenne_twister :: discard())而不是我写的内容!在运行2.3GHz双核处理器的64位Linux机器上,这花费不到5毫秒。
\ $ \ endgroup \ $
–爱德华
15年11月2日在15:49

\ $ \ begingroup \ $
老化是好的,但是它并不能改变您仅用一个整数就可以播种624字节状态的事实。
\ $ \ endgroup \ $
–约翰·伦德伯格(Johan Lundberg)
16-4-20在19:46

\ $ \ begingroup \ $
没关系。在使用32位int进行种子设定并跳过700k迭代之后,仍然只有2 ^ 32种可能的内部状态。您所指的论文是谈论从不良的内部状态恢复,这不是手头的问题-如果将单个数字传递给Mt构造函数,则它使用种子序列填充所有内部状态,因此您不会从几乎全零状态开始。但是这个过程(就像丢弃一样)是确定性的,这就是原始问题所要解决的问题,而丢弃并不能解决问题。
\ $ \ endgroup \ $
–战争
16年6月30日在13:01

\ $ \ begingroup \ $
@etarion他的话“(我知道MT不是加密的RNG,我只是这样做是为了了解这些天真正的32位小。他的随机数的安全性方面。而且,如果您不寻求安全的RNG,则我的解决方案就可以正常工作了(无论如何您都无法使用MT ...)。
\ $ \ endgroup \ $
– Nils_M
16年7月1日在16:05

#4 楼

是的,从技术上讲,它确实为您提供了“正确”的std::mt19937 PRNG种子。但是,这种方法比较笨拙-公平地说,尽管您只是将其作为示例代码提供。您无需使用像(624)这样的显式幻数-即可获取所需的19937位。成员X::rng具有完全随机的19937位状态,可以使用。当然,这取决于std::random_device的策略。当使用(2^19937 - 1)本身时,不仅具有一个基本上取之不尽的时间,而且还具有rng位的随机状态向量。与(2 ^ -32)相反,现在重新创建相同的初始PRNG状态的可能性为〜(2 ^ -19937)。此外,此代码从理论上可以达到所有mt19937状态。

评论


\ $ \ begingroup \ $
我认为您的代码假定std :: seed_seq :: result_type == random_engine :: result_type。通常情况并非如此(例如,对于std :: mt19937_64)。在此代码中,您的种子序列将使用64个无符号整数(= std :: random_device :: value_type)生成,这还不够熵。我错了吗?
\ $ \ endgroup \ $
–康拉德·鲁道夫(Konrad Rudolph)
20 Mar 17 '20 at 10:28



\ $ \ begingroup \ $
我的观点是,您的代码故意保留了随机引擎的通用性(尽管缺少使其成为模板参数),但您的种子代码特定于mt19937。如果特异性是有意的(很好),则在通用的typedef后隐藏与特定RNG的连接是很危险的。
\ $ \ endgroup \ $
–康拉德·鲁道夫(Konrad Rudolph)
20 Mar 20 '20在8:37

\ $ \ begingroup \ $
那就是说,我应该明确指出,我实际上并没有对您的代码提出质疑,我的评论实际上只是一个问题,目的是要确认我的理解,即需要对64位MT19937进行不同的植入(例如更多随机位)。
\ $ \ endgroup \ $
–康拉德·鲁道夫(Konrad Rudolph)
20-3-20在8:42



\ $ \ begingroup \ $
@KonradRudolph-现在我明白你的意思了。你是对的。我的代码看起来“通用”,但不是。这可能会引起混乱。这可能是一个“示例”,但我不希望有人将其用于“货物崇拜”编码。
\ $ \ endgroup \ $
–布雷特·黑尔(Brett Hale)
20-3-20在9:32