Пример #1
0
        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));
        }
Пример #2
0
        //
        /// <summary>
        /// Calculates Ix(a,b) for large a and b (i.e &gt;= 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);
        }
Пример #3
0
        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)));
        }