【问题标题】:how to ask boost::normal_distribution to generate a large vector of random variables without for loop如何要求 boost::normal_distribution 在没有 for 循环的情况下生成大量随机变量向量
【发布时间】:2014-06-04 07:13:11
【问题描述】:

我有一个大向量,我想给它添加正态分布的噪声。我现在正在做的 for 循环是微不足道的:

for (int i=0 ; i<size ; i++){    //size is of order 1000000
    boost::mt19937 gen;
    gen.seed(i);
    boost::normal_distribution<>  nd(0.0 , 1.0);
    boost::variate_generator<boost::mt19937&, boost::normal_distribution<> >
                                                         randNormal (gen,nd);

    noise = randNormal();  
    nsyData[i] = data[i] + sd*noise; 
    }

有没有一种有效的方法来做到这一点?像 MATLAB 做的那样?

【问题讨论】:

  • 复活节主题问题:matlab 会做什么? (说真的,您认为 Matlab 做了什么?)
  • 虽然这并不容易回答! MATLAB 不使用基于硬件加速方法的向量计算吗?像向量加法分布在几个核心上?
  • 我认为,如果您使用正确的标志进行编译,您的 C++ 代码可能会发生同样的情况。事实上,我在汇编输出 for GCC 以及 clang++ 中看到了 AVX 指令

标签: c++ boost noise normal-distribution


【解决方案1】:

这是我的看法:

#include <boost/random/normal_distribution.hpp>
#include <boost/random.hpp>

int main()
{
    boost::mt19937 gen(42); // seed it once
    boost::normal_distribution<double> nd(0.0, 1.0);
    boost::variate_generator<boost::mt19937&, boost::normal_distribution<double> > randNormal(gen, nd);

    std::vector<double> data(100000, 0.0), nsyData;
    nsyData.reserve(data.size());

    double sd = 415*randNormal();
    std::transform(data.begin(), data.end(), std::back_inserter(nsyData), 
            [sd,&randNormal](double data) { return data + sd*randNormal(); });

}

请注意,您在循环的每次迭代中都在播种 mersenne twister。恐怕这完全破坏了生成的随机数的任何质量保证。给你的发电机播种一次。 (显然,如果您需要它是非确定性的,请使用不同的种子,例如来自 random_device 的种子)。

看到这个Live On Coliru

更新在 cmets 中来回折腾后,这是一个 c++03 版本,实际上应该不会太糟糕,同时仍然可以理解:

#include <boost/random/normal_distribution.hpp>
#include <boost/random.hpp>
#include <boost/bind.hpp>

struct Xfrm { 
    typedef double result_type;

    template <typename Gen>
    double operator()(double sd, Gen& randNormal, double data) const {
        return data + sd*randNormal();
    }
};

int main()
{
    boost::mt19937 gen(42); // seed it once
    boost::normal_distribution<double> nd(0.0, 1.0);
    boost::variate_generator<boost::mt19937&, boost::normal_distribution<double> > randNormal(gen, nd);

    std::vector<double> data(100000, 0.0), nsyData;
    nsyData.reserve(data.size());

    double sd = 415*randNormal();

    std::transform(data.begin(), data.end(), std::back_inserter(nsyData), 
            boost::bind(Xfrm(), sd, boost::ref(randNormal), ::_1));
}

【讨论】:

  • 关于播种:PRNG 生成随机值。如果您必须以某种方式在每个插槽中选择下一个随机值,那就太奇怪了。另见here
  • std::transform 通过对每个元素应用转换,将输入范围转换为输出迭代器。在这种情况下,转换由 c++11 lambda [](double data) {...} 给出。输出迭代器是std::back_inserter,它只是将该值附加到nsyData的后面
  • @sali 当然你需要 randNormal() 在 lambda 中,否则你会在每个值上得到相同的偏差。我只需要为sd 补一个值,对吗?请记住您的代码没有显示它,并且我不会发布无法编译的代码。随意修改sd虽然:)
  • @sali 不用担心:here 老式的 C++03 解决方案,这里是使用 Boost Phoenix to emulate the lambda的更花哨的版本>
  • 毕竟,我想出的 another version 不会影响性能 /that/ 太多,但不需要带有 (eewww) 指针的可怕仿函数类型 (呕吐!),改用boost::bind。我可以忍受把这个放在答案中:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-11-01
  • 1970-01-01
  • 2017-06-12
  • 2017-10-25
  • 2012-10-29
  • 2019-02-21
  • 2020-02-29
相关资源
最近更新 更多