Пример #1
0
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;}
Пример #2
0
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;}
      }
   }
}
Пример #5
0
// 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;}
Пример #6
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));}
Пример #7
0
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;}
      }}}