161

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

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

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

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

4

6 回答 6

238

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

你可以在这里看到他的完整演讲:

#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));
于 2013-10-29T18:48:05.840 回答
33

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

例子:

  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

于 2017-08-05T18:42:34.740 回答
9

我把上面所有的东西都涂红了,还有大约 40 个其他页面,里面有这样的 c++,并观看了Stephan 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>#includes - 所以要小心只从一本书中学习:)。

含义 - 自 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 或评论部分听到并将在必要时添加或编辑此帖子。布尔

于 2019-11-24T16:30:13.673 回答
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;
}

~

于 2016-12-21T02:44:02.063 回答
0

这是一些您可以阅读的有关伪随机数生成器的资源。

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

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

代替

std::default_random_engine generator;

经过

std::default_random_engine generator(<some seed number>);
于 2019-05-12T11:47:47.220 回答
-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++ 版本。

于 2016-10-10T12:37:16.103 回答