使用C ++ 11随机库生成随机数


135

如标题所示,我正在尝试找到一种使用新的C ++ 11 <random>库生成随机数的方法。我已经用以下代码尝试过:

std::default_random_engine generator;
std::uniform_real_distribution<double> uniform_distance(1, 10.001);

我的代码存在的问题是,每次我编译并运行它时,它总是生成相同的数字。所以我的问题是,在真正随机的情况下,随机库中还有哪些其他函数可以完成此任务?

对于我的特定用例,我试图获得一个范围内的值 [1, 10]


3
这个问题在“主要基于意见”上是危险的。如果您可以避免征求意见,那么我可以看到这个问题非常有用(如果尚未提出问题)。
John Dibling 2013年

4
我建议使用a std::mt19937作为引擎,除非您有充分的理由不这样做。并且分布是两端的封闭间隔。
克里斯,


2
@chris发行未在两端都关闭,请检查此链接或此链接
memo1288

1
@ memo1288,谢谢,我以为OP正在使用std::uniform_int_distribution,两端封闭。
克里斯

Answers:


191

微软公司的Stephan T. Lavavej(stl)在Going Native上发表了关于如何使用新的C ++ 11随机函数以及为什么不使用的演讲rand()。在其中,他包括一张幻灯片,该幻灯片基本上可以解决您的问题。我已经从下面的幻灯片中复制了代码。

您可以在此处查看他的完整演讲:http : //channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

#include <random>
#include <iostream>

int main() {
    std::random_device rd;
    std::mt19937 mt(rd());
    std::uniform_real_distribution<double> dist(1.0, 10.0);

    for (int i=0; i<16; ++i)
        std::cout << dist(mt) << "\n";
}

我们使用random_device一次来播种名为的随机数生成器mtrandom_device()的速度比慢mt19937,但不需要播种,因为它会从您的操作系统中请求随机数据(该数据将来自各个位置,例如RdRand)。


看着这个提问/回答,看来uniform_real_distribution退货范围内的号码[a, b),你想要[a, b]。为此,我们uniform_real_distibution实际上应如下所示:

std::uniform_real_distribution<double> dist(1, std::nextafter(10, DBL_MAX));

3
default_random_engine根据c ++引物,由于问题是在询问您可能想使用的最通用的随机数生成方法,因此该实现被认为是最有用的方法
aaronman 2013年

2
@aaronman:我要讲STL的谈话,他明确表示不喜欢这种说法default_random_engine
比尔·林奇

5
@chris我们都知道向量和地图之间的区别,不是每个人都知道mt19937和ranlux24之间的区别,如果某人在不知道向量和字典是什么的情况下成功地成为程序员,也许他们应该有一个std::default_container,希望没有人们认为自己是程序员,他们不知道这些差异,因此许多脚本语言都有默认的映射类型结构,可以用用户可能不知道的各种方式来实现
aaronman

21
nextafter对于大多数应用程序而言,此调用过于严格。随机double降落在端点上的机会非常小,以至于在包含和排除端点之间没有实际区别。
Mark Ransom 2014年

3
@chris不相关(但您打开了门),您的std::vector类比在这里不起作用,因为实际上由于CPU缓存,std::vector 是一个很好的默认值。它甚至胜过std::list插入中间的操作。即使您确实了解所有容器并可以基于算法复杂性做出明智的决定,这也是事实。
void.pointer 2014年

24

我的“随机”库为C ++ 11随机类提供了一个非常方便的包装器。您可以使用简单的“获取”方法来完成几乎所有事情。

例子:

  1. 范围内的随机数

    auto val = Random::get(-10, 10); // Integer
    auto val = Random::get(10.f, -10.f); // Float point
    
  2. 随机布尔

    auto val = Random::get<bool>( ) // 50% to generate true
    auto val = Random::get<bool>( 0.7 ) // 70% to generate true
    
  3. 来自std :: initilizer_list的随机值

    auto val = Random::get( { 1, 3, 5, 7, 9 } ); // val = 1 or 3 or...
  4. 来自迭代器范围或所有容器的随机迭代器

    auto it = Random::get( vec.begin(), vec.end() ); // it = random iterator
    auto it = Random::get( vec ); // return random iterator
    

