【问题标题】:C++ fast normal random number generatorC++ 快速正态随机数生成器
【发布时间】:2016-02-21 15:29:21
【问题描述】:

我正在使用mt19937 生成器生成正常的随机数,如下所示:

normal_distribution<double> normalDistr(0, 1);    
mt19937 generator(123);
vector<double> randNums(1000000);
for (size_t i = 0; i != 1000000; ++i)
{
    randNums[i] = normalDistr(generator);
}

上面的代码可以工作,但是由于我在我的代码中生成了超过 1 亿个正常随机数,所以上面的代码非常慢。

有没有更快的方法来生成正常的随机数?

以下是有关如何使用代码的一些背景知识:

  • 随机数的质量并不那么重要
  • 数字的精度不是那么重要,doublefloat 都可以
  • 正态分布的均值 = 0 和 sigma = 1

编辑

@Dúthomhas,安德鲁:

分析后,以下函数占用了超过 50% 的时间:

std::normal_distribution<double>::_Eval<std::mersenne_twister_engine<unsigned int,32,624,397,31,2567483615,11,4294967295,7,2636928640,15,4022730752,18,1812433‌​253> >

【问题讨论】:

  • 我不相信这是 RNG。你分析过你的代码吗?
  • @user2079303 我错了。应该说:您可以使用vector&lt;double&gt; randNums; randNums.reserve(1000000);,而不是使用vector&lt;double&gt; randNums(1000000);。这样可以节省将所有元素设置为 0 的时间。
  • 也许您可以考虑不生成 1 亿个随机数。为什么需要这么多?
  • 您在问一个关于优化的问题。请在您的代码中包含分析的结果。瓶颈很可能不在您认为的位置。
  • @everyone 认为高质量的 RNG 并将它们映射到正态分布很便宜:根据我在 gdb 中单步执行时的(昏昏欲睡的)计数,每次大约需要 485 条指令数字生成循环的迭代when compiled with gcc 5.2 -O3 -ffast-math。 (我想我数不清一次或两次)。其中许多是 FP mul 和 add,它们具有 3 和 5 个周期的延迟(在 Intel SnB 上)。所以生成一个随机数并将其映射到正态分布是不便宜。 (顺便说一句,我认为 很多 成本来自正态分布,而不是 MT PRNG)。

标签: c++ random normal-distribution


【解决方案1】:

最重要的是,你真的需要同时 100,000,000 个随机数吗?所有这些数据的写入和随后从 RAM 中读取不可避免地需要大量时间。如果您一次只需要一个随机数,则应该避免这种情况。

假设您确实需要 RAM 中的所有这些数字,那么您应该首先 如果您真的想知道 CPU 时间在哪里花费/丢失,请分析您的代码。

其次,您应该避免不必要的数据重新分配和初始化。将std::vector::reserve(final_size)std::vector::push_back() 结合使用最容易做到这一点。

第三,您可以使用比std::mt19937 更快的RNG。当数字的质量很重要时,建议使用 RNG。 online documentation 表示lagged Fibonacci generator(在std:: subtract_with_carry_engine 中实现)速度很快,但它可能没有足够长的重复周期——您必须检查这一点。或者,您可能想使用std::min_stdrand(它使用linear congruential generator

std::vector<double> make_normal_random(std::size_t number,
                                       std::uint_fast32_t seed)
{
  std::normal_distribution<double> normalDistr(0,1);    
  std::min_stdrand generator(seed);
  std::vector<double> randNums;
  randNums.reserve(number);
  while(number--)
    randNums.push_back(normalDistr(generator));
  return randNums;
}

【讨论】:

    【解决方案2】:

    您还需要查看 std::vector 保留而不是调整大小。它将允许您在 1 次拍摄中获得所需的所有内存。我假设您不需要一次全部 1 亿个双打?

    【讨论】:

      【解决方案3】:

      如果确实是生成器导致性能下降,则使用普通的rand 函数(需要成对绘制数字),在 0、1 中转换为浮点数或双精度数,然后应用 Box Muller转型。

      这在时间上很难被击败,但请注意,统计属性并不比 rand 好。

      一个数字配方例程 gasdev 可以做到这一点 - 你应该可以下载一个副本。

      【讨论】:

      • std::normal_distribution 已经做到了(Box-Muller 变换)。不建议在 C++ 中使用 rand()
      • 数字食谱不是免费软件,但需要许可证。
      猜你喜欢
      • 1970-01-01
      • 2014-07-26
      • 1970-01-01
      • 2012-03-13
      • 1970-01-01
      • 1970-01-01
      • 2010-11-05
      • 2013-03-18
      • 2012-03-07
      相关资源
      最近更新 更多