public static double Sampler(PZRandomUnivariate random) { double r = random.Sample(); return r; }
/// <summary> /// New version based on Marsaglia and Tsang, "A Simple Method for /// generating gamma variables", ACM Transactions on Mathematical /// Software, Vol 26, No 3 (2000), p363-372. /// Implemented by [email protected], minor modifications for GSL /// by Brian Gough /// </summary> /// <returns></returns> public static double SampleMarsagliaAndTsang(PZRandomUnivariate random, double alpha, double beta) { if (alpha < 1) { double u = UniformDistribution.Sampler(random); return SampleMarsagliaAndTsang(random, 1.0 + alpha, beta) * System.Math.Pow(u, 1.0 / alpha); } { double x, v, u; double d = alpha - 1.0 / 3.0; double c = (1.0 / 3.0) / System.Math.Sqrt(d); while (true) { do { x = NormalDistribution.SamplerPolar(random, 0.0, 1.0); v = 1.0 + c * x; } while (v <= 0); v = v * v * v; u = random.Sample(); if (u < 1 - 0.0331 * x * x * x * x) break; if (System.Math.Log(u) < 0.5 * x * x + d * (1 - v + System.Math.Log(v))) break; } return beta * d * v; } }
/// <summary> /// http://en.wikipedia.org/wiki/Gamma_distribution /// Knuth /// </summary> /// <returns></returns> public static double SampleKnuth(PZRandomUnivariate random, double k, double theta) { double integralk = System.Math.Floor(k); double fractionalk = k - integralk; double xi = 0.0; if (fractionalk > 0) { // Gamma(delta, 1), delta = fractional of k // step 1 int m = 1; double delta = fractionalk; double v0 = System.Math.E / (System.Math.E + delta); double xim = 0.0; double etam = 0.0; while (true) { // step 2 double v2mM1 = random.Sample(); double v2m = random.Sample(); // step 3 if (v2mM1 <= v0) { // step 4 xim = System.Math.Pow(v2mM1, 1 / delta); etam = v2m * System.Math.Pow(xim, delta - 1.0); } else { // ste 5 xim = 1 - System.Math.Log(v2mM1); etam = v2m * System.Math.Exp(-1.0 * xim); } // step 6 if (etam > System.Math.Pow(xim, delta - 1) * System.Math.Exp(-1.0 * xim)) m++; // return step 2 else { // step 7 xi = xim; // xi ~ Gamma(delta, 1) break; } } } // Gamma(n, 1), n = integral of k double sum = 0.0; double ui; for (int i = 0; i < integralk; i++) { ui = random.Sample(); sum += System.Math.Log(ui); } return theta * (xi - sum); }
/* Ratio method (Kinderman-Monahan); see Knuth v2, 3rd ed, p130. * K+M, ACM Trans Math Software 3 (1977) 257-260. * * [Added by Charles Karney] This is an implementation of Leva's * modifications to the original K+M method; see: * J. L. Leva, ACM Trans Math Software 18 (1992) 449-453 and 454-455. */ public static double SamplerRatio(PZRandomUnivariate random, double mu, double sigma) { double u, v, x, y, Q; double s = 0.449871; /* Constants from Leva */ double t = -0.386595; double a = 0.19600; double b = 0.25472; double r1 = 0.27597; double r2 = 0.27846; do /* This loop is executed 1.369 times on average */ { /* Generate a point P = (u, v) uniform in a rectangle enclosing the K+M region v^2 <= - 4 u^2 log(u). */ /* u in (0, 1] to avoid singularity at u = 0 */ u = 1 - random.Sample(); /* v is in the asymmetric interval [-0.5, 0.5). However v = -0.5 is rejected in the last part of the while clause. The resulting normal deviate is strictly symmetric about 0 (provided that v is symmetric once v = -0.5 is excluded). */ v = random.Sample() - 0.5; /* Constant 1.7156 > sqrt(8/e) (for accuracy); but not by too much (for efficiency). */ v *= 1.7156; /* Compute Leva's quadratic form Q */ x = u - s; y = System.Math.Abs(v) - t; Q = x * x + y * (a * y - b * x); /* Accept P if Q < r1 (Leva) */ /* Reject P if Q > r2 (Leva) */ /* Accept if v^2 <= -4 u^2 log(u) (K+M) */ /* This final test is executed 0.012 times on average. */ } while (Q >= r1 && (Q > r2 || v * v > -4 * u * u * System.Math.Log(u))); double r = mu + sigma * (v / u); return r; /* Return slope */ }
/* Of the two methods provided below, I think the Polar method is more * efficient, but only when you are actually producing two random * deviates. We don't produce two, because then we'd have to save one * in a static variable for the next call, and that would screws up * re-entrant or threaded code, so we only produce one. This makes * the Ratio method suddenly more appealing. * * [Added by Charles Karney] We use Leva's implementation of the Ratio * method which avoids calling log() nearly all the time and makes the * Ratio method faster than the Polar method (when it produces just one * result per call). Timing per call (gcc -O2 on 866MHz Pentium, * average over 10^8 calls) * * Polar method: 660 ns * Ratio method: 368 ns * */ /* Polar (Box-Mueller) method; See Knuth v2, 3rd ed, p122 */ public static double SamplerPolar(PZRandomUnivariate random, double mu, double sigma) { double x, y, r2; do { /* choose x,y in uniform square (-1,-1) to (+1,+1) */ x = -1 + 2 * random.Sample(); y = -1 + 2 * random.Sample(); /* see if it is in the unit circle */ r2 = x * x + y * y; } while (r2 > 1.0 || r2 == 0); /* Box-Muller transform */ double r = mu + sigma * y * System.Math.Sqrt(-2.0 * System.Math.Log(r2) / r2); return r; }
/* The exponential distribution has the form p(x) dx = exp(-x/mu) dx/mu for x = 0 ... +infty */ public static double Sampler(PZRandomUnivariate random, double mu) { double e = random.Sample(); double r = -mu * System.Math.Log(e); return r; }