实现正态分布PRNG时Box Muller变换的问题

时间:2016-12-15 23:49:03

标签: c++ random gaussian

使用线性全等生成器我能够创建两个均匀分布的独立伪随机数序列。我试图改变我的程序,允许它使用这些序列并执行Box-Muller变换将它们更改为正态分布集。

然而,我遇到的问题是,无论两个统一序列的输入种子值如何,我的新“正态分布随机数”(Z)总是等于零。

任何提示都将不胜感激。 非常感谢

#define _USE_MATH_DEFINES
#include <iostream>
#include <cmath>
#include <math.h>

using namespace std;

#define M 4294967295

unsigned long get_rand(unsigned long x)                                                                 //establishing function to generate random numbers
{
    unsigned long a = 2269477;
    unsigned long b = 1;                                                                                //Values taken from wikipedia for Linear Congruence Method
    unsigned long m = M;
    unsigned long y;
    y = (a * x + b) % m;
    return y;
}

unsigned long get_normal(unsigned long x1, unsigned long x2)
{
    unsigned long R;
    unsigned long phi;
    unsigned long u;
    R = sqrt(-2 * log(x1));                                                                             //Box-Muller Transform
    phi = (2 * M_PI*x2);
    u = R*cos(phi);
    return u;
}
double u1, u2, Z;
double bin0 = 0;
double bin1 = 0;
double bin2 = 0;                                                                                        //Variables used to store frequency of each number range
double bin3 = 0;
double bin4 = 0;
double bin5 = 0;
double bin6 = 0;
double bin7 = 0;
double bin8 = 0;
double bin9 = 0;

int  main() {
    double seed1,seed2;
    cout << "Please enter seed values " << endl;        
    cin >> seed1;
    cout << "\n";
    cin >> seed2;
    double x;
    cout << "Please enter how many random numbers you want " << endl;       
    cin >> x;
    cout << endl;
    cout << "Random Numbers generated shown below: " << endl;


    for (int i = 0; i < x; i++)                                                                         //generate as many random numbers as the user has asked for 
    {
        seed1 = get_rand(seed1);
        seed2 = get_rand(seed2);

        u1 = (double(seed1) / M);                                                                       //changing to double and dividing by 'M' gets all values between 0 and 1
        cout <<"U1 = " << u1 << endl;                                                                   //type conversion to prevent integer rounding in division
        u2 = (double(seed2) / M);
        cout << "U2 = " << u2 << endl;

        Z = get_normal(u1, u2);
        cout <<"Z = " << Z << endl;

        if (Z >= 0.0 && Z <= 0.1)
        {                                                                                               //checking for which intervals each random number falls into
            bin0++;                                                                                     //if a number falls into this interval, increment the counter by 1 each time
        }

        else if (Z > 0.1 && Z <= 0.2)                                                                   //if it doesnt fall into first interval, it will check the next interval, and so on...
        {
            bin1++;
        }

        else if (Z > 0.2 && Z <= 0.3)
        {
            bin2++;
        }

        else if (Z > 0.3 && Z <= 0.4)
        {
            bin3++;
        }

        else if (Z > 0.4 && Z <= 0.5)
        {
            bin4++;
        }

        else if (Z > 0.5 && Z <= 0.6)
        {
            bin5++;
        }

        else if (Z > 0.6 && Z <= 0.7)
        {
            bin6++;
        }

        else if (Z > 0.7 && Z <= 0.8)
        {
            bin7++;
        }

        else if (Z > 0.8 && Z <= 0.9)
        {
            bin8++;
        }

        else if (Z > 0.9 && Z <= 1.0)
        {
            bin9++;
        }
    }

    double binTotal = bin0 + bin1 + bin2 + bin3 + bin4 + bin5 + bin6 + bin7 + bin8 + bin9;
    cout << endl;

    int bin0Percent = (bin0 / binTotal) * 100;                                                          //working out a percentage 
    cout << " Number of values in range 0.0-0.1:      " << bin0 << endl;                                //output screen for each interval
    cout << " Percentage of values in this interval:   " << bin0Percent << "%" << endl;
    cout << endl;

    int bin1Percent = (bin1 / binTotal) * 100;
    cout << " Number of values in range 0.1-0.2:      " << bin1 << endl;
    cout << " Percentage of values in this interval:   " << bin1Percent << "%" << endl;
    cout << endl;

    int bin2Percent = (bin2 / binTotal) * 100;
    cout << " Number of values in range 0.2-0.3:      " << bin2 << endl;
    cout << " Percentage of values in this interval:   " << bin2Percent << "%" << endl;
    cout << endl;

    int bin3Percent = (bin3 / binTotal) * 100;
    cout << " Number of values in range 0.3-0.4:      " << bin3 << endl;
    cout << " Percentage of values in this interval:   " << bin3Percent << "%" << endl;
    cout << endl;

    int bin4Percent = (bin4 / binTotal) * 100;
    cout << " Number of values in range 0.4-0.5:      " << bin4 << endl;
    cout << " Percentage of values in this interval:   " << bin4Percent << "%" << endl;
    cout << endl;

    int bin5Percent = (bin5 / binTotal) * 100;
    cout << " Number of values in range 0.5-0.6:      " << bin5 << endl;
    cout << " Percentage of values in this interval:   " << bin5Percent << "%" << endl;
    cout << endl;

    int bin6Percent = (bin6 / binTotal) * 100;
    cout << " Number of values in range 0.6-0.7:      " << bin6 << endl;
    cout << " Percentage of values in this interval:   " << bin6Percent << "%" << endl;
    cout << endl;

    int bin7Percent = (bin7 / binTotal) * 100;
    cout << " Number of values in range 0.7-0.8:      " << bin7 << endl;
    cout << " Percentage of values in this interval:   " << bin7Percent << "%" << endl;
    cout << endl;

    int bin8Percent = (bin8 / binTotal) * 100;
    cout << " Number of values in range 0.8-0.9:      " << bin8 << endl;
    cout << " Percentage of values in this interval:   " << bin8Percent << "%" << endl;
    cout << endl;

    int bin9Percent = (bin9 / binTotal) * 100;
    cout << " Number of values in range 0.9-1.0:      " << bin9 << endl;
    cout << " Percentage of values in this interval:   " << bin9Percent << "%" << endl;
    cout << endl;
}

2 个答案:

答案 0 :(得分:1)

M太大了,在long的限制范围内。因此,此M的任何long除以(或模数)将导致0. Perphaps您应该使用unsigned long long

同时

而不是R = sqrt(-2 * log(x1));尝试R= sqrt(fabs(2 * log(x1)));

另外

phi = (2 * M_PI*x2);
u = R*cos(phi);

phi总是2 * PI的倍数,因此cos(phi)= 1。

答案 1 :(得分:1)

get_normal返回long,它不能介于0和1之间,因为它是一个整数。将函数返回的整数存储到doubleZ)并不能神奇地恢复丢弃的小数部分。

我认为你应该在double中使用浮点运算(即get_normal s),并且还要更改返回类型。

顺便说一下,C ++标准库有很多随机数分布。您可能想要使用它而不是尝试编写自己的。