Consistent Pseudo-Random Numbers Across Platforms

Consistent pseudo-random numbers across platforms

Something like a Mersenne Twister (from Boost.Random) is deterministic.

Consistent random numbers across versions and platforms

I ended up isolating the Sha1Prng from the sun sources which guarantees reproducibility on all versions of Java and android. I needed to drop some important methods to ensure compatibility with android, as android does not have access to nio classes...

Reproducibility of java pseudo-random numbers across systems and versions?

Yes.

The javadoc explains:

An instance of this class is used to generate a stream of pseudorandom numbers. The class uses a 48-bit seed, which is modified using a linear congruential formula. (See Donald Knuth, The Art of Computer Programming, Volume 2, Section 3.2.1.)

If two instances of Random are created with the same seed, and the same sequence of method calls is made for each, they will generate and return identical sequences of numbers. In order to guarantee this property, particular algorithms are specified for the class Random. Java implementations must use all the algorithms shown here for the class Random, for the sake of absolute portability of Java code. However, subclasses of class Random are permitted to use other algorithms, so long as they adhere to the general contracts for all the methods.

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

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.

Reproducibility of python pseudo-random numbers across systems and versions?

No, it doesn't. There's no such promise in the random module's documentation.

What the docs do contain is this remark:

Changed in version 2.3: MersenneTwister replaced Wichmann-Hill as the default generator

So a different RNG was used prior to Python 2.3.

So far, I've been using numpy.random.RandomState for reproducible pseudo-randomness, though it too does not make the formal promise you're after.

If you want full reproducibility, you might want to include a copy of random's source in your program, or hack together a "P²RNG" (pseudo-pseudo-RNG) from hashlib.

Crossplatform random number generator

If you need a truly portable implementation, the best option would probably be to just use a custom random number generator, such as this Mersenne Twister implementation or Colin Green's Fast Random Number Generator. By controlling the implementation, you can guarantee that you'll get the same results given the same seed on any platform.

Will System.Random always generate predictable numbers for a given seed across platforms?

As Jeremy mentioned in his answer the documentation states that the number generator is not guaranteed consistent across .NET versions.

However, the documentation also tells you how to implement your algorithm too.

You can implement your own random number generator by inheriting from the Random class and supplying your random number generation algorithm. To supply your own algorithm, you must override the Sample method, which implements the random number generation algorithm. You should also override the Next(), Next(Int32, Int32), and NextBytes methods to ensure that they call your overridden Sample method. You don't have to override the Next(Int32) and NextDouble methods.

Using that we can make our own random class that uses a known fixed algorithm you can use. For example, we could go to the .Net source and implement the current random algorithm, this would allow us to use the ConsistantRandom class and know for sure the algorithom won't change on us.

The following example is derived from the .NET Core 1.1.0 source.

using System;

namespace ConsoleApplication1
{
public class ConsistantRandom: Random
{
private const int MBIG = Int32.MaxValue;
private const int MSEED = 161803398;
private const int MZ = 0;

private int inext;
private int inextp;
private int[] SeedArray = new int[56];

public ConsistantRandom()
: this(Environment.TickCount)
{
}

public ConsistantRandom(int seed)
{
int ii;
int mj, mk;

int subtraction = (seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(seed);
mj = MSEED - subtraction;
SeedArray[55] = mj;
mk = 1;
for (int i = 1; i < 55; i++)
{
ii = (21 * i) % 55;
SeedArray[ii] = mk;
mk = mj - mk;
if (mk < 0) mk += MBIG;
mj = SeedArray[ii];
}
for (int k = 1; k < 5; k++)
{
for (int i = 1; i < 56; i++)
{
SeedArray[i] -= SeedArray[1 + (i + 30) % 55];
if (SeedArray[i] < 0) SeedArray[i] += MBIG;
}
}
inext = 0;
inextp = 21;
}
protected override double Sample()
{
return (InternalSample() * (1.0 / MBIG));
}

private int InternalSample()
{
int retVal;
int locINext = inext;
int locINextp = inextp;

if (++locINext >= 56) locINext = 1;
if (++locINextp >= 56) locINextp = 1;

retVal = SeedArray[locINext] - SeedArray[locINextp];

if (retVal == MBIG) retVal--;
if (retVal < 0) retVal += MBIG;

SeedArray[locINext] = retVal;

inext = locINext;
inextp = locINextp;

return retVal;
}

public override int Next()
{
return InternalSample();
}

private double GetSampleForLargeRange()
{
int result = InternalSample();
bool negative = (InternalSample() % 2 == 0) ? true : false;
if (negative)
{
result = -result;
}
double d = result;
d += (Int32.MaxValue - 1);
d /= 2 * (uint)Int32.MaxValue - 1;
return d;
}

public override int Next(int minValue, int maxValue)
{
if (minValue > maxValue)
{
throw new ArgumentOutOfRangeException("minValue");
}

long range = (long)maxValue - minValue;
if (range <= (long)Int32.MaxValue)
{
return ((int)(Sample() * range) + minValue);
}
else
{
return (int)((long)(GetSampleForLargeRange() * range) + minValue);
}
}
public override void NextBytes(byte[] buffer)
{
if (buffer == null) throw new ArgumentNullException("buffer");
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = (byte)(InternalSample() % (Byte.MaxValue + 1));
}
}
}

}

Because ConsistantRandom derives from Random you can use the class as a drop in replacement anywhere you had the type Random before, you just need to replace Random rnd = new Random(yourSeed); with Random rnd = new ConsistantRandom(yourSeed);.

C/C++ algorithm to produce same pseudo-random number sequences from same seed on different platforms?

Any particular pseudo-random number generation algorithm will behave like this. The problem with rand is that it's not specified how it is implemented. Different implementations will behave in different ways and even have varying qualities.

However, C++11 provides the new <random> standard library header that contains lots of great random number generation facilities. The random number engines defined within are well-defined and, given the same seed, will always produce the same set of numbers.

For example, a popular high quality random number engine is std::mt19937, which is the Mersenne twister algorithm configured in a specific way. No matter which machine, you're on, the following will always produce the same set of real numbers between 0 and 1:

std::mt19937 engine(0); // Fixed seed of 0
std::uniform_real_distribution<> dist;
for (int i = 0; i < 100; i++) {
std::cout << dist(engine) << std::endl;
}


Related Topics



Leave a reply



Submit