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

📄 wnchyppr.cpp

📁 很好用的随机数产生器
💻 CPP
📖 第 1 页 / 共 4 页
字号:
  double o;
  if (x > n/2) { // invert
    x1 = n-x; m1 = N-m; m2 = m; o = 1./omega;}
  else {
    x1 = x; m1 = m; m2 = N-m; o = omega;}

  if (x1 == 0) {
    return exp(FallingFactorial(m2,n) - FallingFactorial(m2+o*m1,n));}
    
  if (x1 == 1) {
    double d, e, q, q0, q1;
    q = FallingFactorial(m2,n-1);
    e = o*m1+m2;
    q1 = q - FallingFactorial(e,n);
    e -= o;
    q0 = q - FallingFactorial(e,n);
    d = e - (n-1);
    return m1*d*(exp(q0) - exp(q1));}

  FatalError("x > 1 not supported by function CWalleniusNCHypergeometric::binoexpand");
  return 0;}


double CWalleniusNCHypergeometric::laplace() {
  // Laplace's method with narrow integration interval, 
  // using error function residues 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 COLORS = 2;         // number of colors
  const int MAXDEG = 40;        // arraysize
  int degree;                   // max expansion degree
  double accur;                 // stop expansion when terms below this threshold
  double omegai[COLORS] = {omega, 1.}; // weights for each color
  double xi[COLORS] = {x, n-x}; // number of each color sampled
  double f0;                    // factor outside integral
  double rho[COLORS];           // r*omegai
  double qi;                    // 2^(-rho)
  double qi1;                   // 1-qi
  double qq[COLORS];            // qi / qi1
  double eta[COLORS+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 * omegai[i];
    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] += xi[i] * log1mx(qi, qi1);
    // eta coefficients
    eta[i][0] = 0.;
    eta[i][1] = eta[i][2] = rho[i]*rho[i];}

  // r, rd, 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 CWalleniusNCHypergeometric::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;
  gammal2 = sqrtpi * 0.75;
  accur = accuracy * sum;

  // summation loop
  for (k=3; k <= degree; k++) {
    phideri[k] = 0.;
    
    // loop for all (2) colors
    for (i=0; i<COLORS; i++) {
      eta[i][k] = 0.;
      // backward loop for all powers
      for (j=k; j>0; j--) {
        // find coefficients recursively from previous coefficients
        eta[i][j]  =  eta[i][j]*(j*rho[i]-(k-2)) +  eta[i][j-1]*rho[i]*(j-1);}
      qqpow = 1.;
      // forward loop for all powers
      for (j=1; j<=k; j++) {
        qqpow *= qq[i];   // qq^j
        // contribution to derivative
        phideri[k] += xi[i] * eta[i][j] * qqpow;}}

    // finish calculation of derivatives
    phideri[k] = -pow2k * phideri[k] + 2*(1-k)*phideri[k-1];

    pow2k *= 2.;    // 2^k
    mfac *= -k;     // (-1)^(k-1)*(k-1)!

    // loop to calculate derivatives of PSI from derivatives of psi.
    // terms # 0, 1, 2, k-2, and k-1 are zero and not included in loop.
    // The j'th derivatives of psi are identical to the derivatives of phi for j>2, and
    // zero for j=1,2. Hence we are using phideri[j] for j>2 here.
    PSIderi[k] = phideri[k]; // this is term # k
    bino = 0.5*(k-1)*(k-2); // binomial coefficient for term # 3
    for (j=3; j < k-2; j++) { // loop for remaining nonzero terms (if k>5)
      PSIderi[k] += PSIderi[k-j] * phideri[j] * bino;
      bino *= double(k-j)/double(j);}

    kfac *= k;

    if ((k & 1) == 0) { // only for even k
      ll = k/2;
      s = (PSIderi[k]/kfac)*v2mk1* gammal2 * erfres[ll];
      sum += s;

      // check for convergence of Taylor expansion
      if (fabs(s) < accur) converg++; else converg = 0;
      if (converg > 1) break;

      // update recursive expressions
      v2mk1 *= v2m2;
      gammal2 *= k/2 + 0.5;}}

  // multiply by terms outside integral  
  return f0 * sum;}


