C ++使用default_random_engine在循环中生成随机数

时间:2015-10-14 17:23:41

标签: c++ random

最近,我正在尝试使用random engines中定义的#include<random>在C ++中生成随机数的程序。我的计划如下: -

#include <iostream>
#include <random>
#include <chrono>
using namespace std;
int random (int lim)
{
    default_random_engine dre (chrono::steady_clock::now().time_since_epoch().count());
    uniform_int_distribution<> uid(1,lim);
    return uid(dre);
}
int main()
{
    for (int i=0;i<10;++i)
    cout<<random(100)<<" ";
    return 0;
}

简单,绝对!但是当我尝试输出时,这些数字不那么随机: -

66 95 95 96 96 96 96 96 97 97

当我略微改变我的程序时将default_random_engine声明为static或将其设为global然后我的输出正确如下: -

62 53 21 38 7 51 46 40 86 12

有人能指出我的程序最初和实际出现的问题吗?小变化是如何帮助我获得更好的输出的呢?

2 个答案:

答案 0 :(得分:6)

每次运行random时,您都会使用相同或非常接近相同种子的方式重新生成生成器,以便每次迭代都获得相同的输出,或者如果它确实发生更改,则不会太多。

要解决此问题,您只需为生成器播种一次,然后继续调用它。通过将其设置为静态,它可以正常工作,因为生成器仅在您继续其随机序列时创建并播种,而不是获取它将创建的第一个随机数。

通常,随机数生成器具有通过算法放置的内部值,算法吐出的内容是它们为随机数返回的内容。然后保留该数字用于算法的下一次迭代。这就是我们获得随机序列的方式。

如果我们使用相同的种子,我们将获得与从相同数字开始的相同输出序列。在您的情况下,您的种子只会在每次chrono::steady_clock::now()前进时更改,并且您的循环运行速度要快于每次调用的时间(种子)。

答案 1 :(得分:1)

关于伪随机数生成器的一个重要事项是它们产生一系列数字,而“随机性”是测试序列的质量。也就是说,您不能说特定数字是随机的(is '4' a random number?)。而是说出数字序列是否是随机的。

当您“播种”pRNG时,您通常会选择它可以生成的序列之一,并且这些数字相对于序列的其余部分是随机的。因此,一般情况下,如果您想要“随机性”,您首先要选择一个特定的序列,然后使用该特定随机序列中的连续数字。

在没有static的代码中,您在循环的每次迭代中选择一个序列,然后使用该序列中的一个数字。由于您没有使用来自相同序列的许多值,因此结果看起来并不是随机的并不奇怪。

当您添加static时,引擎不再在循环的每次迭代中播种。相反,它被播种一次,循环的每次迭代都使用来自该序列的连续值,就像你应该做的那样。这就是static对块作用域变量的意义:它意味着变量存在于它声明的块的外部,并且它只会在第一次初始化。

static可以解决您的问题,但我认为这不是一个好的解决方案。 static变量与全局变量有一些相似之处,并导致一些相同的问题。相反,我建议你明确地将引擎移出循环。例如:

int random (int lim, default_random_engine &dre)
{
    uniform_int_distribution<> uid(1,lim);
    return uid(dre);
}

int main()
{
    default_random_engine dre (chrono::steady_clock::now().time_since_epoch().count());

    for (int i = 0; i < 10; ++i)
    {
        cout << random(100, dre) << " ";
    }
}

现在正在显式管理状态并将其传递到random()函数,这比依赖于块作用域static变量的隐藏和隐式行为更好。

一般而言,我不建议将时间用作种子数据的唯一来源。低分辨率时钟可能不会足够频繁地提供新种子,并且同时在不同位置运行的程序可以使用相同的种子。

我对播种的建议是:

std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 eng(seed);

(初学者无需知道mt19937或其他任何内容是什么。他们只需知道将其粘贴到适当的位置以及如何使用eng。在您的代码中,您需要需要将default_random_engine的每次使用替换为mt19937。)