⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wnchyppr.cpp

📁 很好用的随机数产生器
💻 CPP
📖 第 1 页 / 共 4 页
字号:
    if (++iter > 20) FatalError("Search for inflection point failed in function CWalleniusNCHypergeometric::search_inflect");
    }
  while (fabs(t - t1) > 1E-5);
  return t;}


double CWalleniusNCHypergeometric::probability(long int x_) {
  // calculate probability function. choosing best method
  x = x_;
  if (x < xmin || x > xmax) return 0.;
  if (xmin == xmax) return 1.;
  if (omega == 1.) { // hypergeometric
    return exp(lnbico() + LnFac(n) + LnFac(N-n) - LnFac(N));}

  long int x2 = n - x;
  long int x0 = x < x2 ? x : x2;
  int em = (x == m || x2 == N-m);

  if (x0 == 0 && n > 500) {
    return binoexpand();}

  if (double(n)*x0 < 1000 || double(n)*x0 < 10000 && (N > 1000.*n || em)) {
    return recursive();}

  if (x0 <= 1 && N-n < 3) { 
    return binoexpand();}

  findpars();
  if (w < 0.04 && E < 10 && (!em || w > 0.004)) {
    return laplace();}

  return integrate();}


int CWalleniusNCHypergeometric::MakeTable(double * table, long int MaxLength, long int * start, long int * end) {
  // Makes a table of Wallenius noncentral hypergeometric probability distribution.
  // Returns true if table is long enough. Otherwise it fills the table
  // with as many correct values as possible and returns false.
  // The tails are cut off where the values are < 0.01*accuracy, so that 
  // *start may be > xmin and *end may be < xmax.
  // The first and last x value represented in the table are returned in 
  // *start and *end. The resulting probability values are returned in the 
  // first (*end - *start + 1) positions of table. Any unused part of table 
  // is overwritten with garbage. It is the responsibility of the calling 
  // program to allocate sufficient space for the table. 
  
  const int BUFSIZE = 2048;            // length of internal table
  double accuracya;                    // absolute accuracy
  double p[BUFSIZE+2];                 // probabilities
  double * p1, * p2;                   // offset into p
  double mxo;                          // (m-x)*omega
  double Nmnx;                         // N-m-nu+x
  double y, y1;                        // probability. Save old p[x] before it is overwritten
  double d1, d2, dcom;                 // divisors in probability formula
  long int xi, nu;                     // xi, nu = recursion values of x, n
  long int x1, x2;                     // lowest and highest x or xi
  long int i1, i2;                     // index into table
  double area;                         // estimate of area needed for recursion method

  area = double(n)*(xmax - xmin + 1);
  if (area < 4000 || area < 10000 && N > 1000.*n) {
    // use recursion method
    accuracya = 0.005 * accuracy;      // absolute accuracy
    p1 = p2 = p + 1;                   // make space for p1[-1]
    p1[-1] = 0.;  p1[0]  = 1.;         // initialize for recursion
    x1 = x2 = 0;
    for (nu=1; nu<=n; nu++) {
      if (n - nu < xmin - x1 || p1[x1] < accuracya) {
        x1++;                          // increase lower limit when breakpoint passed or probability negligible
        p2--;}                         // compensate buffer offset in order to reduce storage space
      if (x2 < xmax && p1[x2] >= accuracya) {
        x2++;  y1 = 0.;}               // increase upper limit until x has been reached
      else {
        y1 = p1[x2];}
      if (p2 + x2 - p > BUFSIZE || x1 > x2) {
        goto ONE_BY_ONE;}              // Error: table length exceeded. Use other method

      mxo = (m-x2)*omega;
      Nmnx = N-m-nu+x2+1;
      for (xi = x2; xi >= x1; xi--) {  // backwards loop
        d2 = mxo + Nmnx;
        mxo += omega; Nmnx--;
        d1 = mxo + Nmnx;
        dcom = 1. / (d1 * d2);         // save a division by making common divisor
        y  = p1[xi-1]*mxo*d2*dcom + y1*(Nmnx+1)*d1*dcom;
        y1 = p1[xi-1];                 // (warning: pointer alias, can't swap instruction order)
        p2[xi] = y;}
      p1 = p2;}

    // return results
    i1 = i2 = x2 - x1 + 1;             // desired table length
    if (i2 > MaxLength) i2 = MaxLength;// limit table length
    *start = x1;  *end = x1 + i2 - 1;
    if (i2>0) memcpy(table, p+1, i2*sizeof(table[0]));// copy into external table
    return i1==i2;}                    // true if table size not reduced

  else {
    // area too big for recursion method
    // Calculate values one by one
    ONE_BY_ONE:
    accuracya = 0.01 * accuracy;       // absolute accuracy

    // start to fill table from the end and down. start with x = floor(mean)
    x2 = (long int)mean();
    x1 = x2 + 1;  i1 = MaxLength;
    while (x1 > xmin) {                // loop for left tail
      x1--;  i1--;
      y = probability(x1);
      table[i1] = y;
      if (y < accuracya) break;
      if (i1 == 0) break;}
    *start = x1;
    i2 = x2-x1+1; 
    if (i1>0 && i2>0) { // move numbers down to beginning of table
      memcpy(table, table+i1, i2*sizeof(table[0]));}
    // fill rest of table from mean and up
    i2--;
    while (x2 < xmax) {                // loop for right tail
      if (i2 == MaxLength-1) {
        *end = x2; return 0;}          // table full
      x2++;  i2++;
      y = probability(x2);
      table[i2] = y;
      if (y < accuracya) break;}
    *end = x2;
    return 1;}}


