public static double NormalGreaterThan(double lowerBound, IPolyrand random = null) { if (lowerBound < 1) { // simple rejection while (true) { double x = Rand.Normal(random); if (x >= lowerBound) { return(x); } } } else { // lowerBound >= 1 if (false) { // Devroye's (Ch.9) rejection sampler with x ~ Exp(lowerBound) // requires lowerBound > 0 double c = 2 * lowerBound * lowerBound; while (true) { // note it is possible to generate two exponential r.v.s with a single logarithm (Devroye Ch.9 sec.2.1) double E = -System.Math.Log(NextDouble(random)); double E2 = -System.Math.Log(NextDouble(random)); if (E * E <= c * E2) { return(lowerBound + E / lowerBound); } } } else { // Marsaglia's (1964) rejection sampler (Devroye Ch.9) // Reference: "Non-Uniform Random Variate Generation" by Luc Devroye (1986) double c = lowerBound * lowerBound * 0.5; while (true) { double U = NextDouble(random); double V = NextDouble(random); double x = c - System.Math.Log(U); if (V * V * x <= c) { return(System.Math.Sqrt(2 * x)); } } } throw new Exception("failed to sample"); } }
public static int Poisson(double mean, IPolyrand random = null) { // TODO: There are more efficient samplers if (mean < 0) { throw new ArgumentException("mean < 0"); } else if (mean == 0.0) { return(0); } else if (mean < 10) { double L = System.Math.Exp(-mean); double p = 1.0; int k = 0; do { k++; p *= NextDouble(random); } while (p > L); return(k - 1); } else { // mean >= 10 // Devroye ch10.3, with corrections // Reference: "Non-Uniform Random Variate Generation" by Luc Devroye (1986) double mu = System.Math.Floor(mean); double muLogFact = MMath.GammaLn(mu + 1); double logMeanMu = System.Math.Log(mean / mu); double delta = System.Math.Max(6, System.Math.Min(mu, System.Math.Sqrt(2 * mu * System.Math.Log(128 * mu / System.Math.PI)))); double c1 = System.Math.Sqrt(System.Math.PI * mu / 2); double c2 = c1 + System.Math.Sqrt(System.Math.PI * (mu + delta / 2) / 2) * System.Math.Exp(1 / (2 * mu + delta)); double c3 = c2 + 2; double c4 = c3 + System.Math.Exp(1.0 / 78); double c = c4 + 2 / delta * (2 * mu + delta) * System.Math.Exp(-delta / (2 * mu + delta) * (1 + delta / 2)); while (true) { double u = NextDouble(random) * c; double x, w; if (u <= c1) { double n = Rand.Normal(random); double y = -System.Math.Abs(n) * System.Math.Sqrt(mu) - 1; x = System.Math.Floor(y); if (x < -mu) { continue; } w = -n * n / 2; } else if (u <= c2) { double n = Rand.Normal(random); double y = 1 + System.Math.Abs(n) * System.Math.Sqrt(mu + delta / 2); x = System.Math.Ceiling(y); if (x > delta) { continue; } w = (2 - y) * y / (2 * mu + delta); } else if (u <= c3) { x = 0; w = 0; } else if (u <= c4) { x = 1; w = 0; } else { double v = -System.Math.Log(NextDouble(random)); double y = delta + v * 2 / delta * (2 * mu + delta); x = System.Math.Ceiling(y); w = -delta / (2 * mu + delta) * (1 + y / 2); } double e = -System.Math.Log(NextDouble(random)); w -= e + x * logMeanMu; double qx = x * System.Math.Log(mu) - MMath.GammaLn(mu + x + 1) + muLogFact; if (w <= qx) { return((int)System.Math.Round(x + mu)); } } } }
/// <summary> /// Sample from a Gaussian(0,1) truncated at the given upper and lower bounds /// </summary> /// <param name="lowerBound">Can be -Infinity.</param> /// <param name="upperBound">Must be >= <paramref name="lowerBound"/>. Can be Infinity.</param> /// <returns>A real number >= <paramref name="lowerBound"/> and < <paramref name="upperBound"/></returns> public static double NormalBetween(double lowerBound, double upperBound, IPolyrand random = null) { if (double.IsNaN(lowerBound)) { throw new ArgumentException("lowerBound is NaN"); } if (double.IsNaN(upperBound)) { throw new ArgumentException("upperBound is NaN"); } double delta = upperBound - lowerBound; if (delta == 0) { return(lowerBound); } if (delta < 0) { throw new ArgumentException("upperBound (" + upperBound + ") < lowerBound (" + lowerBound + ")"); } // Switch between the following 3 options: // 1. Gaussian rejection, with acceptance rate Z = NormalCdf(upperBound) - NormalCdf(lowerBound) // 2. Uniform rejection, with acceptance rate sqrt(2*pi)*Z/delta if the interval contains 0 // 3. Truncated exponential rejection, with acceptance rate // = sqrt(2*pi)*Z*lambda*exp(-lambda^2/2)/(exp(-lambda*lowerBound)-exp(-lambda*upperBound)) // = sqrt(2*pi)*Z*lowerBound*exp(lowerBound^2/2)/(1-exp(-lowerBound*(upperBound-lowerBound))) // (3) has the highest acceptance rate under the following conditions: // lowerBound > 0.5 or (lowerBound > 0 and delta < 2.5) // (2) has the highest acceptance rate if the interval contains 0 and delta < sqrt(2*pi) // (1) has the highest acceptance rate otherwise if (lowerBound > 0.5 || (lowerBound > 0 && delta < 2.5)) { // Rejection sampler using truncated exponential proposal double lambda = lowerBound; double s = MMath.ExpMinus1(-lambda * delta); double c = 2 * lambda * lambda; while (true) { double x = -MMath.Log1Plus(s * NextDouble(random)); double u = -System.Math.Log(NextDouble(random)); if (c * u > x * x) { return(x / lambda + lowerBound); } } throw new Exception("failed to sample"); } else if (upperBound < -0.5 || (upperBound < 0 && delta < 2.5)) { return(-NormalBetween(-upperBound, -lowerBound)); } else if (lowerBound <= 0 && upperBound >= 0 && delta < MMath.Sqrt2PI) { // Uniform rejection while (true) { double x = NextDouble(random) * delta + lowerBound; double u = -System.Math.Log(NextDouble(random)); if (2 * u > x * x) { return(x); } } } else { // Gaussian rejection while (true) { double x = Rand.Normal(random); if (x >= lowerBound && x < upperBound) { return(x); } } } }