private static double HypergeometricHYP(PRNG prng, double good, double bad, long sample) { var d1 = bad + good - sample; var d2 = Convert.ToDouble(Math.Min(bad, good)); var Y = d2; var K = sample; while (Y > 0.0) { var U = prng.Draw(); Y -= Math.Floor(U + Y / (d1 + K)); K -= 1; if (K == 0) { break; } } var Z = Math.Floor(d2 - Y); if (good > bad) { Z = sample - Z; } return(Z); }
private static double HypergeometricHRUA(PRNG prng, double good, double bad, long sample) { var D1 = 1.7155277699214135; var D2 = 0.8989161620588988; var minGoodBad = Math.Min(good, bad); var popSize = good + bad; var maxGoodBad = Math.Max(good, bad); var m = Math.Min(sample, popSize - sample); var d4 = minGoodBad / popSize; var d5 = 1.0 - d4; var d6 = m * d4 + 0.5; var d7 = Math.Sqrt((popSize - m) * sample * d4 * d5 / (popSize - 1) + 0.5); var d8 = D1 * d7 + D2; var d9 = Math.Floor(((double)m + 1) * (minGoodBad + 1) / (popSize + 2)); var d10 = LogGamma(d9 + 1) + LogGamma(minGoodBad - d9 + 1) + LogGamma(m - d9 + 1) + LogGamma(maxGoodBad - m + d9 + 1); var d11 = Math.Min(Math.Min(m, minGoodBad) + 1.0, Math.Floor(d6 + 16 * d7)); double Z; while (true) { var X = prng.Draw(); var Y = prng.Draw(); var W = d6 + d8 * (Y - 0.5) / X; if (W < 0.0 || W >= d11) { continue; } Z = Math.Floor(W); var T = d10 - (LogGamma(Z + 1) + LogGamma(minGoodBad - Z + 1) + LogGamma(m - Z + 1) + LogGamma(maxGoodBad - m + Z + 1)); if ((X * (4.0 - X) - 3.0) <= T) { break; } if (X * (X - T) >= 1) { continue; } if (2.0 * Math.Log(X) <= T) { break; } } if (good > bad) { Z = m - Z; } if (m < sample) { Z = good - Z; } return(Z); }
/* * Random variates from the hypergeometric distribution. * * Returns the number of white balls drawn when kk balls * are drawn at random from an urn containing nn1 white * and nn2 black balls. */ private static double RHyper(long kk, double nn1, double nn2, CoinFlipper coins) { var prng = new PRNG(coins); if (kk > 10) { return(HypergeometricHRUA(prng, nn1, nn2, kk)); } else { return(HypergeometricHYP(prng, nn1, nn2, kk)); } }