18

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.

Arno Duvenhage
  • 1,910
  • 17
  • 36
  • 2
    Is the [boost random number](http://www.boost.org/doc/libs/1_60_0/doc/html/boost_random.html) library what you need? – shuttle87 Jan 20 '16 at 15:06
  • @shuttle87, I've been slowly ejecting all boost from my code-base, so I'm not keen on pulling boost libs back in again. – Arno Duvenhage Jan 20 '16 at 15:11
  • 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. – shuttle87 Jan 20 '16 at 15:17
  • @shuttle87 is boost.random guaranteed to deliver the same results on different platforms? – Arno Duvenhage Jan 20 '16 at 15:42
  • 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. – ChrisR Jan 20 '16 at 16:11
  • Current approach: develop my own random distributions that match the C++11 templates.. surprisingly little code required so far. – Arno Duvenhage Jan 21 '16 at 12:38
  • @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. – MSalters Jan 21 '16 at 14:28
  • @MSalters: I have looked at the libstdc++ code, but I have not gone through the license yet - thought it was open source. Thank you – Arno Duvenhage Jan 22 '16 at 04:44
  • @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. – MSalters Jan 22 '16 at 07:27
  • 1
    Came across this: http://www.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. – Arno Duvenhage Jan 22 '16 at 07:38
  • Possible duplicate of [How to generate the same random number sequence over multiple types of compilers and kernels with ?](https://stackoverflow.com/questions/28607889/how-to-generate-the-same-random-number-sequence-over-multiple-types-of-compilers) – DanielTuzes Nov 26 '19 at 00:35

1 Answers1

17

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.

Arno Duvenhage
  • 1,910
  • 17
  • 36
  • 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" – thomthom Sep 28 '20 at 20:03
  • Used it for floating point only, but I do not see why you would get an exception. – Arno Duvenhage Sep 30 '20 at 13:42
  • When given an integer you get an divide by zero exception. – thomthom Sep 30 '20 at 14:20
  • Yea, the normal distribution relies on a real uniform distribution underneath. See https://stackoverflow.com/questions/22016007/how-can-i-create-a-normal-integer-distribution-in-c11 – Arno Duvenhage Oct 01 '20 at 14:42