using System; namespace Orthogonal.Common { #region START_ECUYER /// /// A class of highly effective and reliable random number generators invented by Pierre L'Ecuyer. /// public sealed class RandEcuyer { public RandEcuyer() { iv = new long[32]; AM = 1.0 / 2147483563.0; NDIV = 1.0 + 2147483562.0 / 32.0; SeedRandom(); } /// /// Uses the standard .NET Random class to reseed all generators with random values. /// public void SeedRandom() { Random rand = new Random(); e1m1 = (int)(rand.NextDouble() * E1Max1); e1m2 = (int)(rand.NextDouble() * E1Max2); e2m1 = (int)(rand.NextDouble() * IM1); e2m2 = (int)(rand.NextDouble() * IM2); s10 = (long)(rand.NextDouble() * E3Max1); s11 = (long)(rand.NextDouble() * E3Max1); s12 = (long)(rand.NextDouble() * E3Max1); s20 = (long)(rand.NextDouble() * E3Max2); s21 = (long)(rand.NextDouble() * E3Max2); s22 = (long)(rand.NextDouble() * E3Max2); } #region Ecuyer1 private int e1m1; private int e1m2; private const int E1Max1 = 2147483563; private const int E1Max2 = 2147483399; /// /// Pierre L'Ecuyer's famous 1988 random number generator that combines two /// MLGCs to create a period of around 10^18. This is the original algorithm /// without any enhancements or shuffles. /// /// A random number in the range 0.0 to 1.0 exclusive of endpoints. public double Ecuyer1() { MODMULT(53668, 40014, 12211, E1Max1, ref e1m1); MODMULT(52774, 40692, 3791, E1Max2, ref e1m2); int z = e1m1 - e1m2; if (z < 1) z += (E1Max1 - 1); //2147483562; return z * 4.65661305956e-10; } /// /// A helper method that replaces the original C++ macro. /// private void MODMULT(int a, int b, int c, int m, ref int s) { int q = s / a; s = b * (s - a * q) - c * q; if (s < 0) s += m; } #endregion #region Ecuyer2 private const long IM1 = 2147483563; private const long IM2 = 2147483399; private long[] iv; private long iy; private long e2m1; private long e2m2; private double AM; private double NDIV; private long s10; private long s11; private long s12; private long s20; private long s21; private long s22; /// /// Pierre L'ecuyer's 1988 random number generator that combines two MLCGs, with the /// additional safeguard of a Bayes-Durham shuffle to reduce correlation. /// /// A random number in the range 0.0 to 1.0 exclusive of endpoints. public double Ecuyer2() { const int NTAB = 32; const long IQ1 = 53668; const long IQ2 = 52774; const long IR1 = 12211; const long IR2 = 3791; const long IA1 = 40014; const long IA2 = 40692; const long IMM1 = 2147483562; const double RNMX = 1.0 - 1.2e-7; int j; long k; double temp; if (e2m1 <= 0) { if (-(e2m1) < 1) e2m1 = 1; else e2m1 = -(e2m1); e2m2 = (e2m1); for (j = NTAB + 7; j >= 0; j--) { k = (e2m1) / IQ1; e2m1 = IA1 * (e2m1 - k * IQ1) - k * IR1; if (e2m1 < 0) e2m1 += IM1; if (j < NTAB) iv[j] = e2m1; } iy = iv[0]; } k = (e2m1) / IQ1; e2m1 = IA1 * (e2m1 - k * IQ1) - k * IR1; if (e2m1 < 0) e2m1 += IM1; k = e2m2 / IQ2; e2m2 = IA2 * (e2m2 - k * IQ2) - k * IR2; if (e2m2 < 0) e2m2 += IM2; j = Convert.ToInt32(iy / NDIV) % NTAB; iy = iv[j] - e2m2; iv[j] = e2m1; if (iy < 1) iy += IMM1; if ((temp = AM * iy) > RNMX) return RNMX; else return temp; } #endregion #region Encuyer3 private const long E3Max1 = 9223372036854769163L; private const long E3Max2 = 9223372036854754679L; /// /// Pierre L'Ecuyer's Combined Multiple Recursive Generator with 2 components of order 3 (see /// page 14, Good Parameters and Implementations for Combined Multiple Recursive Random /// Number Generators, May 4, 1998). The period length is (m1^3-1)(m2^3-1)/2, which is /// around 10^113. The implementation assumes that all integers from -m1 to m1 can /// be represented exactly, which is the case with C#'s 64-bit longs. This generator not /// only has an astronomically long period, it has excellent structural properties according /// to the spectral test up to dimension 30. /// /// A random number in the range 0-1. public double Ecuyer3() { const double norm = 1.0842021724855052e-19; const long a13n = 3182104042L; const long a23n = 6199136374L; const long r23 = 985240079L; const long q23 = 1487847900L; const long r21 = 143639429L; const long q21 = 293855150L; const long a21 = 31387477935L; const long r13 = 394451401L; const long q13 = 2898513661L; const long r12 = 251304723L; const long q12 = 5256471877L; const long a12 = 1754669720L; long h, p12, p13, p21, p23; /* Component 1 */ h = s10 / q13; p13 = a13n * (s10 - h * q13) - h * r13; h = s11 / q12; p12 = a12 * (s11 - h * q12) - h * r12; if (p13 < 0) p13 += E3Max1; if (p12 < 0) p12 += E3Max1 - p13; else p12 -= p13; if (p12 < 0) p12 += E3Max1; s10 = s11; s11 = s12; s12 = p12; /* Component 2 */ h = s20 / q23; p23 = a23n * (s20 - h * q23) - h * r23; h = s22 / q21; p21 = a21 * (s22 - h * q21) - h * r21; if (p23 < 0) p23 += E3Max2; if (p21 < 0) p21 += E3Max2 - p23; else p21 -= p23; if (p21 < 0) p21 += E3Max2; s20 = s21; s21 = s22; s22 = p21; /* Combination */ if (p12 > p21) return norm * (p12 - p21); else return norm * (p12 - p21 + E3Max1); } #endregion } #endregion END_ECUYER }