C ++随机数生成:生成cos平方函数

时间:2017-02-26 13:01:40

标签: c++ c++11 random probability probability-density

感兴趣的概率分布是

double x; // range: -pi/2.0 to +pi/2.0
double y = std::pow(std::cos(x), 2.0);

此功能可以通过分析进行集成,但不能反转。因此,无法执行将均匀分布映射到所需概率分布的常用技巧。

是否有另一种方法可用于生成随机变量cos ^ 2(θ)分布?

有可能以数字方式找到反函数,但我不知道这样做的有效(记忆和计算)方法。

1 个答案:

答案 0 :(得分:2)

Inverse transform sampling:你可以从任何概率分布中随机生成样本数,给定它的cdf。

假设你想要cos 2 x分布,从-pi / 2到pi / 2。由于cos 2 x从-pi / 2到pi / 2的积分是pi / 2,你需要按比例缩小以使积分为1.因此,pdf P(x)=(2 / PI)COS 2 X

下一步是从给定的pdf计算cdf,这是pdf的积分。您可以使用任何数值方法来查找P(x)的积分。或者你可以去Wolfram Alpha得到答案:cdf是F(x)=(2 / pi)(0.5x + 0.25sin2x)+ 0.5

接下来,你需要计算F -1 (x)。由于F(x)是单调递增函数,因此可以使用二分法(二分搜索)轻松找到F -1 (x)。 Wolfram Alpha虽然没有这个F -1 (x)公式。

然后从0到1生成一个统一的实数u。您的自定义分布为F -1 (u)。

#include <iostream>
#include <cmath>
#include <random>
#include <boost/random/random_device.hpp>
#include <vector>
#include <iomanip>

const double pi = 3.14159265358979323846;

const double LOW = -pi/2;
const double HIGH = pi/2;
double pdf(double x)
{
    return cos(x) * cos(x);
}

double cdf(double x) //integral of pdf
{
    return (2/pi)*(x/2 + sin(2*x)/4) + 0.5; //from Wolfram Alpha
}

double inverse_cdf(double u)
{   //bisection, not 100% accurate
    double low  = LOW;
    double high = HIGH;
    double epsilon = 1e-10; //any small number, e.g. 1e-15
    while (high - low > epsilon)
    {
        double mid = (low + high) / 2;
        if (cdf(mid) == u) return mid;
        if (cdf(mid) < u) low = mid; else high = mid;
    }
    return (low + high) / 2;
}

double custom_distribution(std::mt19937& rng)
{
    double u = std::uniform_real_distribution<double>(0,1)(rng);
    return inverse_cdf(u);
}

int main()
{
    std::mt19937 rng{boost::random::random_device{}()};

    std::vector<double> xCount(15);
    int nSamples = 10000;
    double gap = (HIGH-LOW) / xCount.size();
    while (nSamples--) xCount[(int)( (custom_distribution(rng) - LOW) / gap )]++;
    for (int i = 0; i < xCount.size(); ++i)
    {
        std::cout << std::setw(2) << i << ":" << xCount[i] << "\t";
        for (int bar = xCount[i]/15; bar--; std::cout << '*');
        std::cout << "\n";
    }
}

示例输出:

 0:17   *
 1:135  *********
 2:305  ********************
 3:604  ****************************************
 4:859  *********************************************************
 5:1106 *************************************************************************
 6:1256 ***********************************************************************************
 7:1353 ******************************************************************************************
 8:1271 ************************************************************************************
 9:1102 *************************************************************************
10:876  **********************************************************
11:614  ****************************************
12:334  **********************
13:143  *********
14:25   *