/***********************************************************************
    calculation methods in class CMultiWalleniusNCHypergeometric
***********************************************************************/
  
CMultiWalleniusNCHypergeometric::CMultiWalleniusNCHypergeometric(long int n_, long int * m_, double * odds_, int colors_, float accuracy_) {
  // constructor
  long int N1;
  int i;
  n = n_;  m = m_;  omega = odds_;  colors = colors_;  accuracy = accuracy_;
  r = 1.;
  for (N=N1=0, i=0; i < colors; i++) {
    if (m[i] < 0 || omega[i] < 0) FatalError("Parameter negative in constructor for CMultiWalleniusNCHypergeometric");
    N += m[i];
    if (omega[i]) N1 += m[i];}
  if (N < n) FatalError("Not enough items in constructor for CMultiWalleniusNCHypergeometric");
  if (N1< n) FatalError("Not enough items with nonzero weight in constructor for CMultiWalleniusNCHypergeometric");
  }


void CMultiWalleniusNCHypergeometric::mean(double * mu) {
  // calculate approximate mean of multivariate Wallenius noncentral hypergeometric 
  // distribution. Result is returned in mu[0..colors-1]
  double omeg[MAXCOLORS];              // scaled weights
  double omr;                          // reciprocal mean weight
  double t, t1;                        // independent variable in iteration
  double To, To1;                      // exp(t*omega[i]), 1-exp(t*omega[i])
  double H;                            // function to find root of
  double HD;                           // derivative of H
  int i;                               // color index
  int iter;                            // number of iterations

  // calculate mean weight
  for (omr=0., i=0; i < colors; i++) omr += omega[i]*m[i];
  omr = N / omr;
  // scale weights to make mean = 1
  for (i = 0; i < colors; i++) omeg[i] = omega[i] * omr;
  // Newton Raphson iteration
  iter = 0;  t = -1.;                  // first guess
  do {
    t1 = t;
    H = HD = 0.;
    // calculate H and HD
    for (i = 0; i < colors; i++) {
      if (omeg[i] != 0.) {
        To1 = pow2_1(t * (1./M_LN2) * omeg[i], &To);
        H += m[i] * To1;
        HD -= m[i] * omeg[i] * To;}}
    t -= (H-n) / HD;
    if (t >= 0) t = 0.5 * t1;
    if (++iter > 20) {
      FatalError("Search for mean failed in function CMultiWalleniusNCHypergeometric::mean");}}
  while (fabs(H - n) > 1E-3);
  // finished iteration. Get all mu[i]
  for (i = 0; i < colors; i++) {
    if (omeg[i] != 0.) {
      To1 = pow2_1(t * (1./M_LN2) * omeg[i]);
      mu[i] = m[i] * To1;}
    else {
      mu[i] = 0.;}}}


