使用std :: random创建全范围的随机浮点数

时间:2014-04-06 14:06:57

标签: c++ c++11 random

我试图测试我使用来自全部可表示的正float的随机数创建的数学课程,但我发现我似乎遇到了问题使用std::random。这个程序

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

template <typename T>
class Rand {
public:
    Rand(T lo=std::numeric_limits<T>::min(), 
         T hi=std::numeric_limits<T>::max()) : 
        r(bind(std::uniform_real_distribution<>(lo, hi),std::mt19937_64{})) {}
    T operator()() const { return r(); }
private:
    std::function<T()> r;
};

int main()
{
    Rand<float> f{};
    const int samples = 1000000;
    float min = std::numeric_limits<float>::max();
    float max = std::numeric_limits<float>::min();
    std::cout << "range min = " << max 
        << ", max = " << min << '\n';
    for (int i=0; i < samples; ++i) {
        float r = f();
        if (r < min) min = r;
        if (r > max) max = r;
    }
    std::cout << "for n = " << samples 
        << "\nsample min = " << min 
        << ", max = " << max << std::endl;
}

产生此输出

range min = 1.17549e-38, max = 3.40282e+38
for n = 1000000
sample min = 8.14884e+31, max = 3.40281e+38

显然,范围极大地倾向于更大的数字。如何生成具有均匀分布的float的所需范围?

2 个答案:

答案 0 :(得分:5)

除了你打印出的统计数据外,我还计算了this distribution的理论和实际均值,方差,偏斜和峰度。这是我的代码和结果:

#include <random>
#include <iostream>
#include <functional>
#include <vector>
#include <numeric>
#include <cmath>

template <typename T>
class Rand {
public:
    Rand(T lo=std::numeric_limits<T>::min(), 
         T hi=std::numeric_limits<T>::max()) : 
        r(bind(std::uniform_real_distribution<>(lo, hi),std::mt19937_64{})) {}
    T operator()() const { return r(); }
private:
    std::function<T()> r;
};

template <class T>
inline
T
sqr(T x)
{
    return x * x;
}

int main()
{
    Rand<float> f{};
    const int samples = 1000000;
    float min = std::numeric_limits<float>::max();
    float max = std::numeric_limits<float>::min();
    std::vector<float> u;
    std::cout << "range min = " << max 
        << ", max = " << min << '\n';
    for (int i=0; i < samples; ++i) {
        float r = f();
        if (r < min) min = r;
        if (r > max) max = r;
        u.push_back(r);
    }
    std::cout << "for n = " << samples 
        << "\nsample min = " << min 
        << ", max = " << max << std::endl;
    double mean = std::accumulate(u.begin(), u.end(),
                                          double(0)) / u.size();
    double var = 0;
    double skew = 0;
    double kurtosis = 0;
    for (int i = 0; i < u.size(); ++i)
    {
        double d = (u[i] - mean);
        double d2 = sqr(d);
        var += d2;
        skew += d * d2;
        kurtosis += d2 * d2;
    }
    var /= u.size();
    double dev = std::sqrt(var);
    skew /= u.size() * dev * var;
    kurtosis /= u.size() * var * var;
    kurtosis -= 3;
    double x_mean = ((double)min + max) / 2;
    double x_var = sqr((double)max - min) / 12;
    double x_skew = 0;
    double x_kurtosis = -6./5;
    std::cout << std::scientific << '\n';
    std::cout << "            expected        actual\n";
    std::cout << "mean      " << x_mean     << "       " << mean << "\n";
    std::cout << "variance  " << x_var      << "       " << var << "\n";
    std::cout << "skew      " << x_skew     << "       " << skew << "\n";
    std::cout << "kurtosis  " << x_kurtosis << "       " << kurtosis << "\n";
}

以下是结果:

range min = 1.17549e-38, max = 3.40282e+38
for n = 1000000
sample min = 8.14884e+31, max = 3.40281e+38

            expected        actual
mean      1.701407e+38       1.700724e+38
variance  9.649275e+75       9.645774e+75
skew      0.000000e+00       7.401975e-04
kurtosis  -1.200000e+00       -1.199432e+00

一切对我来说都很好看。

答案 1 :(得分:-1)

您缺少的最重要的一点是,您不是在(-max_value,max_value)之间生成数字,而是在(0,max_value)之间生成数字。

在0到8.14884e + 31之间有大约0(2,32)个数字,但是在8.14884e + 31和3.40281e + 38之间有大约(30,37)个数字。因此结果很明显。