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

📄 binomial_tpe.c

📁 该文件为c++的数学函数库!是一个非常有用的编程工具.它含有各种数学函数,为科学计算、工程应用等程序编写提供方便!
💻 C
字号:
/* randist/binomial_tpe.c *  * Copyright (C) 1996-2003 James Theiler, Brian Gough *  * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. *  * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * General Public License for more details. *  * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <config.h>#include <math.h>#include <gsl/gsl_rng.h>#include <gsl/gsl_randist.h>#include <gsl/gsl_pow_int.h>#include <gsl/gsl_sf_gamma.h>/* The binomial distribution has the form,   f(x) =  n!/(x!(n-x)!) * p^x (1-p)^(n-x) for integer 0 <= x <= n        =  0                               otherwise   This implementation follows the public domain ranlib function   "ignbin", the bulk of which is the BTPE (Binomial Triangle   Parallelogram Exponential) algorithm introduced in   Kachitvichyanukul and Schmeiser[1].  It has been translated to use   modern C coding standards.   If n is small and/or p is near 0 or near 1 (specifically, if   n*min(p,1-p) < SMALL_MEAN), then a different algorithm, called   BINV, is used which has an average runtime that scales linearly   with n*min(p,1-p).   But for larger problems, the BTPE algorithm takes the form of two   functions b(x) and t(x) -- "bottom" and "top" -- for which b(x) <   f(x)/f(M) < t(x), with M = floor(n*p+p).  b(x) defines a triangular   region, and t(x) includes a parallelogram and two tails.  Details   (including a nice drawing) are in the paper.   [1] Kachitvichyanukul, V. and Schmeiser, B. W.  Binomial Random   Variate Generation.  Communications of the ACM, 31, 2 (February,   1988) 216.   Note, Bruce Schmeiser (personal communication) points out that if   you want very fast binomial deviates, and you are happy with   approximate results, and/or n and n*p are both large, then you can   just use gaussian estimates: mean=n*p, variance=n*p*(1-p).   This implementation by James Theiler, April 2003, after obtaining   permission -- and some good advice -- from Drs. Kachitvichyanukul   and Schmeiser to use their code as a starting point, and then doing   a little bit of tweaking.   Additional polishing for GSL coding standards by Brian Gough.  */#define SMALL_MEAN 14           /* If n*p < SMALL_MEAN then use BINV                                   algorithm. The ranlib                                   implementation used cutoff=30; but                                   on my computer 14 works better */#define BINV_CUTOFF 110         /* In BINV, do not permit ix too large */#define FAR_FROM_MEAN 20        /* If ix-n*p is larger than this, then                                   use the "squeeze" algorithm.                                   Ranlib used 20, and this seems to                                   be the best choice on my machine as                                   well */#define LNFACT(x) gsl_sf_lnfact(x)inline static doubleStirling (double y1){  double y2 = y1 * y1;  double s =    (13860.0 -     (462.0 - (132.0 - (99.0 - 140.0 / y2) / y2) / y2) / y2) / y1 / 166320.0;  return s;}unsigned intgsl_ran_binomial_tpe (const gsl_rng * rng, double pp, unsigned int n){  int ix;                       /* return value */  const double p = (pp > 0.5) ? 1 - pp : pp;    /* choose p=min(pp,1-pp) */  const double q = 1 - p;  const double s = p / q;  const double np = n * p;  if (n == 0)    return 0;  /* Inverse cdf logic for small mean (BINV in K+S) */  if (np < SMALL_MEAN)    {      double f0 = gsl_pow_int (q, n);   /* f(x), starting with x=0 */      while (1)        {          /* This while(1) loop will almost certainly only loop once; but           * if u=1 to within a few epsilons of machine precision, then it           * is possible for roundoff to prevent the main loop over ix to           * achieve its proper value.  following the ranlib implementation,           * we introduce a check for that situation, and when it occurs,           * we just try again.           */          double f = f0;          double u = gsl_rng_uniform (rng);          for (ix = 0; ix <= BINV_CUTOFF; ++ix)            {              if (u < f)                goto Finish;              u -= f;              /* Use recursion f(x+1) = f(x)*[(n-x)/(x+1)]*[p/(1-p)] */              f *= s * (n - ix) / (ix + 1);            }          /* It should be the case that the 'goto Finish' was encountered           * before this point was ever reached.  But if we have reached           * this point, then roundoff has prevented u from decreasing           * all the way to zero.  This can happen only if the initial u           * was very nearly equal to 1, which is a rare situation.  In           * that rare situation, we just try again.           *           * Note, following the ranlib implementation, we loop ix only to           * a hardcoded value of SMALL_MEAN_LARGE_N=110; we could have           * looped to n, and 99.99...% of the time it won't matter.  This           * choice, I think is a little more robust against the rare           * roundoff error.  If n>LARGE_N, then it is technically           * possible for ix>LARGE_N, but it is astronomically rare, and           * if ix is that large, it is more likely due to roundoff than           * probability, so better to nip it at LARGE_N than to take a           * chance that roundoff will somehow conspire to produce an even           * larger (and more improbable) ix.  If n<LARGE_N, then once           * ix=n, f=0, and the loop will continue until ix=LARGE_N.           */        }    }  else    {      /* For n >= SMALL_MEAN, we invoke the BTPE algorithm */      int k;      double ffm = np + p;      /* ffm = n*p+p             */      int m = (int) ffm;        /* m = int floor[n*p+p]    */      double fm = m;            /* fm = double m;          */      double xm = fm + 0.5;     /* xm = half integer mean (tip of triangle)  */      double npq = np * q;      /* npq = n*p*q            */      /* Compute cumulative area of tri, para, exp tails */      /* p1: radius of triangle region; since height=1, also: area of region */      /* p2: p1 + area of parallelogram region */      /* p3: p2 + area of left tail */      /* p4: p3 + area of right tail */      /* pi/p4: probability of i'th area (i=1,2,3,4) */      /* Note: magic numbers 2.195, 4.6, 0.134, 20.5, 15.3 */      /* These magic numbers are not adjustable...at least not easily! */      double p1 = floor (2.195 * sqrt (npq) - 4.6 * q) + 0.5;      /* xl, xr: left and right edges of triangle */      double xl = xm - p1;      double xr = xm + p1;      /* Parameter of exponential tails */      /* Left tail:  t(x) = c*exp(-lambda_l*[xl - (x+0.5)]) */      /* Right tail: t(x) = c*exp(-lambda_r*[(x+0.5) - xr]) */      double c = 0.134 + 20.5 / (15.3 + fm);      double p2 = p1 * (1.0 + c + c);      double al = (ffm - xl) / (ffm - xl * p);      double lambda_l = al * (1.0 + 0.5 * al);      double ar = (xr - ffm) / (xr * q);      double lambda_r = ar * (1.0 + 0.5 * ar);      double p3 = p2 + c / lambda_l;      double p4 = p3 + c / lambda_r;      double var, accept;      double u, v;              /* random variates */    TryAgain:      /* generate random variates, u specifies which region: Tri, Par, Tail */      u = gsl_rng_uniform (rng) * p4;      v = gsl_rng_uniform (rng);      if (u <= p1)        {          /* Triangular region */          ix = (int) (xm - p1 * v + u);          goto Finish;        }      else if (u <= p2)        {          /* Parallelogram region */          double x = xl + (u - p1) / c;          v = v * c + 1.0 - fabs (x - xm) / p1;          if (v > 1.0 || v <= 0.0)            goto TryAgain;          ix = (int) x;        }      else if (u <= p3)        {          /* Left tail */          ix = (int) (xl + log (v) / lambda_l);          if (ix < 0)            goto TryAgain;          v *= ((u - p2) * lambda_l);        }      else        {          /* Right tail */          ix = (int) (xr - log (v) / lambda_r);          if (ix > (double)n)            goto TryAgain;          v *= ((u - p3) * lambda_r);        }      /* At this point, the goal is to test whether v <= f(x)/f(m)        *       *  v <= f(x)/f(m) = (m!(n-m)! / (x!(n-x)!)) * (p/q)^{x-m}       *       */      /* Here is a direct test using logarithms.  It is a little       * slower than the various "squeezing" computations below, but       * if things are working, it should give exactly the same answer       * (given the same random number seed).  */#ifdef DIRECT      var = log (v);      accept =        LNFACT (m) + LNFACT (n - m) - LNFACT (ix) - LNFACT (n - ix)        + (ix - m) * log (p / q);#else /* SQUEEZE METHOD */      /* More efficient determination of whether v < f(x)/f(M) */      k = abs (ix - m);      if (k <= FAR_FROM_MEAN)        {          /*            * If ix near m (ie, |ix-m|<FAR_FROM_MEAN), then do           * explicit evaluation using recursion relation for f(x)           */          double g = (n + 1) * s;          double f = 1.0;          var = v;          if (m < ix)            {              int i;              for (i = m + 1; i <= ix; i++)                {                  f *= (g / i - s);                }            }          else if (m > ix)            {              int i;              for (i = ix + 1; i <= m; i++)                {                  f /= (g / i - s);                }            }          accept = f;        }      else        {          /* If ix is far from the mean m: k=ABS(ix-m) large */          var = log (v);          if (k < npq / 2 - 1)            {              /* "Squeeze" using upper and lower bounds on               * log(f(x)) The squeeze condition was derived               * under the condition k < npq/2-1 */              double amaxp =                k / npq * ((k * (k / 3.0 + 0.625) + (1.0 / 6.0)) / npq + 0.5);              double ynorm = -(k * k / (2.0 * npq));              if (var < ynorm - amaxp)                goto Finish;              if (var > ynorm + amaxp)                goto TryAgain;            }          /* Now, again: do the test log(v) vs. log f(x)/f(M) */#if USE_EXACT          /* This is equivalent to the above, but is a little (~20%) slower */          /* There are five log's vs three above, maybe that's it? */          accept = LNFACT (m) + LNFACT (n - m)            - LNFACT (ix) - LNFACT (n - ix) + (ix - m) * log (p / q);#else /* USE STIRLING */          /* The "#define Stirling" above corresponds to the first five           * terms in asymptoic formula for           * log Gamma (y) - (y-0.5)log(y) + y - 0.5 log(2*pi);           * See Abramowitz and Stegun, eq 6.1.40           */          /* Note below: two Stirling's are added, and two are           * subtracted.  In both K+S, and in the ranlib           * implementation, all four are added.  I (jt) believe that           * is a mistake -- this has been confirmed by personal           * correspondence w/ Dr. Kachitvichyanukul.  Note, however,           * the corrections are so small, that I couldn't find an           * example where it made a difference that could be           * observed, let alone tested.  In fact, define'ing Stirling           * to be zero gave identical results!!  In practice, alv is           * O(1), ranging 0 to -10 or so, while the Stirling           * correction is typically O(10^{-5}) ...setting the           * correction to zero gives about a 2% performance boost;           * might as well keep it just to be pendantic.  */          {            double x1 = ix + 1.0;            double w1 = n - ix + 1.0;            double f1 = fm + 1.0;            double z1 = n + 1.0 - fm;            accept = xm * log (f1 / x1) + (n - m + 0.5) * log (z1 / w1)              + (ix - m) * log (w1 * p / (x1 * q))              + Stirling (f1) + Stirling (z1) - Stirling (x1) - Stirling (w1);          }#endif#endif        }      if (var <= accept)        {          goto Finish;        }      else        {          goto TryAgain;        }    }Finish:  return (pp > 0.5) ? n - ix : ix;}

⌨️ 快捷键说明

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