gpt4 book ai didi

c++ - 决定在 random_device 和 seed_seq 之间生成多个随机数序列的种子

转载 作者:搜寻专家 更新时间:2023-10-31 00:53:47 25 4
gpt4 key购买 nike

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

在这两个之中,有没有首选的方法?为什么?如果是后者,是否存在用于生成 seed_seq 对象的最佳 random_device“源”数量?

有没有比我上面概述的这两种实现方式更好的随机数生成方法?

谢谢!


编辑

(希望)针对多个分布的 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};

最佳答案

std::seed_seq 通常用于您不信任默认实现来正确初始化您正在使用的引擎状态的情况。

在许多 ≥C++11 实现中,std::default_random_enginestd::mt19937 的别名,它是 Mersenne Twister 伪随机数的特定变体生成算法。 Looking at the specification for std::mt19937 ,我们看到它的状态大小为 624 个无符号整数,足以容纳它打算包含的 19937 位状态(这就是它的名称)。传统上,如果您仅使用单个 uint32_t 值(如果 rd 为一个 std::random_device 对象),那么它的绝大部分状态都未初始化。

现在,如果您使用单个 uint32_t 值构造一个 std::mt19937 (如 std::default_random_engine engine{rd()};),该实现需要通过置换原始种子值来初始化其余状态,因此虽然单次调用 rd () 产生有限范围内的实际不同引擎状态,但它至少足以正确初始化引擎。这将产生一个“优质”的随机数生成器。

但是如果您担心引擎没有被正确播种,无论是出于密码原因(尽管请注意 std::mt19937 本身在密码学上是不安全的!)或者仅仅是出于统计原因,您可以使用 std::seed_seq 手动指定整个状态,使用 rd() 填充每个值,这样你就可以保证相对置信度引擎已正确播种。

对于随意使用,或并非绝对需要获得高质量随机数的场景,只需调用一次 std::random_device::operator() 即可进行初始化。

如果你想使用 std::seed_seq,确保你设置正确(你的原始代码中的例子绝对不正确,至少对于 std::mt19937 ,实际上会产生比简单使用 rd() 更糟糕的结果!)。 This post on CodeReview包含经过适当审查的代码。

编辑:

对于Mersenne Twister的预定义模板,状态大小始终为19968位,比实际需要的略多,但也是使用uint32_t值可以完整表示范围的最小值.这算出 624 个 Word,每个 32 位。因此,如果您计划使用种子序列,您将通过对 rd() 的 624 次调用正确地初始化它:

//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 的非标准实例化,则可以通过将其 state_size 乘以查询该特定情况所需的状态大小它的 word_size 然后除以 32。

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() 即可初始化,因此种子序列是不必要。我不确定 std::subtract_with_carry_engine 或其关联的使用 std::discard_block_engine 是如何工作的,但它似乎也像它们一样只包含一个状态的Word

关于c++ - 决定在 random_device 和 seed_seq 之间生成多个随机数序列的种子,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47562404/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com