EARandom

Introduction

EARandom provides pseudo-random number generation suitable for most kinds of professional game development. Functionality is divided into two modules:

EARandom provides two core generators which address a speed/randomness tradeoff:

EARandom provides a number of distribution generators, each of which uses the above core generators underneath:

Note: EARandom is not certified nor suitable for use in certified gambling software. There are strict standards regarding such software which EARandom does not try to comply with. Similarly, EARandom is not suitable for use in security-related entropy collection (at least not by itself). EARandom's goal is to provide good random number generation with high efficiency.

Don't use the C rand() function!

The C library rand function does an OK job as a basic random number generator for testing and hobby purposes. However, the rand function is generally not suitable for professional game software. This is because the rand function:

How random is EARandom?

Both RandomLinearCongruential (i.e. RandomFast) and RandomMersenneTwister (RandomQuality) provide randomness that is likely good enough for most convential game uses. Don't be fooled by the "linear congruential" name; it's not the bad generator that you might think based on what you've read about old C rand implementations.

A good way to test randomness is with the DieHard randomness tests. See http://en.wikipedia.org/wiki/Diehard_tests for information about DieHard. We have a copy of the DieHard tester in EAOS which is used to test EARandom and could test any other random number generator. EARandom produces DieHard scores which are pretty good. A number of home-grown random number generators have shown much worse results.

Common misuses

It is worth mentioning that it is surprisingly common for users of random number generators (including EARandom) to come to the belief that the generator is broken when in fact they are misusing the generator. Common misuses of generators include:

Example usage

EARandom is fairly straightforward to use. Just avoid the above common mistakes and things should work well.

Mixed integer math expressions.

EA::RandomFast rng(GetCPUTick());           // Seed with a hypthetical CPU tick function.

uint32_t n = rng.RandomUint32Uniform();     // Generate value in the range of [0, 0xffffffff] (all bit patterns).
uint32_t i = rng.RandomUint32Uniform(200);  // Generate value in the range of [0, 200).
double   d = rng.RandomDoubleUniform();     // Generate value in the range of [0, 1).
double   f = rng.RandomDoubleUniform(37);   // Generate value in the range of [0, 37).
How to use EARandom with STL (including EASTL).
#include <algorithm>
#include <vector>

std::vector    v;
EA::RandomFast rng;

std::random_shuffle(v.begin(), v.end(), rng);
How to use basic random distributions. Note that these functions take a random number generator as an argument.
EA::RandomQuality rng;

bool    b = EA::RandomBool(rng);
int32_t n = EA::Random256(rng);
double  d = EA::RandomDoubleGaussian(rng, 15.0, 30.0);

How to use RandomUint32WeightedChoice. This function is useful for generating a custom distribution.

EA::RandomQuality rng;
const float       weights[10] = { 1, 2, 3, 4, 5, 5, 4, 3, 2, 1 }; // Create a triangle distribution

uint32_t i = EA::RandomUint32WeightedChoice(rng, 10, weights); 

Interface

RandomLinearCongruential

This algorithm generates good enough pseudorandom numbers for most simulationuses. Its biggest weakness is that there are some patterns that occur in the lower bits. However, it is still far better than the C rand function.

class RandomLinearCongruential
{
public:
	typedef uint32_t result_type;
 

    /// RandomLinearCongruential
    /// Constructs the random number generator with a given initial state (seed).
    /// If the seed is 0xffffffff (MAX_UINT32), then the seed is generated automatically
    /// by a semi-random internal mechanism such as reading the system clock. Note that 
    /// seeding by this mechanism can yield unexpected poor results if you create multiple
    /// instances of this class within a short period of time, as they will all get the 
    /// same seed due to the system clock having not advanced.
    RandomLinearCongruential(uint32_t nSeed = 0xffffffff);

    /// RandomLinearCongruential
    /// Copy constructor
    RandomLinearCongruential(const RandomLinearCongruential& randomLC);

    /// operator =
    RandomLinearCongruential& operator=(const RandomLinearCongruential& randomLC);

    /// GetSeed
    /// Gets the state of the random number generator, which can be entirely 
    /// defined by a single uint32_t. 
    uint32_t GetSeed() const;

    /// SetSeed
    /// Sets the current state of the random number generator, which can be 
    /// entirely defined by a single uint32_t.
    void SetSeed(uint32_t nSeed = 0xffffffff);