// implementations of different calculation methods
double CMultiWalleniusNCHypergeometric::binoexpand(void) {
  // binomial expansion of integrand
  // only implemented for x[i] = 0 for all but one i
  int i, j, k;
  double W = 0.;                       // total weight
  for (i=j=k=0; i<colors; i++) {
    W += omega[i] * m[i];
    if (x[i]) {
      j=i; k++;}}                      // find the nonzero x[i]
  if (k > 1) FatalError("More than one x[i] nonzero in CMultiWalleniusNCHypergeometric::binoexpand");
  return exp(FallingFactorial(m[j],n) - FallingFactorial(W/omega[j],n));}


double CMultiWalleniusNCHypergeometric::lnbico(void) {
  // natural log of binomial coefficients
  bico = 0.;
  int i;
  for (i=0; i<colors; i++) {
    if (x[i] < m[i] && omega[i]) {
      bico += LnFac(m[i]) - LnFac(x[i]) - LnFac(m[i]-x[i]);}}
  return bico;}


void CMultiWalleniusNCHypergeometric::findpars(void) {
  // calculate r, w, E
  // calculate d, E, r, w

  // find r to center peak of integrand at 0.5
  double dd;                           // scaled d
  double dr;                           // 1/d
  
  double z, zd, rr, lastr, rrc, rt, r2, r21, a, b, ro, k1;
  double omax;                         // highest omega
  double omaxr;                        // 1/omax
  double omeg[MAXCOLORS];              // scaled weights
  int i, j = 0;

  // find highest omega
  for (omax=0., i=0; i < colors; i++) {
    if (omega[i] > omax) omax = omega[i];}
  omaxr = 1. / omax;
  dd = E = 0.;
  for (i = 0; i < colors; i++) {
    // scale weights to make max = 1
    omeg[i] = omega[i] * omaxr;
    // calculate d and E
    dd += omeg[i] * (m[i]-x[i]);
    E  += omeg[i] * m[i];}
  dr = 1. / dd;
  E *= dr;
  rr = r * omax;
  if (rr <= dr) rr = 1.2 * dr;  // initial guess
  // Newton-Raphson iteration to find r
  do {
    lastr = rr;
	rrc = 1. / rr;
	z = dd - rrc;                      // z(r)
	zd = rrc * rrc;                    // z'(r)
    for (i=0; i<colors; i++) {
      rt = rr * omeg[i];
      if (rt < 100. && rt > 0.) {      // avoid overflow and division by 0
        r21 = pow2_1(rt, &r2);         // r2=2^r, r21=1.-2^r
	    a = omeg[i] / r21;             // omegai/(1.-2^r)
	    b = x[i] * a;                  // x*omegai/(1.-2^r)
	    z  += b;
	    zd += b * a * r2 * M_LN2;}}
    if (zd == 0) FatalError("can't find r in function CMultiWalleniusNCHypergeometric::findpars");
	rr -= z / zd;                      // next r
	if (rr <= dr) rr = lastr * 0.125 + dr * 0.875;
    if (++j == 70) FatalError("convergence problem searching for r in function CMultiWalleniusNCHypergeometric::findpars");
    }
  while (fabs(rr-lastr) > rr * 1.E-5);
  rd = rr * dd;
  r = rr * omaxr;

  // find peak width
  phi2d = 0.;
  for (i=0; i<colors; i++) {
    ro = rr * omeg[i];
    if (ro < 300 && ro > 0.) {         // avoid overflow and division by 0
      k1 = pow2_1(ro);
      k1 = -1. / k1;
      k1 = omeg[i] * omeg[i] * (k1 + k1*k1);}
    else k1 = 0.;
    phi2d += x[i] * k1;}
  phi2d *= -4. * rr * rr;
  if (phi2d > 0.) FatalError("peak width undefined in function CMultiWalleniusNCHypergeometric::findpars");
  w = 1./sqrt(-phi2d);}


