C++11 random number distributions are not consistent across platforms -- what alternatives are there?
Asked Answered
A

1

19

I'm looking for a set of portable distributions for the standard C++11 engines like `std::mt19937' (see http://en.cppreference.com/w/cpp/numeric/random).

The engine implementations perform consistently (i.e. same sequence generated on different platforms – tested with Clang and MSVC), but the distributions seem to be implemented differently on the different platforms.

So, even though the engines produce the same sequence, it seems that a distribution (for example, std::normal_distribution<double>) does not use the same number of samples (i.e. produces different results) on the different platforms, which is not acceptable in my case.

Is there maybe a 3rd party lib I can use that follows the C++11 random templates, but that will deliver consistent values across popular platforms (Looking at support across GCC, MSVC and Clang/llvm).

Options I have looked at so far are:

  • Boost.random (a bit heavy, but worthwhile since it matches the c++11 counterparts quite well)
  • Cloning from libstd++ (also worthwhile and probably portable, but pulling out specific functions might not be straightforward)
  • Creating my own C++11-like random distributions

I need uniform, normal, poison and Rayleigh.

Adelia answered 20/1, 2016 at 14:57 Comment(11)
Is the boost random number library what you need?Ascham
@shuttle87, I've been slowly ejecting all boost from my code-base, so I'm not keen on pulling boost libs back in again.Adelia
Oh I totally understand not wanting the dependency on boost because of the size of that dependency. However much of the weight of the boost libraries comes from making things cross platform which is useful in the use case you are asking about. If there's a smaller library that only contained the random number functionality I would also like to know about that.Ascham
@Ascham is boost.random guaranteed to deliver the same results on different platforms?Adelia
What about looking for an open source distribution / implementation and cloning it as part of your own codebase/database? You can then control the results ffor any of your environments.Chute
Current approach: develop my own random distributions that match the C++11 templates.. surprisingly little code required so far.Adelia
@ArnoDuvenhage: Be aware that " the C++11 templates" are in reality the templates provided by your compiler. These are copyrighted! Usually this isn't an issue because you can use the Standard Library with the matching compiler. However, you can't just use the GCC distribution with MSVC or vice versa.Alternative
@MSalters: I have looked at the libstdc++ code, but I have not gone through the license yet - thought it was open source. Thank youAdelia
@Arno: It is Open Source. Open Source means it's copyrighted, but if you abide by the relevant Open Source license terms you are allowed to copy the code. I.e read the license.Alternative
Came across this: johndcook.com/blog/cpp_random_number_generation It seems very complete, but I will have to adapt the distributions to work with C++11 engines.Adelia
Possible duplicate of How to generate the same random number sequence over multiple types of compilers and kernels with <random>?Sandhog
A
18

I have created my own C++11 distributions:

template <typename T>
class UniformRealDistribution
{
 public:
    typedef T result_type;

 public:
    UniformRealDistribution(T _a = 0.0, T _b = 1.0)
        :m_a(_a),
         m_b(_b)
    {}

    void reset() {}

    template <class Generator>
    T operator()(Generator &_g)
    {
        double dScale = (m_b - m_a) / ((T)(_g.max() - _g.min()) + (T)1); 
        return (_g() - _g.min()) * dScale  + m_a;
    }

    T a() const {return m_a;}
    T b() const {return m_b;}

 protected:
    T       m_a;
    T       m_b;
};

template <typename T>
class NormalDistribution
{
 public:
    typedef T result_type;

 public:
    NormalDistribution(T _mean = 0.0, T _stddev = 1.0)
        :m_mean(_mean),
         m_stddev(_stddev)
    {}

    void reset()
    {
        m_distU1.reset();
    }

    template <class Generator>
    T operator()(Generator &_g)
    {
        // Use Box-Muller algorithm
        const double pi = 3.14159265358979323846264338327950288419716939937511;
        double u1 = m_distU1(_g);
        double u2 = m_distU1(_g);
        double r = sqrt(-2.0 * log(u1));
        return m_mean + m_stddev * r * sin(2.0 * pi * u2);
    }

    T mean() const {return m_mean;}
    T stddev() const {return m_stddev;}

protected:
    T                           m_mean;
    T                           m_stddev;
    UniformRealDistribution<T>  m_distU1;
};

The uniform distribution seems to deliver good results and the normal distribution delivers very good results:

100000 values -> 68.159% within 1 sigma; 95.437% within 2 sigma; 99.747% within 3 sigma

The normal distribution uses the Box-Muller method, which according to what I have read so far, is not the fastest method, but it runs more that fast enough for my application.

Both the uniform and normal distributions should work with any C++11 engine (tested with std::mt19937) and provides the same sequence on all platforms, which is exactly what I wanted.

Adelia answered 23/1, 2016 at 11:36 Comment(4)
Is this for floating point only? I trying this in a project of mine where I gave the range in ints and I'm getting "floating point exception"Beare
Used it for floating point only, but I do not see why you would get an exception.Adelia
When given an integer you get an divide by zero exception.Beare
Yea, the normal distribution relies on a real uniform distribution underneath. See #22016507Adelia

© 2022 - 2024 — McMap. All rights reserved.