    /// operator ()
    /// Generates a random uint32 with an optional limit. Acts the same as the 
    /// RandomUint32Uniform(uint32_t nLimit) function. This function is useful for
    /// some templated uses whereby you want the class to act as a function object.
    /// If the input nLimit is 0, then the return value is from 0 to MAX_UINT32 inclusively.
    uint32_t operator()(uint32_t nLimit = 0);

    /// RandomUint32Uniform
    /// Return value is from 0 to MAX_UINT32 inclusively, with uniform probability.
    /// This is the most basic random integer generator for this class; it has no 
    /// extra options but is also the fastest. Note that if you want a random
    /// integer between 0 and some value, you should use RandomUint32Uniform(nLimit)
    /// and not use RandomUint32Uniform() % nLimit. The former is both faster and 
    /// more random; using % to achieve a truly random distribution fails unless
    /// nLimit is evenly divisible into MAX_UINT32.
    uint32_t RandomUint32Uniform();

    /// RandomUint32Uniform (with limit)
    /// Return value is from 0 to nLimit-1 inclusively, with uniform probability.
    uint32_t RandomUint32Uniform(uint32_t nLimit);

    /// RandomDoubleUniform
    /// Output is in range of [0, 1) with uniform distribution.
    double RandomDoubleUniform();

    /// RandomDoubleUniform (with limit)
    /// Output is in range of [0, limit) with uniform distribution.
    double RandomDoubleUniform(double limit);
};

RandomTaus

RandomTaus is slower than the other EARandom generators but has only 12 bytes of state data. RandomLinearCongruental has only 4 bytes of data but is not as random as RandomTaus. RandomMersenneTwister is more random than RandomTaus but has about 2500 bytes of state data. Thus RandomTaus is a tradeoff. This generator optimizes randomness and and to some degree size at the cost of speed.

class RandomTaus
{
public:
    typedef uint32_t result_type;


    RandomTaus(uint32_t nSeed = 0xffffffff);
    RandomTaus(const uint32_t* pSeedArray); // Array of 3 uint32_t values.
 
    RandomTaus(const RandomTaus& randomT);
    RandomTaus& operator=(const RandomTaus& randomT);

    // Single uint32_t version, for compatibility.
    // Use the seed array version for best behavior.
    // Not guaranteed to return the uint32_t set by SetSeed(uint32_t).
    uint32_t GetSeed() const;
    void SetSeed(uint32_t nSeed = 0xffffffff);

    void GetSeed(uint32_t* pSeedArray) const; // Array of 3 uint32_t values.
    void SetSeed(const uint32_t* pSeedArray); // Array of 3 uint32_t values.

    /// Output is in range of [0, nLimit) with uniform distribution.
    uint32_t operator()(uint32_t nLimit = 0);

    /// Output is in range of [0, UINT32_MAX] with uniform distribution.
    uint32_t RandomUint32Uniform();

    /// Output is in range of [0, nLimit) with uniform distribution.
    uint32_t RandomUint32Uniform(uint32_t nLimit);

    /// Output is in range of [0, 1) with uniform numeric (not bit) distribution.
    double RandomDoubleUniform();

    /// Output is in range of [0, limit) with uniform numeric (not bit) distribution.
    /// limit is a value > 0.
    double RandomDoubleUniform(double limit);
};

RandomMersenneTwister

This algorithm implements a random number generator via the Mersenne Twister algorithm. This algorithm is popular for its very high degree of randomness (period of 2^19937-1 with 623-dimensional equidistribution) while achieving good speed.

class RandomMersenneTwister
{
public:
    typedef uint32_t result_type;
 
    /// enum SeedArray
    /// This enum is public because it allows the user to know how much 
    /// data or space to provide for the GetSeed and SetSeed functions.
    enum SeedArray { kSeedArrayCount = 624 };

    RandomMersenneTwister(uint32_t nSeed = 0xffffffff);
    RandomMersenneTwister(const uint32_t seedArray[], unsigned nSeedArraySize);
    RandomMersenneTwister(const RandomMersenneTwister& randomMT);

    RandomMersenneTwister& operator=(const RandomMersenneTwister& randomMT);

    void     GetSeed(uint32_t seedArray[], unsigned nSeedArraySize) const;
    void     SetSeed(const uint32_t seedArray[], unsigned nSeedArraySize);
    void     SetSeed(uint32_t nSeed = 0xffffffff);

    uint32_t operator()(uint32_t nLimit = 0);

    uint32_t RandomUint32Uniform();                                          
    uint32_t RandomUint32Uniform(uint32_t nLimit);