double CMultiWalleniusNCHypergeometric::laplace(void) {
  // Laplace's method with narrow integration interval, 
  // using error function residue table, defined in erfres.cpp
  // NOTE: This function can only be used when the integrand peak is narrow.
  // findpars() must be called before this function.

  const int MAXDEG = 40;        // arraysize
  int degree;                   // max expansion degree
  double accur;                 // stop expansion when terms below this threshold
  double f0;                    // factor outside integral
  double rho[MAXCOLORS];        // r*omegai
  double qi;                    // 2^(-rho)
  double qi1;                   // 1-qi
  double qq[MAXCOLORS];         // qi / qi1
  double eta[MAXCOLORS+1][MAXDEG+1]; // eta coefficients
  double phideri[MAXDEG+1];     // derivatives of phi
  double PSIderi[MAXDEG+1];     // derivatives of PSI
  double epsilon;               // factor for integration interval
  double epsilonmax;            // max possible epsilon
  double * erfres;              // pointer to table of error function expansion residues
  int erfreslen;                // length of table

  // variables in asymptotic summation
  static const double sqrtpi = 1.772453850905515993; // sqrt(pi)
  static const double sqrt8  = 2.828427124746190098; // sqrt(8)
  double qqpow;                 // qq^j
  double pow2k;                 // 2^k
  double mfac;                  // (-1)^(k-1)*(k-1)!
  double kfac;                  // k!
  double bino;                  // binomial coefficient  
  double wr;                    // 1/w, w = integrand peak width
  double vr;                    // 1/v, v = integration interval
  double v2m2;                  // (2*v)^(-2)
  double v2mk1;                 // (2*v)^(-k-1)
  double gammal2;               // Gamma((k+1)/2);
  double s;                     // summation term
  double sum;                   // Taylor sum

  int i;                        // loop counter for color
  int j;                        // loop counter for derivative
  int k;                        // loop counter for expansion degree
  int ll;                       // k/2
  int converg=0;                // number of consequtive terms below accuracy

  // initialize
  for (k=0; k<=2; k++)  phideri[k] = PSIderi[k] = 0;

  // find rho[i], qq[i], first eta coefficients, and zero'th derivative of phi
  for (i=0; i<colors; i++) {
    rho[i] = r * omega[i];
    if (rho[i] == 0.) continue;
    if (rho[i] > 40.) {
      qi=0.;  qi1 = 1.;}  // avoid underflow
    else {
      qi1 = pow2_1(-rho[i], &qi);}   // qi=2^(-rho), qi1=1.-2^(-rho)
    qq[i] = qi / qi1;    // 2^(-r*omegai)/(1.-2^(-r*omegai))
    // peak = zero'th derivative
    phideri[0] += x[i] * log1mx(qi, qi1);
    // eta coefficients
    eta[i][0] = 0.;
    eta[i][1] = eta[i][2] = rho[i]*rho[i];}

  // d, r, and w must be calculated by findpars()
  // zero'th derivative
  phideri[0] -= (rd - 1.) * M_LN2;
  // scaled factor outside integral
  f0 = rd * exp(phideri[0] + lnbico());
  // calculate narrowed integration interval
  vr = sqrt8 * w;
  wr = 1. / w;
  phideri[2] = phi2d;
  epsilonmax = 0.5 * wr;
  if (accuracy < 1.E-10 && epsilonmax >= 8.) {
    epsilon = 8.; erfres = erfres80; erfreslen = sizeof(erfres80)/sizeof(erfres80[0]);}
  else if (accuracy < 1.E-8 && epsilonmax >= 7.) {
    epsilon = 7.; erfres = erfres70; erfreslen = sizeof(erfres70)/sizeof(erfres70[0]);}
  else if (accuracy < 1.E-6 && epsilonmax >= 6.) {
    epsilon = 6.; erfres = erfres60; erfreslen = sizeof(erfres60)/sizeof(erfres60[0]);}
  else {
    epsilon = 5.; erfres = erfres50; erfreslen = sizeof(erfres50)/sizeof(erfres50[0]);}
  if (epsilon > epsilonmax) FatalError("Laplace failed. Peak width too high in function CMultiWalleniusNCHypergeometric::laplace");
  degree = MAXDEG;
  if (degree >= erfreslen*2) degree = erfreslen*2-2;

  // variables used in summation
  v2m2 = 0.25*vr*vr;        // (2*v)^(-2)

  // set up for starting loop at k=3
  PSIderi[0] = 1.;
  pow2k = 8.;
  mfac = 2.;
  kfac = 2.;
  sum = 0.5 * vr * sqrtpi * erfres[0];
  v2mk1 = 0.5*vr*v2m2*v2m2;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -