【问题标题】:How do I generate a random number using the C++11 standard library如何使用 C++11 标准库生成随机数
【发布时间】:2011-11-05 06:39:02
【问题描述】:

新的 C++11 标准有一整章专门介绍随机数生成器。但是我如何执行过去像这样编码的最简单、最常见的任务,但不求助于标准 C 库:

srand((unsigned int)time(0));
int i = rand();

随机数引擎、分布和种子是否有合理的默认值,可以开箱即用?

【问题讨论】:

  • 维基百科? en.wikipedia.org/wiki/…
  • 你的代码有什么问题? AFAIK,添加了新的随机数生成器,用于更“严肃”的应用程序,其中随机数生成的各个方面非常重要。
  • @GMan:公平地说,新标准中的一些随机数引擎可以说是简单快速,我不会认为它们特别“严重”。
  • 每次在 C++ 中使用标准 C 库时,我都觉得自己在做一些不合时宜的事情。看看演员阵容!必须有更好的方法。
  • @Charles Bailey 我也不确定!没错,只有 rand 是标准 C 的一部分,random 是 POSIX 的一部分,arc4random 在 BSD 中提供。

标签: c++ random c++11


【解决方案1】:

您应该能够执行以下操作:

std::default_random_engine e((unsigned int)time(0));
int i = e();

default_random_engine 的质量取决于实现。您也可以使用std::min_rand0std::min_rand

可能一种更好的随机引擎播种方法是使用实​​现中可用的真实随机数,而不是使用time

例如

std::random_device rd;
std::default_random_engine e( rd() );

【讨论】:

  • 澄清一下,你的意思是“应该”,如“这不存在,但它应该存在”还是 sd “存在,你的编译器应该支持它”?
  • 信息性:此答案与 OP 示例之间的最大区别在于编码器控制引擎状态的位置:它在 e。在 OP 的代码中,状态作为静态数据隐藏在 rand 中。这是处理多线程程序时的一个重要区别。 rand 必须有某种保护以使其线程安全。 default_random_engine 没有。如果你想从多个线程调用它,你自己提供同步机制,外部。这意味着如果您不需要同步 default_random_engine 可以更快。
  • @Charles:我刚刚在 libc++ 上测试了你的代码。给定适当的包含,它可以工作。在time 上可能需要std::。符合托管 C++11 平台要求 std::default_random_engine。在 libc++ 上,它等同于 minstd_rand,即 linear_congruential_engine
  • @Howard:播种怎么样?标准中是否默认使用一些依赖于实现的魔法来播种随机数生成器?还是 time(0) 仍然是唯一实用且可移植的方式?我没有将随机数用于加密之类的奇特事物——只是为了随机化我的测试。
  • @Bartosz:如果您不需要或不希望随机序列的可重复性,我更喜欢 Charles 的 random_device 建议。或者您可以继续使用time。或者有一个叫做seed_seq 的东西,它是一种增强型类固醇的播种机。我已经实现了这个东西,但仍然不太确定何时使用它。 :-) 为了更有趣,seed_seq 本身可以播种!
【解决方案2】:

统一和简化已经提供的一些示例我将总结为:

// Good random seed, good engine
auto rnd1 = std::mt19937(std::random_device{}());

// Good random seed, default engine
auto rnd2 = std::default_random_engine(std::random_device{}());

// like rnd1, but force distribution to int32_t range
auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}()));

// like rnd3, but force distribution across negative numbers as well
auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}()));

然后我进行了一些测试以查看默认设置:

#include <random>
#include <functional>
#include <limits>
#include <iostream>

template<class Func>
void print_min_mean_max(Func f) {
   typedef decltype(f()) ret_t;
   ret_t min = std::numeric_limits<ret_t>::max(), max = std::numeric_limits<ret_t>::min();
   uint64_t total = 0, count = 10000000;
   for (uint64_t i = 0; i < count; ++i) {
      auto res = f();
      min = std::min(min,res);
      max = std::max(max,res);
      total += res;
   }
   std::cout << "min: " << min << " mean: " << (total/count) << " max: " << max << std::endl;
}

int main() {
   auto rnd1 = std::mt19937(std::random_device{}());
   auto rnd2 = std::default_random_engine(std::random_device{}());

   auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}()));
   auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}()));

   print_min_mean_max(rnd1);
   print_min_mean_max(rnd2);
   print_min_mean_max(rnd3);
   print_min_mean_max(rnd4);
}

产生输出:

min: 234 mean: 2147328297 max: 4294966759
min: 349 mean: 1073305503 max: 2147483423
min: 601 mean: 1073779123 max: 2147483022
min: -2147481965 mean: 178496 max: 2147482978

所以我们可以看到,mt19937 和 default_random_engine 有不同的默认范围,所以建议使用 uniform_int_distribution。

此外,默认的 uniform_int_distribution 为 [0, max_int](非负数),即使使用有符号整数类型也是如此。如果您想要全范围,则必须明确提供范围。

最后,its important to remember this 有时像这样。

【讨论】:

  • 值得注意的是,std::mt19937:std::mt19937_64 有一个 64 位版本,每次调用返回 64 位随机性。 auto rnd5 = std::mt19937_64(std::random_device{}()); // min: 4879020137534 mean: 1655417118684 max: 18446741225191893648
  • 顺便说一句,在许多发行版和 std::bind() 中重复使用相同的随机数引擎是否安全,或者将每个发行版绑定到新引擎实例是否更好?这个怎么样:std::mt19937_64 random = std::mt19937_64(std::random_device{}());auto randomCardinality = std::bind(std::uniform_int_distribution&lt;int&gt;(1, 4), random);auto randomValue = std::bind(std::uniform_real_distribution&lt;double&gt;(-1.0, 1.0), random);
【解决方案3】:

如果您现有的代码在新标准之前适用,那么它将继续适用。为需要更高质量的伪随机性的应用程序添加了新的随机数生成器,例如随机模拟。

【讨论】:

    【解决方案4】:

    我在我的项目中使用以下代码。 'engine' 和 'distribution' 可以是库提供的其中之一。

    #include <random>
    #include <functional>
    #include <iostream>
    ...
    std::uniform_int_distribution<unsigned int> unif;
    std::random_device rd;
    std::mt19937 engine(rd());
    std::function<unsigned int()> rnd = std::bind(unif, engine);
    
    std::cout << rnd() << '\n';
    

    【讨论】:

      【解决方案5】:

      给你。范围内的随机双打:

      // For ints
      // replace _real_ with _int_, 
      // <double> with <int> and use integer constants
      
      #include <random>
      #include <iostream>
      #include <ctime>
      #include <algorithm>
      #include <iterator>
      
      int main()
      {
          std::default_random_engine rng(std::random_device{}()); 
          std::uniform_real_distribution<double> dist(-100, 100);  //(min, max)
      
          //get one
          const double random_num = dist(rng);
      
          //or..
          //print 10 of them, for fun.
          std::generate_n( 
              std::ostream_iterator<double>(std::cout, "\n"), 
              10, 
              [&]{ return dist(rng);} ); 
          return 0;
      }
      

      【讨论】:

        【解决方案6】:

        随机数生成是一个难题。没有真正随机的方法来做到这一点。如果您只是生成随机性来播种游戏环境,那么您的方法应该没问题。 rand() 有几个缺点。

        如果您需要随机性来生成加密密钥,那么您就是 S.O.L.在这种情况下,最好的方法是使用通常具有机制的操作系统。在 POSIX 上是 random() (如果您愿意,可以从 /dev/random 读取)。在 Windows 上,您可以使用 CryptoAPI:

        https://www.securecoding.cert.org/confluence/display/seccode/MSC30-C.+Do+not+use+the+rand%28%29+function+for+generating+pseudorandom+numbers

        【讨论】:

          【解决方案7】:

          您可以使用 RC4 生成随机字节。这可能具有您想要的属性。它实现起来快速且相当简单。当种子已知时,该序列在所有实现中是可重复的,而当种子未知时,则完全不可预测。 http://en.wikipedia.org/wiki/RC4

          【讨论】:

            猜你喜欢
            • 2013-11-09
            • 2016-06-25
            • 1970-01-01
            • 2017-02-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-12-04
            相关资源
            最近更新 更多