public static double GammaQImp(double a, double x) { const Routine routine = Routine.Q; // special values if (x == 0) { return(1.0); } if (double.IsInfinity(x)) { return(0.0); } if (a == 0) { return(0); } if (a == 1.0) { return(Math.Exp(-x)); } // Process small a, large x with asymptotic series, which is faster than CF if (x >= Asym_MinLargeZ(a)) { return(Asym_SeriesLargeZ(a, x) * PrefixRegularized(a, x) / x); } if (a == 0.5) { return(Math.Exp(-x) * Math2.Erfcx(Math.Sqrt(x))); } // is a small if ((a < 30) && (x >= a + 1) && (x < DoubleLimits.MaxLogValue)) { double frac = a - Math.Floor(a); if (frac == 0) { return(IntegerA(routine, a, x)); } else if (frac == 0.5) { return(HalfIntegerA(routine, a, x)); } } // // Begin by testing whether we're in the "bad" zone // where the result will be near 0.5 and the usual // series and continued fractions are slow to converge: // if (a > 20) { // The second limit below is chosen so that we use Temme's expansion // only if the result would be larger than about 10^-6. // Below that the regular series and continued fractions // converge OK, and if we use Temme's method we get increasing // errors from the dominant erfc term as it's (inexact) argument // increases in magnitude. double sigma = Math.Abs((x - a) / a); if (sigma < 0.4 || (a > 200 && 20 / a > sigma * sigma)) { double t = TemmeSymmetricAsym.GammaP(a, x); return((x >= a) ? t : 1 - t); } } if (x < (a + 1)) { if (a < 1) { return(SmallA(routine, a, x)); } return(1.0 - PrefixRegularized(a, x) * LowerSeries(a, x) / a); } // Use CF for x > a+1 return(PrefixRegularized(a, x) * UpperFraction(a, x)); }
// /// <summary> /// Calculates Ix(a,b) for large a and b (i.e >= 15) using an asymptotic expansion /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="lambda"></param> /// <returns></returns> static double LargeABAsymptotic(double a, double b, double lambda) { const double eps = 10 * DoubleLimits.MachineEpsilon; // relative tolerance Debug.Assert(a >= 15 && b >= 15, "Requires a >= 15 && b >= 15"); Debug.Assert(lambda >= 0); // lambda = (a + b)*(1-x) - b // This is BASYM from TOMS708. // See: Armido Didonato, Alfred Morris, Algorithm 708: // Significant Digit Computation of the Incomplete Beta Function Ratios, // ACM Transactions on Mathematical Software, // Volume 18, Number 3, 1992, pages 360-373. // // num is the maximum value that n can take in the do loop // ending at statement 50. it is required that num be even. // the arrays a0, b0, c, d have dimension num + 2. // const double e0 = 2.0 * Constants.RecipSqrtPI; // 2/Sqrt(PI) const double e1 = 0.5 * Constants.RecipSqrt2; // 0.5/Sqrt(2) const int num = 20; double[] a0 = new double[num + 2]; double[] b0 = new double[num + 2]; double[] c = new double[num + 2]; double[] d = new double[num + 2]; double h, r0, r1, w0; if (a < b) { h = a / b; r0 = 1.0 / (1.0 + h); r1 = (b - a) / b; w0 = 1.0 / Math.Sqrt(a * (1.0 + h)); } else { h = b / a; r0 = 1.0 / (1.0 + h); r1 = (b - a) / a; w0 = 1.0 / Math.Sqrt(b * (1.0 + h)); } //double f = -(a * Math2.Log1pmx( -lambda/a ) + b * Math2.Log1pmx(lambda/b)); double f = -(a * Math2.Log1p(-lambda / a) + b * Math2.Log1p(lambda / b)); double t = Math.Exp(-f); if (t == 0) { return(0); } double z0 = Math.Sqrt(f); double z = 0.5 * (z0 / e1); double z2 = f + f; a0[1] = (2.0 / 3.0) * r1; c[1] = -0.5 * a0[1]; d[1] = -c[1]; // double j0 = ( 0.5 / e0 ) * Math.Exp(z0*z0)*Math2.Erfc(z0); double j0 = (0.5 / e0) * Math2.Erfcx(z0); double j1 = e1; double sum2 = j0 + d[1] * w0 * j1; double s = 1.0; double h2 = h * h; double hn = 1.0; double w = w0; double znm1 = z; double zn = z2; for (int n = 2; n <= num; n += 2) { hn = h2 * hn; a0[n] = 2.0 * r0 * (1.0 + h * hn) / (n + 2.0); int np1 = n + 1; s = s + hn; a0[np1] = 2.0 * r1 * s / (n + 3.0); for (int i = n; i <= np1; i++) { double r = -0.5 * (i + 1.0); b0[1] = r * a0[1]; for (int m = 2; m <= i; m++) { double bsum = 0.0; int mm1 = m - 1; for (int j = 1; j <= mm1; j++) { int mmj = m - j; bsum += (j * r - mmj) * a0[j] * b0[mmj]; } b0[m] = r * a0[m] + bsum / m; } c[i] = b0[i] / (i + 1.0); double dsum = 0.0; int im1 = i - 1; for (int j = 1; j <= im1; j++) { dsum += d[i - j] * c[j]; } d[i] = -(dsum + c[i]); } j0 = e1 * znm1 + (n - 1.0) * j0; j1 = e1 * zn + n * j1; znm1 = z2 * znm1; zn = z2 * zn; w = w0 * w; double t0 = d[n] * w * j0; w = w0 * w; double t1 = d[np1] * w * j1; sum2 += (t0 + t1); if ((Math.Abs(t0) + Math.Abs(t1)) <= eps * sum2) { break; } } double u = StirlingGamma.GammaSeries(a + b) / (StirlingGamma.GammaSeries(a) * StirlingGamma.GammaSeries(b)); //Math.Exp(-Bcorr(a, b)); // below is the equivalent: // double p = a/(a+b); // double q = b/(a+b); // return Math.Sqrt(2*Math.PI*(a+b)/(a*b))*_Beta.PowerTerms(a, b, p, q, true); return(e0 * t * u * sum2); }
public static double TgammaImp(double a, double x) { const Routine routine = Routine.Upper; // special values if (x == 0) { return(Math2.Tgamma(a)); } if (double.IsInfinity(x)) { return(0.0); } // Γ(0,x) = E_1(x) if (a == 0) { return(Math2.Expint(1, x)); } if (a == 1.0) { return(Math.Exp(-x)); } // Process small a, large x with asymptotic series, which is faster than CF if (x >= Asym_MinLargeZ(a)) { return(Asym_SeriesLargeZ(a, x) * Prefix(a, x) / x); } if (a == 0.5) { return(Constants.SqrtPI * Math.Exp(-x) * Math2.Erfcx(Math.Sqrt(x))); } // is a small if ((a < 30) && (x >= a + 1) && (x < DoubleLimits.MaxLogValue)) { double frac = a - Math.Floor(a); if (frac == 0) { return(IntegerA(routine, a, x)); } else if (frac == 0.5) { return(HalfIntegerA(routine, a, x)); } } // Use the series if (x < (a + 1)) { if (a < 1) { return(SmallA(routine, a, x)); } return(TgammaMinusLowerSeries(a, x)); } // Use CF for x > a+1 return(Prefix(a, x, UpperFraction(a, x))); }