using System; namespace Orthogonal.Common { /// /// A class of highly effective and reliable random number generators invented by Pierre L'Ecuyer. /// public sealed class RandEcuyer { private int ecs1 = -1; private int ecs2 = -1; private long s10 = -1; private long s11 = -1; private long s12 = -1; private long s20 = -1; private long s21 = -1; private long s22 = -1; private const double norm = 1.0842021724855052e-19; private const int m21 = 2147483563; private const int m22 = 2147483399; private const long m1 = 9223372036854769163L; private const long m2 = 9223372036854754679L; private const long a12 = 1754669720L; private const long q12 = 5256471877L; private const long r12 = 251304723L; private const long a13n = 3182104042L; private const long q13 = 2898513661L; private const long r13 = 394451401L; private const long a21 = 31387477935L; private const long q21 = 293855150L; private const long r21 = 143639429L; private const long a23n = 6199136374L; private const long q23 = 1487847900L; private const long r23 = 985240079L; public RandEcuyer() { Random rand = new Random(); // Seed the generators using the standard Rand class. ecs1 = (int)(rand.NextDouble() * m21); ecs2 = (int)(rand.NextDouble() * m22); s10 = (long)(rand.NextDouble() * m1); s11 = (long)(rand.NextDouble() * m1); s12 = (long)(rand.NextDouble() * m1); s20 = (long)(rand.NextDouble() * m2); s11 = (long)(rand.NextDouble() * m2); s22 = (long)(rand.NextDouble() * m2); } /// /// Pierre L'Ecuyer's famous 1988 random number generator that combines two /// MLGCs to create a period of around 10^18. Numerical Recipies in C improves /// this raw algorithm by using a Bayes-Durham shuffle to remove any low order /// bit correlations. /// /// A random number in the range 0-1. public double Uniform2() { MODMULT(53668, 40014, 12211, m21, ref ecs1); MODMULT(52774, 40692, 3791, m22, ref ecs2); int z = ecs1 - ecs2; if (z < 1) z += 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; } /// /// 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 Uniform() { 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 += m1; if (p12 < 0) p12 += m1 - p13; else p12 -= p13; if (p12 < 0) p12 += m1; 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 += m2; if (p21 < 0) p21 += m2 - p23; else p21 -= p23; if (p21 < 0) p21 += m2; s20 = s21; s21 = s22; s22 = p21; /* Combination */ if (p12 > p21) return norm * (p12 - p21); else return norm * (p12 - p21 + m1); } } }