    double   RandomDoubleUniform();
    double   RandomDoubleUniform(double limit);
};

Random distributions

Here is a list of the currently provided distribution functions.

/// RandomBool
/// Returns true or false.
template <typename Random>
bool RandomBool(Random& r);

/// Random2
/// Returns a value between 0 and 1, inclusively.
template <typename Random>
int32_t Random2(Random& r);

/// Random4
/// Returns a value between 0 and 3, inclusively.
template <typename Random>
int32_t Random4(Random& r);

/// Random8
/// Returns a value between 0 and 7, inclusively.
template <typename Random>
int32_t Random8(Random& r);

/// Random16
/// Returns a value between 0 and 15, inclusively.
template <typename Random>
int32_t Random16(Random& r);

/// Random32
/// Returns a value between 0 and 31, inclusively.
template <typename Random>
int32_t Random32(Random& r);

/// Random64
/// Returns a value between 0 and 63, inclusively.
template <typename Random>
int32_t Random64(Random& r);

/// Random128
/// Returns a value between 0 and 127, inclusively.
template <typename Random>
int32_t Random128(Random& r);

/// Random256
/// Returns a value between 0 and 255, inclusively.
template <typename Random>
int32_t Random256(Random& r);

/// RandomPowerOfTwo
/// Returns a value between 0 and 2 ^ nPowerOfTwo - 1, inclusively. 
/// This is a generalized form of the RandomN set of functions.
template <typename Random>
int32_t RandomPowerOfTwo(Random& r, unsigned nPowerOfTwo);

/// RandomInt32UniformRange
/// Return value is from nBegin to nEnd-1 inclusively, with uniform probability.
template <typename Random>
int32_t RandomInt32UniformRange(Random& r, int32_t nBegin, int32_t nEnd);

/// RandomDoubleUniformRange
/// Return value is in range of [nBegin, nEnd) with uniform probability.
template <typename Random>
double RandomDoubleUniformRange(Random& r, double begin, double end);

/// RandomUint32WeightedChoice
/// Return value is from 0 to nLimit-1 inclusively, with probabilities proportional to weights.
/// The input array weights must be of length . These values are used to 
/// determine the probability of each choice. That is, weight[i] is proportional 
/// to the probability that this function will return i. Negative values are ignored. 
/// This function is useful in generating a custom distribution.
template <typename Random>
uint32_t RandomUint32WeightedChoice(Random& r, uint32_t nLimit, float weights[]);

/// RandomUint32Gaussian
/// Return value is in the range [0, nLimit) with Gaussian (a.k.a 'normal') distribution.
template <typename Random>
uint32_t RandomUint32Gaussian(Random& r, int32_t nBegin, int32_t nEnd);

/// RandomDoubleGaussian
/// Return value is in the range [0, nLimit) with Gaussian (a.k.a 'normal') distribution.
template <typename Random>
double RandomDoubleGaussian(Random& r, double nBegin, double nEnd);

/// RandomUint32Triangle
/// Return value is in the range [0, nLimit) with triangular distribution.
template <typename Random>
uint32_t RandomUint32Triangle(Random& r, int32_t nBegin, int32_t nEnd);

/// RandomDoubleTriangle
/// Return value is in the range [0, nLimit) with triangular distribution.
template <typename Random>
double RandomDoubleTriangle(Random& r, double nBegin, double nEnd);

Random fills and shuffles

How to fill a container or sequence with random uint32_t values:

#include <eastl/algorithm.h>                             // or #include <algorithm> to use std STL.
 
EA::StdC::Random rand(someSeedValue);                    // We can just use EA::StdC::Random directly because 
eastl::generate(myVector.begin(), myVector.end(), rand); // it has an operator() that returns uint32_t.

How to randomize (shuffle) an existing container or sequence of uint32_t values:

#include <eastl/algorithm.h>                             // or #include <algorithm> to use std STL.
 
EA::StdC::Random rand(someSeedValue);
eastl::random_shuffle(myVector.begin(), myVector.end(), rand);

How to fill a container or sequence with random double:

#include <eastl/algorithm.h>                             // or #include <algorithm> to use std STL.
 
struct GenerateDouble
{ 
EA::StdC::Random mRand; // We need to make a tiny class that simply has
// an operator() member function that returns double. GenerateDouble(uint32_t seed) : mRand(seed){}
double operator(){ mRand.RandomDoubleUniform(); } };
GenerateDouble randDouble(someSeedValue);
eastl::generate(myVector.begin(), myVector.end(), randDouble);