当编写需要多个独立随机数分布/序列的代码时(下面有两个例子),似乎有两种典型的方法来实现(伪)随机数生成。一种是简单地使用random_device
对象为两个独立引擎生成两个随机种子:
std::random_device rd;
std::mt19937 en(rd());
std::mt19937 en2(rd());
std::uniform_real_distribution<> ureald{min,max};
std::uniform_int_distribution<> uintd{min,max};
另一个涉及使用random_device
对象使用多个&#34;来源&#34;来创建seed_seq
对象。随机性:
// NOTE: keeping this here for history, but a (hopefully) corrected version of
// this implementation is posted below the edit
std::random_device rd;
std::seed_seq seedseq{rd(), rd(), rd()}; // is there an optimal number of rd() to use?
std::vector<uint32_t> seeds(5);
seedseq.generate(seeds.begin(), seeds.end());
std::mt19937 en3(seeds[0]);
std::mt19937 en4(seeds[1]);
std::uniform_real_distribution<> ureald{min,max};
std::uniform_int_distribution<> uintd{min,max};
在这两个中,有一个首选方法吗?为什么?如果是后者,是否有random_device
&#34;来源的最佳数量&#34;用于生成seed_seq
对象?
有没有比我上面概述的这两种实现方法更好的随机数生成方法?
谢谢!
(希望)更正了多个发行版的seed_seq
实施版本:
std::random_device rd;
std::seed_seq seedseq1{rd(), rd(), rd()}; // is there an optimal number of rd() to use?
std::seed_seq seedseq2{rd(), rd(), rd()};
std::mt19937 en3(seedseq1);
std::mt19937 en4(seedseq2);
std::uniform_real_distribution<> ureald{min,max};
std::uniform_int_distribution<> uintd{min,max};
答案 0 :(得分:3)
std::seed_seq
。
在许多≥C++ 11实现中,std::default_random_engine
是std::mt19937
的别名,它是Mersenne Twister伪随机数生成算法的特定变体。 Looking at the specification for std::mt19937
,我们看到它有一个大小为624的无符号整数状态,足以保存它所包含的19937位状态(这就是它的名字)。传统上,如果您只使用一个uint32_t
值(这是您从调用rd()
一次获得的结果,如果rd
是std::random_device
对象),那么您“让绝大多数国家都没有初始化。
现在,对于任何人如果对他们播种不良的Mersenne Twister引擎感到恐慌的好消息是,如果你构造一个std::mt19937
的{{1}}值uint32_t
,实现需要通过置换原始种子值来初始化状态的其余部分,因此当std::default_random_engine engine{rd()};
的单个调用产生有限范围的实际不同引擎状态时,仍然足以至少正确地初始化引擎。这将产生一个“质量好”的随机数发生器。
但是如果您担心引擎没有正确播种,可能是出于加密原因(虽然注意rd()
本身并非加密安全!)或仅仅出于统计原因,您可以使用{{1手动指定整个状态,使用std::mt19937
填写每个值,这样就可以保证发动机正确播种的相对置信度。
对于临时使用,或者对于获得高质量随机数不是绝对必要的情况,只需通过一次调用std::seed_seq
进行初始化就可以了。
如果您想使用rd()
,请确保正确设置(原始代码中的示例肯定不正确,至少对于std::random_device::operator()
而言,实际上会产生更糟糕的结果而不是简单地使用std::seed_seq
!)。 This post on CodeReview包含经过适当审查的代码。
对于Mersenne Twister的预定义模板,状态大小始终为19968位,略高于实际需要的大小,但也是使用std::mt19937
值可完全表示范围的最小值。这适用于每个32位的624个字。因此,如果您打算使用种子序列,则可以使用624次调用将其正确初始化为rd()
:
uint32_t
如果您正在使用rd()
的非标准实例化,则可以通过将其//Code copied from https://codereview.stackexchange.com/questions/109260/seed-stdmt19937-from-stdrandom-device
std::vector<uint32_t> random_data(624);
std::random_device source;
std::generate(random_data.begin(), random_data.end(), std::ref(source));
std::seed_seq seeds(random_data.begin(), random_data.end());
std::mt19937 engine(seeds);
//Or:
//std::mt19937_64 engine(seeds);
乘以其std::mersenne_twister_engine
然后再划分来查询该特定情况所需的州大小到了32。
state_size
对于其他引擎类型,您需要根据具体情况对其进行评估。 word_size
及其预定义变体使用其单词大小的单个整数,因此它们只需要using mt_engine = std::mersenne_twister_engine</*...*/>;
constexpr size_t state_size = mt_engine::state_size * mt_engine::word_size / 32;
std::vector<uint32_t> random_data(state_size);
std::random_device source;
std::generate(random_data.begin(), random_data.end(), std::ref(source));
std::seed_seq seeds(random_data.begin(), random_data.end());
mt_engine engine (seeds);
的单个调用来初始化,因此种子序列是不必要的。我不确定std::linear_congruential_engine
或其关联的rd()
是如何工作的,但似乎就像它们也只包含一个 Word 国家。