在C ++中使用random

问题描述 投票:4回答:3

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

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);
c++ random
3个回答
6
投票

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

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

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

因此,不建议使用示例#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是没有意义的。当然,对于编译器优化,它并没有真正受到伤害,并且为了清楚起见,有时可能最好将分布保持在其使用的位置附近。

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

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

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

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


1
投票

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

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

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


-1
投票

uniform_int_distribution usage

前两个循环是完全等价的,因为one superfluous stack store类型是无状态的(就像所有这样的分布一样)。第二个可能会稍微慢一点:我看到-O3与GCC或clang在random_device

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

重复使用mt19937

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

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

Different streams

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

© www.soinside.com 2019 - 2024. All rights reserved.