/// <summary> /// Computes <c>GammaLn(x) - (x-0.5)*log(x) + x - 0.5*log(2*pi)</c> for x >= 10 /// </summary> /// <param name="x">A real number >= 10</param> /// <returns></returns> private static double GammaLnSeries(double x) { // GammaLnSeries(10) = 0.008330563433362871 if (x < 10) { return(MMath.GammaLn(x) - (x - 0.5) * Math.Log(x) + x - LnSqrt2PI); } else { // the series is: sum_{i=1}^inf B_{2i} / (2i*(2i-1)*x^(2i-1)) double sum = 0; double term = 1.0 / x; double delta = term * term; for (int i = 0; i < c_gammaln_series.Length; i++) { sum += c_gammaln_series[i] * term; term *= delta; } return(sum); } }
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)); } } } }