繁体   English   中英

在C ++中使用random

[英]Use of random in C++

这些代码是否等同于“随机性”?

1)

std::vector<int> counts(20);
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 19);

for (int i = 0; i < 10000; ++i) {
    ++counts[dis(gen)];
}

2)

std::vector<int> counts(20);
std::random_device rd;
std::mt19937 gen(rd());

for (int i = 0; i < 10000; ++i) {
    std::uniform_int_distribution<> dis(0, 19);
    ++counts[dis(gen)];
}

3)

std::vector<int> counts(20);
std::random_device rd;

for (int i = 0; i < 10000; ++i) {
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, 19);
    ++counts[dis(gen)];
}

4)

std::vector<int> counts(20);

for (int i = 0; i < 10000; ++i) {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, 19);
    ++counts[dis(gen)];
}

在std :: random_device的文档中,可以说多个std :: random_device对象可能生成相同的数字序列,因此代码4是坏的,不是吗?

而对于其他代码?

如果我需要为多个不相关的东西生成随机值,我是否需要创建不同的生成器或者我可以保持相同吗?

1)

std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> disInt(0, 10);
std::uniform_float_distribution<> disFloat(0, 1.0f);

// Use for one stuff
disInt(gen);

// Use same gen for another unrelated stuff
disFloat(gen);

2)

std::random_device rd1, rd2;
std::mt19937 gen1(rd1()), gen2(rd2());
std::uniform_int_distribution<> disInt(0, 10);

// Use for one stuff
disInt(gen1);

// Use another gen for another unrelated stuff
disFloat(gen2);

随机生成器的要点是保持算法的状态 ,以便基于特定随机种子产生可重复的伪随机数字序列。

随机设备的要点是为随机发生器提供随机种子

如果您尝试为每个随机值播种新生成器,则不再使用随机生成器算法提供的随机性。 相反,您偏向生成器以依赖随机设备本身的随机性。

因此, 建议使用示例#3和#4。

生成随机序列的正确方法是示例#1:

std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 19);

for (int i = 0; i < 10000; ++i) {
    int foo = dis(gen);
}

示例#2也是正确的,但在循环内构造uniform_int_distribution没有意义的。 当然,对于编译器优化,它并没有真正受到伤害,并且为了清楚起见,有时可能最好将分布保持在其使用的位置附近。

如果我需要为多个不相关的东西生成随机值,我是否需要创建不同的生成器或者我可以保持相同吗?

如果您愿意,欢迎使用多个生成器来处理不相关的随机序列 - 这实际上是它们的主要抽奖卡之一。 如果在生成其他序列时不使用其生成器,则保留特定序列的伪随机算法的随机性保证(最明显的是,当从序列中提取数字时进行交织)。

这对于可重复性也很有用:例如,当您实际具有特定种子值(而不是从随机设备中拉出)时,将该种子用于一个特定序列会产生可重复的结果,而不管在同一个序列中使用的是否有任何其他序列时间。

另一个主要好处是,通过使用单独的生成器,您可以获得适用于其他对象的相同线程安全保证 换句话说,这意味着如果要同时生成多个伪随机序列,则可以在没有锁的情况下执行此操作,前提是每个线程在单独的生成器上运行。

正如您所正确提到的, std::random_device可能总是返回相同的序列。 这尤其发生在MinGW上,其中使用std::random_device任何程序的多次运行中行为是完全确定的。

std::mt19937std::uniform_int_distribution的行为在给定输入的情况下是确定性的。 因此,在MinGW上,所有四个片段的随机性同样不好 ,每个片段都会返回相同的序列(虽然每个片段的序列可能不同)。

如果您对此感到担心,请使用std::chrono::high_resolution_clock std::mt19937来初始化std::mt19937 ,或者代替std::mt19937 ,或者与std::random_device一起使用。

random_device用法

前两个循环是完全等效的,因为uniform_int_distribution类型是无状态的 (就像所有这样的分布一样)。 第二个可能会稍慢:我看到一个多余的堆栈存储与GCC或Clang在-O3

对于基于/dev/random类的常见random_device实现, 后两个循环也是等效的random_device只是一个句柄并访问相同的熵池而不管干扰和重新初始化。 但是,实现可以保留一个种子不需要的位,并将它们用于另一个种子。 这当然更难以测试,因为该州故意不可再生。 (majk是正确的 ,一个确定的实施random_device使最后的循环只生产单一的值。)

重复random_device使用

前两者通常是优选的 :从单个种子生成许多随机数是PRNG的 ,并且放弃可能在常见安装中快速耗尽熵池。 根据实现,您的进程可能会阻止 (广泛地)等待更多的熵,或者可能会依赖于操作系统提供的PRNG。 无论哪种情况,您都会剥夺其他进程的真熵。

在这里使用mt19937并不会增加太多: random_device已经可以直接uniform_int_distribution一起使用, random_device要缺点是(很少) 多次轮询random_device以获得20个值的均匀分布(因为这不是2的幂) )。

不同的溪流

一个生成器用于各种分布(交错或否)是完全合理的。 在某些情况下,您可能希望使用单独线程 ,通常使用多个线程或您想要控制种子 作为后者的一个例子,您可以根据具有特定种子的PRNG来定义某种程序内容生成。 如果在生成期间需要(或在将来的版本中)需要其他随机数,则对它们使用单​​独的生成器允许内容生成器对于任何这样的附加随机数使用具有相同的功能。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM