int32 StochasticLib1::BinomialRatioOfUniforms (int32 n, double p) { /* Subfunction for Binomial distribution. Assumes p < 0.5. Uses the Ratio-of-Uniforms rejection method (BRUAt). The computation time hardly depends on the parameters, except that it matters a lot whether parameters are within the range where the LnFac function is tabulated. Reference: E. Stadlober: "The ratio of uniforms approach for generating discrete random variates". Journal of Computational and Applied Mathematics, vol. 31, no. 1, 1990, pp. 181-189. */ static int32 b_n_last = -1; // last n static double b_p_last = -1.; // last p static int32 b_mode; // mode static int32 b_bound; // upper bound static double b_a; // hat center static double b_h; // hat width static double b_g; // value at mode static double b_r1; // ln(p/(1-p)) double u; // uniform random double q1; // 1-p double np; // n*p double var; // variance double lf; // ln(f(x)) double x; // real sample int32 k; // integer sample if(b_n_last != n || b_p_last != p) { // Set_up b_n_last = n; b_p_last = p; q1 = 1.0 - p; np = n * p; b_mode = (int32)(np + p); // mode b_a = np + 0.5; // hat center b_r1 = log(p / q1); b_g = LnFac(b_mode) + LnFac(n-b_mode); var = np * q1; // variance b_h = sqrt(SHAT1 * (var+0.5)) + SHAT2; // hat width b_bound = (int32)(b_a + 6.0 * b_h); // safety-bound if (b_bound > n) b_bound = n;} // safety-bound while (1) { // rejection loop u = Random(); if (u == 0) continue; // avoid division by 0 x = b_a + b_h * (Random() - 0.5) / u; if (x < 0. || x > b_bound) continue; // reject, avoid overflow k = (int32)x; // truncate lf = (k-b_mode)*b_r1+b_g-LnFac(k)-LnFac(n-k);// ln(f(k)) if (u * (4.0 - u) - 3.0 <= lf) break; // lower squeeze accept if (u * (u - lf) > 1.0) continue; // upper squeeze reject if (2.0 * log(u) <= lf) break;} // final acceptance return k;}
int32 StochasticLib1::PoissonRatioUniforms(double L) { /* This subfunction generates a random variate with the poisson distribution using the ratio-of-uniforms rejection method (PRUAt). Execution time does not depend on L, except that it matters whether L is within the range where ln(n!) is tabulated. Reference: E. Stadlober: "The ratio of uniforms approach for generating discrete random variates". Journal of Computational and Applied Mathematics, vol. 31, no. 1, 1990, pp. 181-189. */ static double p_L_last = -1.0; // previous L static double p_a; // hat center static double p_h; // hat width static double p_g; // ln(L) static double p_q; // value at mode static int32 p_bound; // upper bound int32 mode; // mode double u; // uniform random double lf; // ln(f(x)) double x; // real sample int32 k; // integer sample if (p_L_last != L) { p_L_last = L; // Set-up p_a = L + 0.5; // hat center mode = (int32)L; // mode p_g = log(L); p_q = mode * p_g - LnFac(mode); // value at mode p_h = sqrt(SHAT1 * (L+0.5)) + SHAT2; // hat width p_bound = (int32)(p_a + 6.0 * p_h);} // safety-bound while(1) { u = Random(); if (u == 0) continue; // avoid division by 0 x = p_a + p_h * (Random() - 0.5) / u; if (x < 0 || x >= p_bound) continue; // reject if outside valid range k = (int32)(x); lf = k * p_g - LnFac(k) - p_q; if (lf >= u * (4.0 - u) - 3.0) break; // quick acceptance if (u * (u - lf) > 1.0) continue; // quick rejection if (2.0 * log(u) <= lf) break;} // final acceptance return(k);}
double StochasticLib1::fc_lnpk(int32_t k, int32_t L, int32_t m, int32_t n) { // subfunction used by hypergeometric and Fisher's noncentral hypergeometric distribution return(LnFac(k) + LnFac(m - k) + LnFac(n - k) + LnFac(L + k)); }
int32_t StochasticLib1::HypInversionMod (int32_t n, int32_t m, int32_t N) { /* Subfunction for Hypergeometric distribution. Assumes 0 <= n <= m <= N/2. Overflow protection is needed when N > 680 or n > 75. Hypergeometric distribution by inversion method, using down-up search starting at the mode using the chop-down technique. This method is faster than the rejection method when the variance is low. */ // Sampling int32_t I; // Loop counter int32_t L = N - m - n; // Parameter double modef; // mode, float double Mp, np; // m + 1, n + 1 double p; // temporary double U; // uniform random double c, d; // factors in iteration double divisor; // divisor, eliminated by scaling double k1, k2; // float version of loop counter double L1 = L; // float version of L Mp = (double)(m + 1); np = (double)(n + 1); if (N != hyp_N_last || m != hyp_m_last || n != hyp_n_last) { // set-up when parameters have changed hyp_N_last = N; hyp_m_last = m; hyp_n_last = n; p = Mp / (N + 2.); modef = np * p; // mode, real hyp_mode = (int32_t)modef; // mode, integer if (hyp_mode == modef && p == 0.5) { hyp_mp = hyp_mode--; } else { hyp_mp = hyp_mode + 1; } // mode probability, using log factorial function // (may read directly from fac_table if N < FAK_LEN) hyp_fm = exp(LnFac(N-m) - LnFac(L+hyp_mode) - LnFac(n-hyp_mode) + LnFac(m) - LnFac(m-hyp_mode) - LnFac(hyp_mode) - LnFac(N) + LnFac(N-n) + LnFac(n) ); // safety bound - guarantees at least 17 significant decimal digits // bound = min(n, (int32_t)(modef + k*c')) hyp_bound = (int32_t)(modef + 11. * sqrt(modef * (1.-p) * (1.-n/(double)N)+1.)); if (hyp_bound > n) hyp_bound = n; } // loop until accepted while(1) { U = Random(); // uniform random number to be converted // start chop-down search at mode if ((U -= hyp_fm) <= 0.) return(hyp_mode); c = d = hyp_fm; // alternating down- and upward search from the mode k1 = hyp_mp - 1; k2 = hyp_mode + 1; for (I = 1; I <= hyp_mode; I++, k1--, k2++) { // Downward search from k1 = hyp_mp - 1 divisor = (np - k1)*(Mp - k1); // Instead of dividing c with divisor, we multiply U and d because // multiplication is faster. This will give overflow if N > 800 U *= divisor; d *= divisor; c *= k1 * (L1 + k1); if ((U -= c) <= 0.) return(hyp_mp - I - 1); // = k1 - 1 // Upward search from k2 = hyp_mode + 1 divisor = k2 * (L1 + k2); // re-scale parameters to avoid time-consuming division U *= divisor; c *= divisor; d *= (np - k2) * (Mp - k2); if ((U -= d) <= 0.) return(hyp_mode + I); // = k2 // Values of n > 75 or N > 680 may give overflow if you leave out this.. // overflow protection // if (U > 1.E100) {U *= 1.E-100; c *= 1.E-100; d *= 1.E-100;} } // Upward search from k2 = 2*mode + 1 to bound for (k2 = I = hyp_mp + hyp_mode; I <= hyp_bound; I++, k2++) { divisor = k2 * (L1 + k2); U *= divisor; d *= (np - k2) * (Mp - k2); if ((U -= d) <= 0.) return(I); // more overflow protection // if (U > 1.E100) {U *= 1.E-100; d *= 1.E-100;} } } }
// main int main() { // parameters. You may change these int32 n = 10; // number of balls double p = 0.4; // probability int32 nn = 1000000; // number of samples // other variables double sum; // sum double ssum; // squaresum int32 min, max; // minimum, maximum double mean; // mean double var; // variance int32 i; // loop counter int32 x; // random variate const int DSIZE = 18; // size of array int32 dis[DSIZE] = {0}; // number of samples in each range int c; // index into dis list double f; // calculated function value double xme; // expected mean double xva; // expected variance double xsd; // expected standard deviation int32 delta; // step in list int32 xa; // list minimum int32 x1, x2; // category range // make random library int32 seed = time(0); // random seed StochasticLib1 sto(seed); // make instance of random library // calculate mean and variance xme = n*p; // calculated mean xva = n*p*(1-p); // calculated variance // calculate appropriate list divisions xsd = sqrt(xva); // calculated std.dev. xa = int(xme - 6.*xsd + 0.5); // calculated minimum if (xa < 0) xa=0; delta = int(12.*xsd/(DSIZE-1)); // list step if (delta < 1) delta = 1; // initialize sum = ssum = 0; min = 2000000000; max = -1; // start message printf("taking %li samples from binomial distribution...", nn); // sample nn times for (i=0; i < nn; i++) { // generate random number with desired distribution x = sto.Binomial(n,p); // update sums sum += x; ssum += (double)x*x; if (x < min) min = x; if (x > max) max = x; c = (x-xa)/delta; if (c < 0) c = 0; if (c >= DSIZE) c = DSIZE-1; dis[c]++;} // calculate sampled mean and variance mean = sum / nn; var = (ssum - sum*sum/nn) / nn; // print sampled and theoretical mean and variance printf("\n\nparameters: n=%li, p=%.3G", n, p); printf("\n mean variance"); printf("\nsampled: %12.6f %12.6f", mean, var); printf("\nexpected: %12.6f %12.6f", xme, xva); // print found and expected frequencies printf("\n\n x found expected"); for (c = 0; c < DSIZE; c++) { x1 = c*delta + xa; if (x1 > n) break; x2 = x1+delta-1; if (c==0 && min<x1) x1 = min; if (c==DSIZE-1 && max>x2) x2 = max; if (x2 > n) x2 = n; // calculate expected frequency for (x=x1, f=0.; x <= x2; x++) { f += exp(LnFac(n) - LnFac(x) - LnFac(n-x) + x*log(p) + (n-x)*log(1-p));} // output found and expected frequency if (dis[c] || f*nn > 1E-4) { if (x1==x2) { printf("\n%7li %12.6f %12.6f", x1, (double)dis[c]/nn, f);} else { printf("\n%6li-%-6li %12.6f %12.6f", x1, x2, (double)dis[c]/nn, f);}}} EndOfProgram(); // system-specific exit code return 0;}
double StochasticLib1::fc_lnpk(int32 k, int32 L, int32 m, int32 n) { // subfunction used by hypergeometric and extended hypergeometric distribution return(LnFac(k) + LnFac(m - k) + LnFac(n - k) + LnFac(L + k));}
int32 StochasticLib1::HypInversionMod (int32 n, int32 m, int32 N) { /* Subfunction for Hypergeometric distribution. Assumes 0 <= n <= m <= N/2. Overflow protection is needed when N > 680 or n > 75. Hypergeometric distribution by inversion method, using down-up search starting at the mode using the chop-down technique. This method is faster than the rejection method when the variance is low. */ static int32 h_n_last = -1, h_m_last = -1, h_N_last = -1; static int32 h_mode, h_mp, h_bound; static double h_fm; int32 L, I, K; double modef, Mp, np, p, c, d, U, divisor; Mp = (double)(m + 1); np = (double)(n + 1); L = N - m - n; if (N != h_N_last || m != h_m_last || n != h_n_last) { // set-up when parameters have changed h_N_last = N; h_m_last = m; h_n_last = n; p = Mp / (N + 2.); modef = np * p; // mode, real h_mode = (int32)modef; // mode, integer if (h_mode == modef && p == 0.5) { h_mp = h_mode--;} else { h_mp = h_mode + 1;} // mode probability, using log factorial function // (may read directly from fac_table if N < FAK_LEN) h_fm = exp(LnFac(N-m) - LnFac(L+h_mode) - LnFac(n-h_mode) + LnFac(m) - LnFac(m-h_mode) - LnFac(h_mode) - LnFac(N) + LnFac(N-n) + LnFac(n) ); // safety bound - guarantees at least 17 significant decimal digits // bound = min(n, (int32)(modef + k*c')) h_bound = (int32)(modef + 11. * sqrt(modef * (1.-p) * (1.-n/(double)N)+1.)); if (h_bound > n) h_bound = n;} // loop until accepted while(1) { U = Random(); // uniform random number to be converted if ((U -= h_fm) <= 0.) return(h_mode); c = d = h_fm; // alternating down- and upward search from the mode for (I = 1; I <= h_mode; I++) { K = h_mp - I; // downward search divisor = (np - K)*(Mp - K); // Instead of dividing c with divisor, we multiply U and d because // multiplication is faster. This will give overflow if N > 800 U *= divisor; d *= divisor; c *= (double)K * (double)(L + K); if ((U -= c) <= 0.) return(K - 1); K = h_mode + I; // upward search divisor = (double)K * (double)(L + K); U *= divisor; c *= divisor; // re-scale parameters to avoid time-consuming division d *= (np - K) * (Mp - K); if ((U -= d) <= 0.) return(K); // Values of n > 75 or N > 680 may give overflow if you leave out this.. // overflow protection // if (U > 1.E100) {U *= 1.E-100; c *= 1.E-100; d *= 1.E-100;} } // upward search from K = 2*mode + 1 to K = bound for (K = h_mp + h_mode; K <= h_bound; K++) { divisor = (double)K * (double)(L + K); U *= divisor; d *= (np - K) * (Mp - K); if ((U -= d) <= 0.) return(K); // more overflow protection // if (U > 1.E100) {U *= 1.E-100; d *= 1.E-100;} }}}