double CWalleniusNCHypergeometric::integrate() {
  // Wallenius non-central hypergeometric distribution function
  // calculation by numerical integration with variable-length steps
  // NOTE: findpars() must be called before this function.
  double s;                            // result of integration step
  double sum;                          // integral
  double ta, tb;                       // subinterval for integration step

  lnbico();                            // compute log of binomial coefficients

  // choose method:
  if (w < 0.02 || w < 0.1 && (x==m || n-x==N-m) && accuracy > 1E-6) {
    // normal method. Step length determined by peak width w
    double delta, s1;
    s1 = accuracy < 1E-9 ? 0.5 : 1.;
    delta = s1 * w;                    // integration steplength
    ta = 0.5 + 0.5 * delta;
    sum = integrate_step(1.-ta, ta);   // first integration step around center peak
    do {
      tb = ta + delta;
      if (tb > 1.) tb = 1.;
      s  = integrate_step(ta, tb);     // integration step to the right of peak
      s += integrate_step(1.-tb,1.-ta);// integration step to the left of peak
      sum += s;
      if (s < accuracy * sum) break;   // stop before interval finished if accuracy reached
      ta = tb;
      if (tb > 0.5 + w) delta *= 2.;}  // increase step length far from peak
    while (tb < 1.);}

  else {
    // difficult situation. Step length determined by inflection points
    double t1, t2, tinf, delta, delta1;
    sum = 0.;
    // do left and right half of integration interval separately:
    for (t1=0., t2=0.5; t1 < 1.; t1+=0.5, t2+=0.5) { 
      // integrate from 0 to 0.5 or from 0.5 to 1
      tinf = search_inflect(t1, t2);   // find inflection point
      delta = tinf - t1; if (delta > t2 - tinf) delta = t2 - tinf; // distance to nearest endpoint
      delta *= 1./7.;                  // 1/7 will give 3 steps to nearest endpoint
      if (delta < 1E-4) delta = 1E-4;
      delta1 = delta;
      // integrate from tinf forwards to t2
      ta = tinf;
      do {
        tb = ta + delta1;
        if (tb > t2 - 0.25*delta1) tb = t2; // last step of this subinterval
        s = integrate_step(ta, tb);         // integration step
        sum += s;
        delta1 *= 2;                        // double steplength
        if (s < sum * 1E-4) delta1 *= 8.;   // large step when s small
        ta = tb;}
      while (tb < t2);
      if (tinf) {
        // integrate from tinf backwards to t1
        tb = tinf;
        do {
          ta = tb - delta;
          if (ta < t1 + 0.25*delta) ta = t1; // last step of this subinterval
          s = integrate_step(ta, tb);        // integration step
          sum += s;
          delta *= 2;                        // double steplength
          if (s < sum * 1E-4) delta *= 8.;   // large step when s small
          tb = ta;}
        while (ta > t1);}}}

  return sum * rd;}


double CWalleniusNCHypergeometric::integrate_step(double ta, double tb) {
  // integration subprocedure used by integrate()
  // makes one integration step from ta to tb using Gauss-Legendre method.
  // result is scaled by multiplication with exp(bico)
  double ab, delta, tau, ltau, y, sum, taur, rdm1;
  int i;

  // define constants for Gauss-Legendre integration with IPOINTS points
  #define IPOINTS  8  // number of points in each integration step

  #if IPOINTS == 3
  static const double xval[3]    = {-.774596669241,0,0.774596668241};
  static const double weights[3] = {.5555555555555555,.88888888888888888,.55555555555555};
  #elif IPOINTS == 4
  static const double xval[4]    = {-0.861136311594,-0.339981043585,0.339981043585,0.861136311594},
  static const double weights[4] = {0.347854845137,0.652145154863,0.652145154863,0.347854845137};
  #elif IPOINTS == 5
  static const double xval[5]    = {-0.906179845939,-0.538469310106,0,0.538469310106,0.906179845939};
  static const double weights[5] = {0.236926885056,0.478628670499,0.568888888889,0.478628670499,0.236926885056};
  #elif IPOINTS == 6
  static const double xval[6]    = {-0.932469514203,-0.661209386466,-0.238619186083,0.238619186083,0.661209386466,0.932469514203};
  static const double weights[6] = {0.171324492379,0.360761573048,0.467913934573,0.467913934573,0.360761573048,0.171324492379};
  #elif IPOINTS == 8
  static const double xval[8]    = {-0.960289856498,-0.796666477414,-0.525532409916,-0.183434642496,0.183434642496,0.525532409916,0.796666477414,0.960289856498};
  static const double weights[8] = {0.10122853629,0.222381034453,0.313706645878,0.362683783378,0.362683783378,0.313706645878,0.222381034453,0.10122853629};
  #elif IPOINTS == 12
  static const double xval[12]   = {-0.981560634247,-0.90411725637,-0.769902674194,-0.587317954287,-0.367831498998,-0.125233408511,0.125233408511,0.367831498998,0.587317954287,0.769902674194,0.90411725637,0.981560634247};
  static const double weights[12]= {0.0471753363866,0.106939325995,0.160078328543,0.203167426723,0.233492536538,0.249147045813,0.249147045813,0.233492536538,0.203167426723,0.160078328543,0.106939325995,0.0471753363866};
  #elif IPOINTS == 16
  static const double xval[16]   = {-0.989400934992,-0.944575023073,-0.865631202388,-0.755404408355,-0.617876244403,-0.458016777657,-0.281603550779,-0.0950125098376,0.0950125098376,0.281603550779,0.458016777657,0.617876244403,0.755404408355,0.865631202388,0.944575023073,0.989400934992};
  static const double weights[16]= {0.027152459411,0.0622535239372,0.0951585116838,0.124628971256,0.149595988817,0.169156519395,0.182603415045,0.189450610455,0.189450610455,0.182603415045,0.169156519395,0.149595988817,0.124628971256,0.0951585116838,0.0622535239372,0.027152459411};
  #else
  #error // IPOINTS must be a value for which the tables are defined
  #endif

  delta = 0.5 * (tb - ta);
  ab = 0.5 * (ta + tb);
  rdm1 = rd - 1.;
  sum = 0;

  for (i = 0; i < IPOINTS; i++) {
    tau = ab + delta * xval[i];
    ltau = log(tau);
    taur = r * ltau;
    // possible loss of precision due to subtraction here:
    y = pow1pow(taur*omega,x) + pow1pow(taur,n-x) + rdm1*ltau + bico;
      if (y > -50.) sum += weights[i] * exp(y);}
  return delta * sum;}


