/** Returns a random number from the distribution. */ protected int hmdu(int N, int M, int n, RandomEngine randomGenerator) { int I, K; double p, nu, c, d, U; if (N != N_last || M != M_last || n != n_last) { // set-up */ N_last = N; M_last = M; n_last = n; Mp = (double) (M + 1); np = (double) (n + 1); N_Mn = N - M - n; p = Mp / (N + 2.0); nu = np * p; /* mode, real */ if ((m = (int) nu) == nu && p == 0.5) { /* mode, integer */ mp = m--; } else { mp = m + 1; /* mp = m + 1 */ } /* mode probability, using the external function flogfak(k) = ln(k!) */ fm = Math.exp( Arithmetic.logFactorial(N - M) - Arithmetic.logFactorial(N_Mn + m) - Arithmetic.logFactorial(n - m) + Arithmetic.logFactorial(M) - Arithmetic.logFactorial(M - m) - Arithmetic.logFactorial(m) - Arithmetic.logFactorial(N) + Arithmetic.logFactorial(N - n) + Arithmetic.logFactorial(n)); /* safety bound - guarantees at least 17 significant decimal digits */ /* b = min(n, (long int)(nu + k*c')) */ b = (int) (nu + 11.0 * Math.sqrt(nu * (1.0 - p) * (1.0 - n / (double) N) + 1.0)); if (b > n) b = n; } for (; ; ) { if ((U = randomGenerator.raw() - fm) <= 0.0) return (m); c = d = fm; /* down- and upward search from the mode */ for (I = 1; I <= m; I++) { K = mp - I; /* downward search */ c *= (double) K / (np - K) * ((double) (N_Mn + K) / (Mp - K)); if ((U -= c) <= 0.0) return (K - 1); K = m + I; /* upward search */ d *= (np - K) / (double) K * ((Mp - K) / (double) (N_Mn + K)); if ((U -= d) <= 0.0) return (K); } /* upward search from K = 2m + 1 to K = b */ for (K = mp + m; K <= b; K++) { d *= (np - K) / (double) K * ((Mp - K) / (double) (N_Mn + K)); if ((U -= d) <= 0.0) return (K); } } }
/** Returns a random number from the distribution. */ protected int hprs(int N, int M, int n, RandomEngine randomGenerator) { int Dk, X, V; double Mp, np, p, nu, U, Y, W; /* (X, Y) <-> (V, W) */ if (N != N_last || M != M_last || n != n_last) { /* set-up */ N_last = N; M_last = M; n_last = n; Mp = (double) (M + 1); np = (double) (n + 1); N_Mn = N - M - n; p = Mp / (N + 2.0); nu = np * p; // main parameters // approximate deviation of reflection points k2, k4 from nu - 1/2 U = Math.sqrt(nu * (1.0 - p) * (1.0 - (n + 2.0) / (N + 3.0)) + 0.25); // mode m, reflection points k2 and k4, and points k1 and k5, which // delimit the centre region of h(x) // k2 = ceil (nu - 1/2 - U), k1 = 2*k2 - (m - 1 + delta_ml) // k4 = floor(nu - 1/2 + U), k5 = 2*k4 - (m + 1 - delta_mr) m = (int) nu; k2 = (int) Math.ceil(nu - 0.5 - U); if (k2 >= m) k2 = m - 1; k4 = (int) (nu - 0.5 + U); k1 = k2 + k2 - m + 1; // delta_ml = 0 k5 = k4 + k4 - m; // delta_mr = 1 // range width of the critical left and right centre region dl = (double) (k2 - k1); dr = (double) (k5 - k4); // recurrence constants r(k) = p(k)/p(k-1) at k = k1, k2, k4+1, k5+1 r1 = (np / (double) k1 - 1.0) * (Mp - k1) / (double) (N_Mn + k1); r2 = (np / (double) k2 - 1.0) * (Mp - k2) / (double) (N_Mn + k2); r4 = (np / (double) (k4 + 1) - 1.0) * (M - k4) / (double) (N_Mn + k4 + 1); r5 = (np / (double) (k5 + 1) - 1.0) * (M - k5) / (double) (N_Mn + k5 + 1); // reciprocal values of the scale parameters of expon. tail envelopes ll = Math.log(r1); // expon. tail left // lr = -Math.log(r5); // expon. tail right // // hypergeom. constant, necessary for computing function values f(k) c_pm = fc_lnpk(m, N_Mn, M, n); // function values f(k) = p(k)/p(m) at k = k2, k4, k1, k5 f2 = Math.exp(c_pm - fc_lnpk(k2, N_Mn, M, n)); f4 = Math.exp(c_pm - fc_lnpk(k4, N_Mn, M, n)); f1 = Math.exp(c_pm - fc_lnpk(k1, N_Mn, M, n)); f5 = Math.exp(c_pm - fc_lnpk(k5, N_Mn, M, n)); // area of the two centre and the two exponential tail regions // area of the two immediate acceptance regions between k2, k4 p1 = f2 * (dl + 1.0); // immed. left p2 = f2 * dl + p1; // centre left p3 = f4 * (dr + 1.0) + p2; // immed. right p4 = f4 * dr + p3; // centre right p5 = f1 / ll + p4; // expon. tail left p6 = f5 / lr + p5; // expon. tail right } for (; ; ) { // generate uniform number U -- U(0, p6) // case distinction corresponding to U if ((U = randomGenerator.raw() * p6) < p2) { // centre left // immediate acceptance region R2 = [k2, m) *[0, f2), X = k2, ... m -1 if ((W = U - p1) < 0.0) return (k2 + (int) (U / f2)); // immediate acceptance region R1 = [k1, k2)*[0, f1), X = k1, ... k2-1 if ((Y = W / dl) < f1) return (k1 + (int) (W / f1)); // computation of candidate X < k2, and its counterpart V > k2 // either squeeze-acceptance of X or acceptance-rejection of V Dk = (int) (dl * randomGenerator.raw()) + 1; if (Y <= f2 - Dk * (f2 - f2 / r2)) { // quick accept of return (k2 - Dk); // X = k2 - Dk } if ((W = f2 + f2 - Y) < 1.0) { // quick reject of V V = k2 + Dk; if (W <= f2 + Dk * (1.0 - f2) / (dl + 1.0)) { // quick accept of return (V); // V = k2 + Dk } if (Math.log(W) <= c_pm - fc_lnpk(V, N_Mn, M, n)) { return (V); // final accept of V } } X = k2 - Dk; } else if (U < p4) { // centre right // immediate acceptance region R3 = [m, k4+1)*[0, f4), X = m, ... k4 if ((W = U - p3) < 0.0) return (k4 - (int) ((U - p2) / f4)); // immediate acceptance region R4 = [k4+1, k5+1)*[0, f5) if ((Y = W / dr) < f5) return (k5 - (int) (W / f5)); // computation of candidate X > k4, and its counterpart V < k4 // either squeeze-acceptance of X or acceptance-rejection of V Dk = (int) (dr * randomGenerator.raw()) + 1; if (Y <= f4 - Dk * (f4 - f4 * r4)) { // quick accept of return (k4 + Dk); // X = k4 + Dk } if ((W = f4 + f4 - Y) < 1.0) { // quick reject of V V = k4 - Dk; if (W <= f4 + Dk * (1.0 - f4) / dr) { // quick accept of return (V); // V = k4 - Dk } if (Math.log(W) <= c_pm - fc_lnpk(V, N_Mn, M, n)) { return (V); // final accept of V } } X = k4 + Dk; } else { Y = randomGenerator.raw(); if (U < p5) { // expon. tail left Dk = (int) (1.0 - Math.log(Y) / ll); if ((X = k1 - Dk) < 0) continue; // 0 <= X <= k1 - 1 Y *= (U - p4) * ll; // Y -- U(0, h(x)) if (Y <= f1 - Dk * (f1 - f1 / r1)) { return (X); // quick accept of X } } else { // expon. tail right Dk = (int) (1.0 - Math.log(Y) / lr); if ((X = k5 + Dk) > n) continue; // k5 + 1 <= X <= n Y *= (U - p5) * lr; // Y -- U(0, h(x)) / if (Y <= f5 - Dk * (f5 - f5 * r5)) { return (X); // quick accept of X } } } // acceptance-rejection test of candidate X from the original area // test, whether Y <= f(X), with Y = U*h(x) and U -- U(0, 1) // log f(X) = log( m! (M - m)! (n - m)! (N - M - n + m)! ) // - log( X! (M - X)! (n - X)! (N - M - n + X)! ) // by using an external function for log k! if (Math.log(Y) <= c_pm - fc_lnpk(X, N_Mn, M, n)) return (X); } }