275 lines
25 KiB
MQL5
275 lines
25 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Random.mqh |
|
|
//| Copyright © 2018, Amr Ali |
|
|
//| https://www.mql5.com/en/users/amrali |
|
|
//+------------------------------------------------------------------+
|
|
#property description "Random number generation using the 32-bit PCG generator."
|
|
//+------------------------------------------------------------------+
|
|
//| Class CRandom |
|
|
//| Usage: Random number generation using the 32-bit PCG generator. |
|
|
//+------------------------------------------------------------------+
|
|
class CRandom
|
|
{
|
|
protected:
|
|
//--- The generator uses two 64-bit integers for its internal state
|
|
ulong m_state;
|
|
ulong m_inc;
|
|
//---
|
|
void pcg32_srandom_r(const ulong initstate,const ulong initseq);
|
|
uint pcg32_random_r(void);
|
|
uint pcg32_boundedrand_r(const uint bound);
|
|
ulong pcg32x2_random_r(void);
|
|
|
|
public:
|
|
//--- Constructor and destructor
|
|
CRandom(void); // Constructor (auto-seed)
|
|
CRandom(const ulong initstate,const ulong initseq); // Constructor (custom seed)
|
|
~CRandom(void) { }
|
|
|
|
//--- Methods for generation of random numbers
|
|
int RandomInteger(void); // Random integer [0,2147483648)
|
|
int RandomInteger(const int max); // Random integer [0,max)
|
|
int RandomInteger(const int min,const int max); // Random integer [min,max)
|
|
|
|
double RandomDouble(void); // Random double [0.0,1.0)
|
|
double RandomDouble(const double max); // Random double [0.0,max)
|
|
double RandomDouble(const double min,const double max); // Random double [min,max)
|
|
|
|
bool RandomBoolean(void); // Random true/false (equal probability)
|
|
bool RandomBoolean(const double prob_true); // Random true/false (with prob_true)
|
|
|
|
double RandomNormal(void); // Random double (normal distribution)
|
|
double RandomNormal(const double mu,const double sigma); // Random double (normal distribution)
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initializes a new instance of the CRandom class with auto-seed. |
|
|
//+------------------------------------------------------------------+
|
|
void CRandom::CRandom(void)
|
|
{
|
|
pcg32_srandom_r(GetTickCount(), ChartID() * MathRand());
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Initializes a new instance of the CRandom class and set the |
|
|
//| initial values for the generator of random numbers. |
|
|
//| Seed the rng. Specified in two parts, state initializer and a |
|
|
//| sequence selection constant (a.k.a. stream id) |
|
|
//+------------------------------------------------------------------+
|
|
void CRandom::CRandom(const ulong initstate,const ulong initseq)
|
|
{
|
|
pcg32_srandom_r(initstate, initseq);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random integer in the range [0,2147483648) |
|
|
//| The maximum possible random integer is 2147483647 (INT_MAX). |
|
|
//+------------------------------------------------------------------+
|
|
int CRandom::RandomInteger(void)
|
|
{
|
|
//--- return result
|
|
return (int)(pcg32_random_r() >> 1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random integer in the range [0,max) |
|
|
//| max : exclusive and must be greater than zero. |
|
|
//+------------------------------------------------------------------+
|
|
int CRandom::RandomInteger(const int max)
|
|
{
|
|
if(max <= 0)
|
|
return(-1);
|
|
//--- return result
|
|
return (int)pcg32_boundedrand_r(max);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random integer in the range [min,max) |
|
|
//| min : inclusive |
|
|
//| max : exclusive and must be greater than min. |
|
|
//+------------------------------------------------------------------+
|
|
int CRandom::RandomInteger(const int min,const int max)
|
|
{
|
|
if(max <= min)
|
|
return(-1);
|
|
//--- return result
|
|
return (int)pcg32_boundedrand_r(max - min) + min;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random double in the range [0.0, 1.0) |
|
|
//+------------------------------------------------------------------+
|
|
//| https://docs.python.org/3/library/random.html#recipes |
|
|
//| http://mumble.net/~campbell/tmp/random_real.c |
|
|
//| The mantissa comes from a uniform distribution of integers in |
|
|
//| the range 0 <= mantissa < 2^52. The exponent comes from a |
|
|
//| geometric distribution where exponents smaller than -53 occur |
|
|
//| half as often as the next larger exponent. The recipe is |
|
|
//| conceptually equivalent to an algorithm that chooses from all |
|
|
//| the multiples of 2^-1074 in the range 0.0 = x < 1.0. All floats |
|
|
//| in the interval [0, 1) are possible selections. |
|
|
//+------------------------------------------------------------------+
|
|
double CRandom::RandomDouble(void)
|
|
{
|
|
ulong mantissa = pcg32x2_random_r() >> 12;
|
|
double significand = 1.0 + mantissa * DBL_EPSILON;
|
|
|
|
//--- sample p from geometric(2) by finding the first 1-bit
|
|
//--- in a stream of bits generated uniformly at random and
|
|
//--- interpret it as a power of two, 00101000000000000000.
|
|
double power2 = 2.0;
|
|
uint x;
|
|
while((x = pcg32_random_r()) == 0)
|
|
{
|
|
power2 *= 4294967296.0; // 2^32
|
|
}
|
|
|
|
//--- x & -x creates a bitmask for the rightmost set bit in x.
|
|
//--- power2 is 2 with probability 1/2, 4 with probability 1/4,
|
|
//--- 8 with probability 1/8, and so on.
|
|
power2 *= (x & (0-x));
|
|
|
|
//--- scale 2^52 numbers from [1, 2) into a smaller binade,
|
|
//--- this constructs fp number in [0, 1) as dyadic rational.
|
|
return significand / power2;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random double in the range [0.0,max) |
|
|
//| max : exclusive and must be greater than zero. |
|
|
//+------------------------------------------------------------------+
|
|
double CRandom::RandomDouble(const double max)
|
|
{
|
|
if(max <= 0)
|
|
return(-1);
|
|
//--- return result
|
|
for(;;)
|
|
{
|
|
double r = RandomDouble() * max;
|
|
//--- check for rounding error
|
|
if(r < max)
|
|
return r;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random double in the range [min,max) |
|
|
//| min : inclusive |
|
|
//| max : exclusive and must be greater than min. |
|
|
//+------------------------------------------------------------------+
|
|
double CRandom::RandomDouble(const double min,const double max)
|
|
{
|
|
if(max <= min)
|
|
return(-1);
|
|
//--- return result
|
|
for(;;)
|
|
{
|
|
double r = RandomDouble() * (max - min) + min;
|
|
//--- check for rounding error
|
|
if(r < max)
|
|
return r;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random true/false with equal probability |
|
|
//+------------------------------------------------------------------+
|
|
bool CRandom::RandomBoolean(void)
|
|
{
|
|
return pcg32_random_r() <= INT_MAX;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random true/false where 'true' has the probability of prob_true |
|
|
//+------------------------------------------------------------------+
|
|
bool CRandom::RandomBoolean(const double prob_true)
|
|
{
|
|
return pcg32_random_r() <= prob_true * UINT_MAX;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random normal sample with specified mean and standard deviation |
|
|
//+------------------------------------------------------------------+
|
|
double CRandom::RandomNormal(const double mu,const double sigma)
|
|
{
|
|
return mu + sigma * RandomNormal();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random normal sample with mean 0 and standard deviation 1 |
|
|
//+------------------------------------------------------------------+
|
|
double CRandom::RandomNormal(void)
|
|
{
|
|
// Use Box-Muller algorithm
|
|
double u1 = RandomDouble();
|
|
double u2 = RandomDouble();
|
|
double r=::MathSqrt(-2.0*::MathLog(u1));
|
|
double theta=2.0*M_PI*u2;
|
|
return r * ::MathSin(theta);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| PCG Random Number Generation, Minimal C Edition |
|
|
//| |
|
|
//| http://www.pcg-random.org |
|
|
//| |
|
|
//| Copyright 2014 Melissa O'Neill <oneill@pcg-random.org> |
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
//| Set the initial values for the generator of random numbers. |
|
|
//| Seed the rng. Specified in two parts, state initializer and a |
|
|
//| sequence selection constant (a.k.a. stream id) |
|
|
//+------------------------------------------------------------------+
|
|
void CRandom::pcg32_srandom_r(const ulong initstate,const ulong initseq)
|
|
{
|
|
m_state = 0;
|
|
m_inc = (initseq << 1) | 1; // Must *always* be odd.
|
|
pcg32_random_r();
|
|
m_state += initstate;
|
|
pcg32_random_r();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed 32-bit unsigned integer [0,UINT_MAX] |
|
|
//| This is the heart of the generator. |
|
|
//+------------------------------------------------------------------+
|
|
uint CRandom::pcg32_random_r(void)
|
|
{
|
|
ulong oldstate = m_state;
|
|
m_state = oldstate * (ulong)6364136223846793005 + m_inc;
|
|
uint xorshifted = (uint)(((oldstate >> 18) ^ oldstate) >> 27);
|
|
uint rot = (uint)(oldstate >> 59);
|
|
return (xorshifted >> rot) | (xorshifted << ((0-rot) & 31));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random integer, r, where 0 <= r < bound |
|
|
//| Generates random numbers less than a given bound. |
|
|
//+------------------------------------------------------------------+
|
|
uint CRandom::pcg32_boundedrand_r(const uint bound)
|
|
{
|
|
// To avoid bias, we need to make the range of the RNG a multiple of
|
|
// bound, which we do by dropping output less than a threshold.
|
|
// A naive scheme to calculate the threshold would be to do
|
|
//
|
|
// uint32_t threshold = 0x100000000ull % bound;
|
|
//
|
|
// but 64-bit div/mod is slower than 32-bit div/mod (especially on
|
|
// 32-bit platforms). In essence, we do
|
|
//
|
|
// uint32_t threshold = (0x100000000ull-bound) % bound;
|
|
//
|
|
// because this version will calculate the same modulus, but the LHS
|
|
// value is less than 2^32.
|
|
|
|
uint threshold = (0-bound) % bound;
|
|
|
|
// Uniformity guarantees that this loop will terminate. In practice, it
|
|
// should usually terminate quickly; on average (assuming all bounds are
|
|
// equally likely), 82.25% of the time, we can expect it to require just
|
|
// one iteration. In the worst case, someone passes a bound of 2^31 + 1
|
|
// (i.e., 2147483649), which invalidates almost 50% of the range. In
|
|
// practice, bounds are typically small and only a tiny amount of the range
|
|
// is eliminated.
|
|
for(;;)
|
|
{
|
|
uint r = pcg32_random_r();
|
|
if(r >= threshold)
|
|
return r % bound;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed 64-bit unsigned long integer [0,ULONG_MAX] |
|
|
//| We build a 64-bit number by tying together two 32-bit numbers. |
|
|
//+------------------------------------------------------------------+
|
|
ulong CRandom::pcg32x2_random_r(void)
|
|
{
|
|
return ((ulong)(pcg32_random_r()) << 32)
|
|
| pcg32_random_r();
|
|
}
|
|
//+------------------------------------------------------------------+
|