在random_device和seed_seq之间做出决定,为多个随机数序列生成种子

时间:2017-11-29 21:47:57

标签: c++ c++11 random

当编写需要多个独立随机数分布/序列的代码时(下面有两个例子),似乎有两种典型的方法来实现(伪)随机数生成。一种是简单地使用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};

1 个答案:

答案 0 :(得分:3)

如果您不相信默认实现以正确初始化您正在使用的引擎的状态,则通常会使用

std::seed_seq

在许多≥C++ 11实现中,std::default_random_enginestd::mt19937的别名,它是Mersenne Twister伪随机数生成算法的特定变体。 Looking at the specification for std::mt19937,我们看到它有一个大小为624的无符号整数状态,足以保存它所包含的19937位状态(这就是它的名字)。传统上,如果您只使用一个uint32_t值(这是您从调用rd()一次获得的结果,如果rdstd::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 国家。