Пример #1
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)));
        }
Пример #2
0
        public static double ImpQ(double z, double p, double q)
        {
            Debug.Assert(q <= 0.5);

            if (q == 0)
            {
                return(DoubleLimits.MinNormalValue);
            }

#if EXTRA_DEBUG
            var gs = new Stack <double>();
#endif

            // Set outside limits
            // Since Q(a, z) > 1/2 for a >= z+1, so maxA < z+1
            double minA = DoubleLimits.MinNormalValue;
            double maxA = z + 1;
            if (maxA == z)
            {
                maxA = Math2.FloatNext(z);
            }


            double step   = 0.5;
            double aGuess = 0;

            // We can use the relationship between the incomplete
            // gamma function and the poisson distribution to
            // calculate an approximate inverse.
            // Mostly it is pretty accurate, except when a is small or q is tiny.

            // Case 1: a <= 1 : Inverse Cornish Fisher is unreliable in this area

            var e = Math.Exp(-z);
            if (q <= e)
            {
                if (q == e)
                {
                    return(1);
                }

                maxA = 1;

                // If a <= 1, P(a, x) = 1 - Q(a, x) >= (1-e^-x)^a
                var den   = (z > 0.5) ? Math2.Log1p(-Math.Exp(-z)) : Math.Log(-Math2.Expm1(-z));
                var limit = Math2.Log1p(-q) / den;
                if (limit < 1)
                {
                    minA = Math.Max(minA, limit);
                }

                step   = (1 - minA) * 0.125;
                aGuess = minA * (1 + step);

                if (minA < 0.20)
                {
                    // Use a first order approximation Q(a, z) at a=0
                    // Mathematica: Assuming[ a >= 0 && z > 0, FunctionExpand[Normal[Series[GammaRegularized[a, z], {a, 0, 1}]]]]
                    // q = -a * ExpIntegralEi[-z]
                    // The smaller a is the better the guess
                    // as z->0 term2/term1->1/2*Log[z], which is adequate for double precision

                    aGuess = -q / Math2.Expint(-z);
                    if (aGuess <= DoubleLimits.MachineEpsilon)
                    {
                        ExtraDebugLog(z, p, q, aGuess, aGuess, step, minA, maxA, 0, "");
                        return(aGuess);
                    }
                    step = aGuess * 0.125;
                }
                else if (minA >= 0.40)
                {
                    // Use a first order approximation Q(a, z) at a=1
                    // Mathematica: Assuming[ a >= 0 && z > 0, FunctionExpand[Normal[Series[GammaRegularized[a, z], {a, 1, 1}]]]]

                    var d = Math.Exp(-z) * (Math.Log(z) + Constants.EulerMascheroni) + Math2.Expint(1, z);
                    aGuess = (q - Math.Exp(-z)) / d + 1;
                }

                aGuess = Math.Max(aGuess, minA);
                aGuess = Math.Min(aGuess, maxA);

                var r = SolveA(z, p, q, aGuess, step, minA, maxA);

                ExtraDebugLog(z, p, q, r.Result, aGuess, step, minA, maxA, r.Iterations, "");

                return(r.Result);
            }


            // Case 2: a > 1 : Inverse Poisson Corning Fisher approximation improves as a->Infinity

            minA   = 1;
            aGuess = 0.5 + InversePoissonCornishFisher(z, q, p);
            if (aGuess > 100)
            {
                step = 0.01;
            }
            else if (aGuess > 10)
            {
                step = 0.01;
            }
            else
            {
                // our poisson approximation is weak.
                step = 0.05;
            }


#if EXTRA_DEBUG
            double og = aGuess;
#endif

            aGuess = Math.Max(aGuess, minA);
            aGuess = Math.Min(aGuess, maxA);

#if EXTRA_DEBUG
            if (og != aGuess)
            {
                gs.Push(og);
            }
#endif

            // For small values of q, our Inverse Poisson Cornish Fisher guess can be way off
            // Try to come up with a better guess
            if (q < DoubleLimits.MachineEpsilon)
            {
                step *= 10;
                if (Math.Abs((aGuess - 1) / z) < 1.0 / 8)
                {
#if EXTRA_DEBUG
                    double old = aGuess;
#endif
                    (aGuess, _) = RefineQ_SmallA_LargeZ(z, p, q, aGuess);
#if EXTRA_DEBUG
                    if (old != aGuess)
                    {
                        gs.Push(old);
                    }
#endif
                }
            }

            var rr = SolveA(z, p, q, aGuess, step, minA, maxA);

#if EXTRA_DEBUG
            if (rr.Iterations > 6)
            {
                string guesses = (gs.Count == 0) ? string.Empty : ", og: (" + string.Join(", ", gs) + ")";
                ExtraDebugLog(z, p, q, rr.Result, aGuess, step, minA, maxA, rr.Iterations, guesses);
            }
#endif

            return(rr.Result);
        }