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

Posted

技术标签:

【中文标题】使用 C++11 随机库生成随机数【英文标题】:Generate random numbers using C++11 random library 【发布时间】:2013-11-09 01:21:20 【问题描述】:

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

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

我的代码的问题是每次编译和运行它时,它总是生成相同的数字。所以我的问题是随机库中的哪些其他函数可以在真正随机的同时实现这一点?

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

【问题讨论】:

这个问题非常危险地接近于“主要基于意见”。如果您可以摆脱征求意见,我可以看到这个问题非常有用(如果尚未提出)。 我建议使用std::mt19937 作为引擎,除非您有充分的理由不这样做。并且分布是两端的闭区间。 见***.com/q/7114043/420683和***.com/q/16536617/420683和***.com/q/11817493/420683和channel9.msdn.com/Events/GoingNative/2013/… @chris 两端没有关闭分布,检查这个link 或者这个link @memo1288,谢谢,我以为 OP 使用的是std::uniform_int_distribution,它两端都关闭了。 【参考方案1】:

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

你可以看到他的完整谈话here:

#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 来播种名为mt 的随机数生成器。 random_device()mt19937 慢,但它不需要播种,因为它从您的操作系统请求随机数据(这些数据将来自不同的位置,例如 RdRand)。


查看this question / answer,似乎uniform_real_distribution 返回了[a, b) 范围内的一个数字,您想要[a, b]。为此,我们的 uniform_real_distibution 实际上应该如下所示:

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

【讨论】:

由于问题是询问生成随机数的最通用方法,您可能只想使用 default_random_engine,根据 c++ 入门,它是实现认为最有用的方法 @aaronman:我将通过 STL 的演讲,他明确不喜欢 default_random_engine 的存在。 @chris 我们都知道向量和地图之间的区别,不是每个人都知道 mt19937 和 ranlux24 之间的区别,如果有人在不知道向量和字典是什么的情况下设法成为程序员,也许他们应该有一个std::default_container,希望没有人认为自己是不知道差异的程序员,很多脚本语言都有一个默认的映射类型结构,可以以用户可能不知道的各种方式实现知道 nextafter 调用对于大多数应用程序来说都是多余的。随机的double 恰好落在端点上的机会非常小,以至于包含和排除它之间没有实际区别。 @chris 无关(但你打开了门),你的 std::vector 类比在这里不起作用,因为std::vector 实际上是一个很好的默认值,因为 CPU 缓存。它甚至优于 std::list 在中间插入。即使您确实了解所有容器并且可以根据算法复杂性做出明智的决定,也是如此。【参考方案2】:

您有两种常见的情况。首先是您想要随机数,并且对质量或执行速度不太在意。在这种情况下,请使用以下宏

#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++,序列始终完全相同。

【讨论】:

除非不统一(0 到 N-1)。原因很简单,假设 N=100 和 RAND_MAX = 32758。没有办法将 32758 个元素(RAND_MAX)统一映射到 100 个输入。唯一的方法是在 32000 上设置一个界限,如果超出界限,则重新执行 rand() 如果 N 为 100,那么您的 RNG 必须非常好,才能检测到与平坦分布的偏差。【参考方案3】:

这是我刚刚写的内容::

#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, mtrd(), dist0.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;

~

【讨论】:

【参考方案4】:

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

例子:

    范围内的随机数

    auto val = Random::get(-10, 10); // Integer
    auto val = Random::get(10.f, -10.f); // Float point
    

    随机布尔值

    auto val = Random::get<bool>( ) // 50% to generate true
    auto val = Random::get<bool>( 0.7 ) // 70% to generate true
    

    来自 std::initilizer_list 的随机值

    auto val = Random::get(  1, 3, 5, 7, 9  ); // val = 1 or 3 or...
    

    来自迭代器范围或所有容器的随机迭代器

    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

【讨论】:

【参考方案5】:

这里有一些关于伪随机数生成器的资源。

https://en.wikipedia.org/wiki/Pseudorandom_number_generator

基本上,计算机中的随机数需要一个种子(这个数字可以是当前系统时间)。

替换

std::default_random_engine generator;

std::default_random_engine generator(<some seed number>);

【讨论】:

【参考方案6】:

我把上面所有的东西都涂红了,还有大约 40 个其他带有 c++ 的页面,比如 this 并观看了 video from Stephan T. Lavavej "STL" 并且仍然不确定随机数在实践中是如何工作的,所以我花了整整一个星期天来弄清楚它的全部内容以及它是如何工作和可以使用的。

在我看来,STL 关于“不再使用 srand”是正确的,他在视频 2 中解释得很好。 他还建议使用:

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

b) mt19937 的示例 -- 更快,能够创建种子,未加密


我拿出了我可以访问的所有声称的 c++11 书籍,并找到了 f.e.像 Breymann (2015) 这样的德国作家仍然使用

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

只用&lt;random&gt; 而不是&lt;time&gt; and &lt;cstdlib&gt; #includes - 所以要小心只从一本书中学习:)。

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

程序通常需要随机数源。在新的之前 标准,C 和 C++ 都依赖于一个简单的 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 (more bout that here) 生成引擎和分发的好主意。


为了能够实际使用 &lt;random&gt; 中的标准库提供的随机数生成器这里有一些带有不同示例的可执行代码,已减少到希望安全时间的最少必要性和钱给你们:

    #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 或 in评论部分,如有必要,将添加或编辑此帖子。布尔

【讨论】:

以上是关于使用 C++11 随机库生成随机数的主要内容,如果未能解决你的问题,请参考以下文章

Python random库

C语言中随机函数每次调用的随机序列是不是一样?

c语言产生4位随机数

为啥不直接使用 random_device?

随手记3:C#Unity中随机数的使用

C++11使用mt19937和uniform_int_distributio 生成随机数