还有更多的东西!查看github页面:

https://github.com/effolkronium/random


4

我红上述所有的东西,关于它40个其它页面与类似于C ++ 这样并观看了由斯蒂芬T. Lavavej“STL”的视频 仍然不知道随机数是如何工作的实践,所以我花了整整周日弄清楚它的全部内容以及它的工作方式和使用方式。

我认为STL关于“不再使用srand”是正确的,他在视频2中对此做了很好的解释。他还建议使用:

a)void random_device_uniform()-用于加密生成,但速度较慢(以我的示例为例)

b)具有mt19937-更快,能够创建种子的能力的示例,而不是加密的


我拿出了所有我拥有的声称拥有c ++ 11本书的书,发现例如Breymann(2015)之类的德国作家仍然使用

srand( time( 0 ) );
srand( static_cast<unsigned int>(time(nullptr))); or
srand( static_cast<unsigned int>(time(NULL))); or

只是<random>代替<time> and <cstdlib>#includings -所以要小心刚刚从一本书学习:)。

含义-自c ++ 11起不应该使用,因为:

程序通常需要一个随机数源。在新标准发布之前,C和C ++都依赖于名为rand的简单C库函数。该函数产生的伪随机整数在0到至少32767的系统相关最大值的范围内均匀分布。rand函数有几个问题:许多(如果不是最多的话)程序需要与兰德生产的一种。某些应用程序需要随机浮点数。某些程序需要反映不均匀分布的数字。程序员在尝试转换rand生成的数字的范围,类型或分布时,经常会引入非随机性。(引自Lippmans C ++入门2012年第五版)


我终于在Bjarne Stroustrups的新书中找到20本书中最好的解释-他应该知道他的东西-在“ C ++之旅2019”,“使用C ++ 2016编程原理和实践”和“ C ++编程语言第4版”中2014”和“ Lippmans C ++入门第五版2012”中的一些示例:

这真的很简单,因为随机数生成器由两部分组成: (1)生成随机或伪随机值序列的引擎。(2)将这些值映射为范围内的数学分布的分布。


尽管有微软STL专家的意见,Bjarne Stroustrups写道:

在中,标准库提供了随机数引擎和分布(第24.7节)。默认情况下,使用default_random_engine,选择它是为了实现广泛的应用和低成本。

void die_roll()示例来自Bjarne Stroustrups-一个很好的主意,可以使用生成引擎并进行分发using (更多信息请参见此处)


