PNRGByLeo/Src/Main.mqh
Nique_372 d70c88b6dd
2026-09-01 12:16:49 -05:00

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
//+------------------------------------------------------------------+