double CWalleniusNCHypergeometric::search_inflect(double t_from, double t_to) {
  // search for an inflection point of the integrand PHI(t) in the interval
  // t_from < t < t_to
  const int COLORS = 2;                // number of colors
  double t, t1;                        // independent variable
  double rho[COLORS];                  // r*omega[i]
  double q;                            // t^rho[i] / (1-t^rho[i])
  double q1;                           // 1-t^rho[i]
  double xx[COLORS];                   // x[i]
  double zeta[COLORS][4][4];           // zeta[i,j,k] coefficients
  double phi[4];                       // derivatives of phi(t) = log PHI(t)
  double Z2;                           // PHI''(t)/PHI(t)
  double Zd;                           // derivative in Newton Raphson iteration
  double rdm1;                         // r * d - 1
  double tr;                           // 1/t
  double log2t;                        // log2(t)
  double method;                       // 0 for z2'(t) method, 1 for z3(t) method
  int i;                               // color
  int iter;                            // count iterations

  rdm1 = rd - 1.;
  if (t_from == 0 && rdm1 <= 1.) return 0.; //no inflection point
  rho[0] = r*omega;  rho[1] = r;
  xx[0] = x;  xx[1] = n - x;
  t = 0.5 * (t_from + t_to);
  for (i=0; i<COLORS; i++) {           // calculate zeta coefficients
    zeta[i][1][1] = rho[i];
    zeta[i][1][2] = rho[i] * (rho[i] - 1.);
    zeta[i][2][2] = rho[i] * rho[i];
    zeta[i][1][3] = zeta[i][1][2] * (rho[i] - 2.);
    zeta[i][2][3] = zeta[i][1][2] * rho[i] * 3.;
    zeta[i][3][3] = zeta[i][2][2] * rho[i] * 2.;}
  iter = 0;

  do {
    t1 = t;
    tr = 1. / t;
    log2t = log(t)*(1./M_LN2);
    phi[1] = phi[2] = phi[3] = 0.;
    for (i=0; i<COLORS; i++) {         // calculate first 3 derivatives of phi(t)
      q1 = pow2_1(rho[i]*log2t,&q);
      q /= q1;
      phi[1] -= xx[i] * zeta[i][1][1] * q;
      phi[2] -= xx[i] * q * (zeta[i][1][2] + q * zeta[i][2][2]);
      phi[3] -= xx[i] * q * (zeta[i][1][3] + q * (zeta[i][2][3] + q * zeta[i][3][3]));}
    phi[1] += rdm1;
    phi[2] -= rdm1;
    phi[3] += 2. * rdm1;
    phi[1] *= tr;
    phi[2] *= tr * tr;
    phi[3] *= tr * tr * tr;
    method = (iter & 2) >> 1;          // alternate between the two methods
    Z2 = phi[1]*phi[1] + phi[2];
    Zd = method*phi[1]*phi[1]*phi[1] + (2.+method)*phi[1]*phi[2] + phi[3];

    if (t < 0.5) {
      if (Z2 > 0) {
        t_from = t;}
      else {
        t_to = t;}
      if (Zd >= 0) { 
        // use binary search if Newton-Raphson iteration makes problems
        t = (t_from ? 0.5 : 0.2) * (t_from + t_to);}
      else {
        // Newton-Raphson iteration
        t -= Z2 / Zd;}}
    else {
      if (Z2 < 0) {
        t_from = t;}
      else {
        t_to = t;}
      if (Zd <= 0) {
        // use binary search if Newton-Raphson iteration makes problems
        t = 0.5 * (t_from + t_to);}
      else {
        // Newton-Raphson iteration
        t -= Z2 / Zd;}}
    if (t >= t_to) t = (t1 + t_to) * 0.5;
    if (t <= t_from) t = (t1 + t_from) * 0.5;

⌨️ 快捷键说明

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