444 lines
17 KiB
MQL5
444 lines
17 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Main.mqh |
|
|
//| Copyright 2026, Niquel Mendoza |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2026, Niquel Mendoza"
|
|
#property link "https://www.mql5.com"
|
|
#property strict
|
|
|
|
|
|
#ifndef PRNGBYLEO_SRC_MAIN_MQH
|
|
#define PRNGBYLEO_SRC_MAIN_MQH
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
// Original API design and implementation: Copyright © 2025, Amr Ali
|
|
// (https://forge.mql5.io/amrali/Xoshiro256)
|
|
// Refactored into a generic template backend by Niquel Mendoza, 2026
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
namespace TSN
|
|
{
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
// convencion backend a de tener
|
|
/*
|
|
uint nextUInt32(void);
|
|
ulong nextUInt64(void);
|
|
Se deben de tener dichas funciones.
|
|
*/
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
class CRandom : public TBackend
|
|
{
|
|
private:
|
|
ulong mulhi(const ulong x, const ulong y, ulong &lowPart);
|
|
|
|
public:
|
|
CRandom(void) {}
|
|
~CRandom(void) {}
|
|
|
|
//--- Boundend for (32 \ 64)
|
|
uint boundedUInt32(const uint bound);
|
|
ulong boundedUInt64(const ulong bound);
|
|
|
|
//--- Public methods for generating random numbers
|
|
// Integer
|
|
__forceinline int RandomInteger(void); // Random integer [0, INT_MAX]
|
|
__forceinline int RandomInteger(const int max); // Random integer [0, max)
|
|
__forceinline int RandomInteger(const int min, const int max); // Random integer [min, max)
|
|
|
|
// Long
|
|
__forceinline long RandomLong(void); // Random long [0, LONG_MAX]
|
|
__forceinline long RandomLong(const long max); // Random long [0, max)
|
|
__forceinline long RandomLong(const long min, const long max); // Random long [min, max)
|
|
|
|
// Double
|
|
__forceinline 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)
|
|
|
|
// Double High Res
|
|
double RandomDoubleHighRes(void); // Random double [0.0, 1.0)
|
|
double RandomDoubleHighRes(const double max); // Random double [0.0, max)
|
|
double RandomDoubleHighRes(const double min, const double max); // Random double [min, max)
|
|
|
|
// Boolean
|
|
__forceinline bool RandomBoolean(void); // Random true/false (equal probability)
|
|
bool RandomBoolean(const double prob_true); // Random true/false (with prob_true)
|
|
|
|
// Normal
|
|
double RandomNormal(void); // Random double (normal distribution with mean 0, std dev 1)
|
|
__forceinline double RandomNormal(const double mu, const double sigma); // Random double (normal distribution)
|
|
|
|
// Sampling utilities
|
|
template<typename T>
|
|
void Shuffle(T &arr[]); // Shuffle array arr[] in place using Fisher-Yates
|
|
template<typename T>
|
|
bool Sample(const T &arr[], T &dest[], int k); // Sample k unique items from arr[] without replacement
|
|
template<typename T>
|
|
bool SampleWithReplacement(const T &arr[], T &dest[], int k);
|
|
};
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random 32-bit INT, in range [0, INT_MAX] |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
__forceinline int CRandom::RandomInteger(void)
|
|
{
|
|
return (int)(nextUInt32() >> 1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random 32-bit INT, in the range [0, max) |
|
|
//| i.e., inclusive of 0 and exclusive of max. |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
__forceinline int CRandom::RandomInteger(const int max)
|
|
{
|
|
return (max > 0) ? (int)boundedUInt32(max) : 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random 32-bit INT, in the range [min, max) |
|
|
//| i.e., inclusive of min and exclusive of max. |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
__forceinline int CRandom::RandomInteger(const int min, const int max)
|
|
{
|
|
return (max > min) ? (int)boundedUInt32(max - min) + min : min;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random 64-bit LONG, in range [0, LONG_MAX] |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
__forceinline long CRandom::RandomLong(void)
|
|
{
|
|
return (long)(nextUInt64() >> 1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random 64-bit LONG, in the range [0, max) |
|
|
//| i.e., inclusive of 0 and exclusive of max. |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
__forceinline long CRandom::RandomLong(const long max)
|
|
{
|
|
return (max > 0) ? (long)boundedUInt64(max) : 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random 64-bit LONG, in the range [min, max)|
|
|
//| i.e., inclusive of min and exclusive of max. |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
__forceinline long CRandom::RandomLong(const long min, const long max)
|
|
{
|
|
return (max > min) ? (long)boundedUInt64(max - min) + min : min;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random double in the range [0.0, 1.0) |
|
|
//| i.e., inclusive of 0.0 and exclusive of 1.0. |
|
|
//| Doubles are rounded down to the nearest multiple of 1/2^53. |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
__forceinline double CRandom::RandomDouble()
|
|
{
|
|
return (nextUInt64() >> 11) * (1.0 / (1ull << 53));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random double in the range [0.0, max) |
|
|
//| i.e., inclusive of 0.0 and exclusive of max. |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
double CRandom::RandomDouble(const double max)
|
|
{
|
|
if(max <= 0)
|
|
return 0;
|
|
double r;
|
|
do
|
|
{
|
|
r = RandomDouble() * max;
|
|
}
|
|
while(r >= max); // Check for rounding error
|
|
return r;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random double in the range [min, max) |
|
|
//| i.e., inclusive of min and exclusive of max. |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
double CRandom::RandomDouble(const double min, const double max)
|
|
{
|
|
if(max <= min)
|
|
return min;
|
|
double r;
|
|
double range = max - min;
|
|
do
|
|
{
|
|
r = RandomDouble() * range + min;
|
|
}
|
|
while(r >= max);
|
|
return r;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random double in the range [0.0, 1.0) |
|
|
//| i.e., inclusive of 0.0 and exclusive of 1.0. |
|
|
//| |
|
|
//| https://docs.python.org/3/library/random.html#recipe |
|
|
//| http://mumble.net/~campbell/tmp/random_real.c |
|
|
//| |
|
|
//| Uses an alternative precise sampling method that is capable of |
|
|
//| generating all possible values in the interval [0, 1) that can |
|
|
//| be represented by a double precision float, a feature important |
|
|
//| for scientific simulation and other high-precision applications. |
|
|
//| Note however that this method is slower than RandomDouble(). |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
double CRandom::RandomDoubleHighRes()
|
|
{
|
|
const ulong mantissa = 0x10000000000000 | nextUInt64() >> 12; // 2^52...2^53-1
|
|
double power2 = 0x20000000000000; // 2^53
|
|
uint x;
|
|
while((x = nextUInt32()) == 0)
|
|
{
|
|
power2 *= 0x100000000; // 2^32
|
|
}
|
|
// bitmask for rightmost 1-bit (geometric distribution)
|
|
x &= (0u - x);
|
|
power2 *= x;
|
|
return mantissa / power2;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random double in the range [0.0, max) |
|
|
//| i.e., inclusive of 0.0 and exclusive of max. |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
double CRandom::RandomDoubleHighRes(const double max)
|
|
{
|
|
if(max <= 0)
|
|
return 0;
|
|
double r;
|
|
do
|
|
{
|
|
r = RandomDoubleHighRes() * max;
|
|
}
|
|
while(r >= max);
|
|
return r;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random double in the range [min, max) |
|
|
//| i.e., inclusive of min and exclusive of max. |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
double CRandom::RandomDoubleHighRes(const double min, const double max)
|
|
{
|
|
if(max <= min)
|
|
return min;
|
|
double r;
|
|
const double range = max - min;
|
|
do
|
|
{
|
|
r = RandomDoubleHighRes() * range + min;
|
|
}
|
|
while(r >= max);
|
|
return r;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Uniformly distributed random true/false with equal probability |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
__forceinline bool CRandom::RandomBoolean(void)
|
|
{
|
|
// Use a high bit since the low bits are less random.
|
|
return (nextUInt32() & 0x80000000) != 0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random true/false where 'true' has the probability of prob_true |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
bool CRandom::RandomBoolean(const double prob_true)
|
|
{
|
|
if(prob_true < 0.0)
|
|
return false;
|
|
if(prob_true >= 1.0)
|
|
return true;
|
|
return RandomDouble() < prob_true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random normal sample with specified mean and standard deviation |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
__forceinline double CRandom::RandomNormal(const double mu, const double sigma)
|
|
{
|
|
return mu + sigma * RandomNormal();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random normal sample with mean 0 and standard deviation 1 |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
double CRandom::RandomNormal(void)
|
|
{
|
|
// Use Box-Muller algorithm
|
|
double u1;
|
|
do
|
|
{
|
|
u1 = RandomDouble();
|
|
}
|
|
while(u1 == 0.0);
|
|
const double u2 = RandomDouble();
|
|
const double r = ::MathSqrt(-2.0 * ::MathLog(u1));
|
|
const double theta = 2.0 * M_PI * u2;
|
|
return r * ::MathSin(theta);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Shuffle the order of array elements in-place. |
|
|
//| To shuffle to a new array, use Sample(a, dest, ArraySize(a)). |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
template<typename T>
|
|
void CRandom::Shuffle(T &arr[])
|
|
{
|
|
// Fisher-Yates shuffle
|
|
int size = ArraySize(arr);
|
|
for(int i = 0; i < size - 1; i++)
|
|
{
|
|
// Select random index and swap items [j] and [i]
|
|
const int j = RandomInteger(i, size);
|
|
const T tmp = arr[i];
|
|
arr[i] = arr[j];
|
|
arr[j] = tmp;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sample k unique items from array (without replacement) |
|
|
//| k cannot be larger than the size of arr[]. |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
template<typename T>
|
|
bool CRandom::Sample(const T &arr[], T &dest[], int k)
|
|
{
|
|
int size = ArraySize(arr);
|
|
if(size == 0 || k <= 0)
|
|
return false;
|
|
if(k > size)
|
|
k = size; // cannot sample more than available
|
|
if(ArrayResize(dest, k) != k)
|
|
return false;
|
|
|
|
// Make a temporary index array
|
|
int idx[];
|
|
if(ArrayResize(idx, size) != size)
|
|
return false;
|
|
for(int i = 0; i < size; i++)
|
|
idx[i] = i;
|
|
|
|
// Select the first k random unique indices
|
|
for(int i = 0; i < k; i++)
|
|
{
|
|
// Fisher-Yates shuffle on indices
|
|
const int j = RandomInteger(i, size);
|
|
const int tmp = idx[i];
|
|
idx[i] = idx[j];
|
|
idx[j] = tmp;
|
|
|
|
dest[i] = arr[idx[i]];
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sample k items from array with replacement. |
|
|
//| A sampled item is replaced back into arr[], so that the same |
|
|
//| item can be selected more than once (duplicate selections). |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
template<typename T>
|
|
bool CRandom::SampleWithReplacement(const T &arr[], T &dest[], int k)
|
|
{
|
|
int size = ArraySize(arr);
|
|
if(size == 0 || k <= 0)
|
|
return false;
|
|
if(ArrayResize(dest, k) != k)
|
|
return false;
|
|
|
|
// Pick a random index from arr[]
|
|
for(int i = 0; i < k; i++)
|
|
{
|
|
const int ind = RandomInteger(0, size);
|
|
dest[i] = arr[ind];
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------------+
|
|
//| Uniformly distributed random 64-bit ULONG, r, where 0 <= r < bound |
|
|
//| This implementation uses a fast rejection method (Lemire's fastrange). |
|
|
//| Source: https://github.com/lemire/fastrange |
|
|
//+------------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
ulong CRandom::boundedUInt64(const ulong bound)
|
|
{
|
|
ulong l;
|
|
ulong m = mulhi(nextUInt64(), bound, l);
|
|
if(l < bound)
|
|
{
|
|
ulong t = (0ull - bound) % bound;
|
|
while(l < t)
|
|
{
|
|
m = mulhi(nextUInt64(), bound, l);
|
|
}
|
|
}
|
|
return m;
|
|
}
|
|
//+------------------------------------------------------------------------+
|
|
//| Uniformly distributed random 32-bit UINT, r, where 0 <= r < bound |
|
|
//| 32-bit variant of Lemire's fastrange. |
|
|
//+------------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
uint CRandom::boundedUInt32(const uint bound)
|
|
{
|
|
ulong m = nextUInt32() * (ulong)bound;
|
|
uint l = (uint) m;
|
|
if(l < bound)
|
|
{
|
|
uint t = (0u - bound) % bound;
|
|
while(l < t)
|
|
{
|
|
m = nextUInt32() * (ulong)bound;
|
|
l = (uint) m;
|
|
}
|
|
}
|
|
return (uint)(m >> 32);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Emulate 64x64 bit multiplication to 128 bit result. |
|
|
//| Returns high part of product, and low part is set in `lowPart`. |
|
|
//| github.com/numpy/numpy/blob/main/numpy/random/src/pcg64/pcg64.h |
|
|
//+------------------------------------------------------------------+
|
|
template <typename TBackend>
|
|
ulong CRandom::mulhi(const ulong x, const ulong y, ulong &lowPart)
|
|
{
|
|
lowPart = x * y; // Lower 64 bits are straightforward clock-arithmetic.
|
|
|
|
ulong x0 = x & 0xFFFFFFFF;
|
|
ulong x1 = x >> 32;
|
|
ulong y0 = y & 0xFFFFFFFF;
|
|
ulong y1 = y >> 32;
|
|
ulong w0 = x0 * y0;
|
|
ulong t = x1 * y0 + (w0 >> 32);
|
|
ulong w1 = t & 0xFFFFFFFF;
|
|
ulong w2 = t >> 32;
|
|
w1 += x0 * y1;
|
|
ulong hi = x1 * y1 + w2 + (w1 >> 32);
|
|
|
|
return hi;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
}
|
|
#endif // PRNGBYLEO_SRC_MAIN_MQH
|
|
//+------------------------------------------------------------------+
|