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

📄 wnchyppr.cpp

📁 很好用的随机数产生器
💻 CPP
📖 第 1 页 / 共 4 页
字号:
  gammal2 = sqrtpi * 0.75;
  accur = accuracy * sum;

  // summation loop
  for (k=3; k <= degree; k++) {
    phideri[k] = 0.;
    
    // loop for all colors
    for (i=0; i<colors; i++) {
      if (rho[i] == 0.) continue;
      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] += x[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 CMultiWalleniusNCHypergeometric::integrate(void) {
  // 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) {
    // 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 CMultiWalleniusNCHypergeometric::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, j;

  // 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 (j = 0; j < IPOINTS; j++) {
    tau = ab + delta * xval[j];
    ltau = log(tau);
	taur = r * ltau;
    y = 0.;
    for (i = 0; i < colors; i++) {
      // possible loss of precision due to subtraction here:
      if (omega[i]) {
        y += pow1pow(taur*omega[i],x[i]);}} // ln((1-e^taur*omegai)^xi)
    y += rdm1*ltau + bico;
	if (y > -50.) sum += weights[j] * exp(y);}
  return delta * sum;}


double CMultiWalleniusNCHypergeometric::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
  double t, t1;                        // independent variable
  double rho[MAXCOLORS];               // r*omega[i]
  double q;                            // t^rho[i] / (1-t^rho[i])
  double q1;                           // 1-t^rho[i]
  double zeta[MAXCOLORS][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
  t = 0.5 * (t_from + t_to);
  for (i=0; i<colors; i++) {           // calculate zeta coefficients
    rho[i] = r * omega[i];
    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)
      if (rho[i] == 0.) continue;
      q1 = pow2_1(rho[i]*log2t,&q);
      q /= q1;
      phi[1] -= x[i] * zeta[i][1][1] * q;
      phi[2] -= x[i] * q * (zeta[i][1][2] + q * zeta[i][2][2]);
      phi[3] -= x[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;
    if (++iter > 20) FatalError("Search for inflection point failed in function CMultiWalleniusNCHypergeometric::search_inflect");
    }
  while (fabs(t - t1) > 1E-5);
  return t;}


double CMultiWalleniusNCHypergeometric::probability(long int * x_) {
  // calculate probability function. choosing best method
  int i, j, em;
  long int xsum;
  x = x_;

  for (xsum=i=0; i<colors; i++)  xsum += x[i];
  if (xsum != n) 
    FatalError("sum of x values not equal to n in function CMultiWalleniusNCHypergeometric::probability");

  if (colors < 3) { 
    if (colors <= 0) return 1.;
    if (colors == 1) return x[0] == m[0];
    // colors = 2
    if (omega[1] == 0.) return x[0] == m[0];
    return CWalleniusNCHypergeometric(n,m[0],N,omega[0]/omega[1],accuracy).probability(x[0]);}

  for (i=j=em=0; i<colors; i++) {
    if (x[i] > m[i]) return 0.;
    if (omega[i]==0. && x[i]) return 0.;
    if (x[i] > 0) j++;
    if (x[i] == m[i]) em++;}

  if (n == 0 || n == N) return 1.;

  if (j == 1) 
    return binoexpand();

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

  return integrate();}


/***********************************************************************
         Methods for CMultiWalleniusNCHypergeometricMoments
***********************************************************************/

double CMultiWalleniusNCHypergeometricMoments::moments(double * mu, double * variance, long int * combinations) {
  // calculates mean and variance of multivariate Wallenius noncentral 
  // hypergeometric distribution by calculating all combinations of x-values.
  // Return value = sum of all probabilities. The deviation of this value 
  // from 1 is a measure of the accuracy.
  // Returns the mean to mean[0...colors-1]
  // Returns the variance to variance[0...colors-1]
  double sumf;               // sum of all f(x) values
  long int msum;             // temporary sum
  int i;                     // loop counter

  // get approximate mean
  mean(sx);
  // round mean to integers
  for (i=0; i < colors; i++) {
    xm[i] = (long int)(sx[i]+0.4999999);}

  // set up for recursive loops
  for (i=colors-1, msum=0; i >= 0; i--) {
    remaining[i] = msum;  msum += m[i];}
  for (i=0; i<colors; i++)  sx[i] = sxx[i] = 0.;
  sn = 0;

  // recursive loops to calculate sums  
  sumf = loop(n, 0);

  // calculate mean and variance
  for (i = 0; i < colors; i++) {
    mu[i] = sx[i]/sumf;
    variance[i] = sxx[i]/sumf - sx[i]*sx[i]/(sumf*sumf);}

  // return combinations and sum
  if (combinations) *combinations = sn;
  return sumf;}
    

double CMultiWalleniusNCHypergeometricMoments::loop(long int n, int c) {
  // recursive function to loop through all combinations of x-values.
  // used by moments()
  long int x, x0;                      // x of color c
  long int xmin, xmax;                 // min and max of x[c]
  double s1, s2, sum = 0.;             // sum of f(x) values
  int i;                               // loop counter

  if (c < colors-1) {
    // not the last color
    // calculate min and max of x[c] for given x[0]..x[c-1]
    xmin = n - remaining[c];  if (xmin < 0) xmin = 0;
    xmax = m[c];  if (xmax > n) xmax = n;
    x0 = xm[c];  if (x0 < xmin) x0 = xmin;  if (x0 > xmax) x0 = xmax;
    // loop for all x[c] from mean and up
    for (x = x0, s2 = 0.; x <= xmax; x++) {
      xi[c] = x;
      sum += s1 = loop(n-x, c+1); // recursive loop for remaining colors
      if (s1 < accuracy && s1 < s2) break; // stop when values become negligible
      s2 = s1;}
    // loop for all x[c] from mean and down
    for (x = x0-1; x >= xmin; x--) {
      xi[c] = x;
      sum += s1 = loop(n-x, c+1); // recursive loop for remaining colors
      if (s1 < accuracy && s1 < s2) break; // stop when values become negligible
      s2 = s1;}}
  else {
    // last color
    xi[c] = n;
    s1 = probability(xi);
    for (i=0; i < colors; i++) {
      sx[i]  += s1 * xi[i];
      sxx[i] += s1 * xi[i] * xi[i];}
    sn++;
    sum = s1;}
  return sum;}

⌨️ 快捷键说明

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