为了能够实际使用标准库在 此处提供的随机数生成器,<random> 此处的一些可执行代码带有不同的示例,这些示例已减少到最希望为您节省时间和金钱的必要条件:

    #include <random>     //random engine, random distribution
    #include <iostream>   //cout
    #include <functional> //to use bind

    using namespace std;


    void space() //for visibility reasons if you execute the stuff
    {
       cout << "\n" << endl;
       for (int i = 0; i < 20; ++i)
       cout << "###";
       cout << "\n" << endl;
    }

    void uniform_default()
    {
    // uniformly distributed from 0 to 6 inclusive
        uniform_int_distribution<size_t> u (0, 6);
        default_random_engine e;  // generates unsigned random integers

    for (size_t i = 0; i < 10; ++i)
        // u uses e as a source of numbers
        // each call returns a uniformly distributed value in the specified range
        cout << u(e) << " ";
    }

    void random_device_uniform()
    {
         space();
         cout << "random device & uniform_int_distribution" << endl;

         random_device engn;
         uniform_int_distribution<size_t> dist(1, 6);

         for (int i=0; i<10; ++i)
         cout << dist(engn) << ' ';
    }

    void die_roll()
    {
        space();
        cout << "default_random_engine and Uniform_int_distribution" << endl;

    using my_engine = default_random_engine;
    using my_distribution = uniform_int_distribution<size_t>;

        my_engine rd {};
        my_distribution one_to_six {1, 6};

        auto die = bind(one_to_six,rd); // the default engine    for (int i = 0; i<10; ++i)

        for (int i = 0; i <10; ++i)
        cout << die() << ' ';

    }


    void uniform_default_int()
    {
       space();
       cout << "uniform default int" << endl;

       default_random_engine engn;
       uniform_int_distribution<size_t> dist(1, 6);

        for (int i = 0; i<10; ++i)
        cout << dist(engn) << ' ';
    }

    void mersenne_twister_engine_seed()
    {
        space();
        cout << "mersenne twister engine with seed 1234" << endl;

        //mt19937 dist (1234);  //for 32 bit systems
        mt19937_64 dist (1234); //for 64 bit systems

        for (int i = 0; i<10; ++i)
        cout << dist() << ' ';
    }


    void random_seed_mt19937_2()
    {
        space();
        cout << "mersenne twister split up in two with seed 1234" << endl;

        mt19937 dist(1234);
        mt19937 engn(dist);

        for (int i = 0; i < 10; ++i)
        cout << dist() << ' ';

        cout << endl;

        for (int j = 0; j < 10; ++j)
        cout << engn() << ' ';
    }



    int main()
    {
            uniform_default(); 
            random_device_uniform();
            die_roll();
            random_device_uniform();
            mersenne_twister_engine_seed();
            random_seed_mt19937_2();
        return 0;
    }

我认为所有这些都加起来了,就像我说的,花了我大量的阅读和时间将其提炼为这些示例-如果您对数字生成有更多的了解,我很乐意通过pm或在评论部分中听到并将在必要时添加它或编辑此信息。布尔


0

这是我刚才写的一些东西:

#include <random>
#include <chrono>
#include <thread>

using namespace std;

//==============================================================
// RANDOM BACKOFF TIME
//==============================================================
class backoff_time_t {
  public:
    random_device                      rd;
    mt19937                            mt;
    uniform_real_distribution<double>  dist;

    backoff_time_t() : rd{}, mt{rd()}, dist{0.5, 1.5} {}

    double rand() {
      return dist(mt);
    }
};

thread_local backoff_time_t backoff_time;


int main(int argc, char** argv) {
   double x1 = backoff_time.rand();
   double x2 = backoff_time.rand();
   double x3 = backoff_time.rand();
   double x4 = backoff_time.rand();
   return 0;
}



-3

您有两种常见情况。第一个是您想要随机数,并且对质量或执行速度不太感兴趣。在这种情况下,请使用以下宏

#define uniform() (rand()/(RAND_MAX + 1.0))

这将使您的p在0到1的范围内-epsilon(除非RAND_MAX大于双精度的精度,但是在使用它时请担心这一点)。

int x =(int)(uniform()* N);

现在给出0到N -1之间的随机整数。

如果需要其他分布,则必须转换p。有时,多次调用Uniform()更容易。

如果您想要可重复的行为,请使用常量作为种子,否则请使用time()进行调用。

现在,如果您对质量或运行时性能感到不安,请重写Uniform()。但是,否则请不要触摸代码。始终将uniform()保持在0到1减epsilon上。现在,您可以包装C ++随机数库以创建更好的uniform(),但这是一种中等级别的选择。如果您对RNG的特性感到困扰,那么也值得花费一些时间来了解底层方法的工作原理,然后提供一个。因此,您可以完全控制代码,并且可以保证,使用相同的种子,序列将始终完全相同,无论您要链接的平台或C ++版本是什么。


3
除非不一致(0到N-1)。原因很简单,假设N = 100并且RAND_MAX =32758。没有一种方法可以将32758个元素(RAND_MAX)统一映射到100个输入。唯一的方法是在32000上设置界限,如果超出界限则重新执行rand()
amchacon

1
如果N为100,则您的RNG必须非常好,才能检测到平面分布的偏差。
马尔科姆·麦克莱恩
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.