Ejemplo n.º 1
0
        /// <summary>
        /// Returns the Beta function
        /// <para>B(a,b) = Γ(a)*Γ(b)/Γ(a+b)</para>
        /// </summary>
        /// <param name="a">Requires finite a > 0</param>
        /// <param name="b">Requires finite b > 0</param>
        public static double Beta(double a, double b)
        {
            double c = a + b;

            if ((!(a > 0) || double.IsInfinity(a))
                || (!(b > 0) || double.IsInfinity(b))) {
                Policies.ReportDomainError("Beta(a: {0}, b: {1}): Requires finite a,b > 0", a, b);
                return double.NaN;
            }
            if (double.IsInfinity(c)) {
                Policies.ReportDomainError("Beta(a: {0}, b: {1}): Requires finite c == a+b: c = {2}", a, b, c);
                return double.NaN;
            }


            // Special cases:
            if ((c == a) && (b < DoubleLimits.MachineEpsilon))
                return Math2.Tgamma(b);
            if ((c == b) && (a < DoubleLimits.MachineEpsilon))
                return Math2.Tgamma(a);
            if (b == 1)
                return 1 / a;
            if (a == 1)
                return 1 / b;

            // B(a,b) == B(b, a)
            if (a < b)
                Utility.Swap(ref a, ref b);

            // from this point a >= b

            if (a < 1) {
                // When x < TinyX, Γ(x) ~ 1/x 
                const double SmallX = DoubleLimits.MachineEpsilon / Constants.EulerMascheroni;
                if ( a < SmallX ) {
                    if (c < SmallX)
                        return (c / a) / b;
                    Debug.Assert(c <= GammaSmall.UpperLimit);
                    return 1/(GammaSmall.Tgamma(c) * a * b);
                }
                if ( b < SmallX ) 
                    return Tgamma(a) / (b * Tgamma(c));
                return Tgamma(a) * ( Tgamma(b)/Tgamma(c) );
            }

            // Our Stirling series is more accurate than Lanczos
            if (a >= StirlingGamma.LowerLimit ) {
                if (b >= StirlingGamma.LowerLimit)
                    return StirlingGamma.Beta(a, b);

                // for large a, Γ(a)/Γ(a + b) ~ a^-b, so don't underflow too soon
                if (b < 1 || b * Math.Log(a) <= -DoubleLimits.MinLogValue)
                    return Tgamma(b) * StirlingGamma.TgammaDeltaRatio(a, b);

                // fall through for very large a small 1 < b < LowerLimit

            }


            return Lanczos.Beta(a, b);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Returns Log(Beta(a,b))
        /// </summary>
        /// <param name="a">Requires finite a > 0</param>
        /// <param name="b">Requires finite b > 0</param>
        public static double LogBeta(double a, double b)
        {
            double c = a + b;

            if ((!(a > 0) || double.IsInfinity(a))
                || (!(b > 0) || double.IsInfinity(b))) {
                Policies.ReportDomainError("Beta(a: {0}, b: {1}): Requires finite a,b > 0", a, b);
                return double.NaN;
            }
            if (double.IsInfinity(c)) {
                Policies.ReportDomainError("Beta(a: {0}, b: {1}): Requires finite c == a+b: c = {2}", a, b, c);
                return double.NaN;
            }

            // Special cases:
            if ((c == a) && (b < DoubleLimits.MachineEpsilon))
                return Math2.Lgamma(b);
            if ((c == b) && (a < DoubleLimits.MachineEpsilon))
                return Math2.Lgamma(a);
            if (b == 1)
                return -Math.Log(a);
            if (a == 1)
                return -Math.Log(b);

            // B(a,b) == B(b, a)
            if (a < b)
                Utility.Swap(ref a, ref b);

            // from this point a >= b

            if (a < 1) {
                // When x < TinyX, Γ(x) ~ 1/x 
                const double SmallX = DoubleLimits.MachineEpsilon / Constants.EulerMascheroni;
                if (a < SmallX) {
                    if (c < SmallX) {
                        // In this range, Beta(a, b) ~= 1/a + 1/b 
                        // so, we won't have an argument overflow as long as 
                        // the min(a, b) >= 2*DoubleLimits.MinNormalValue

                        if (b < 2 * DoubleLimits.MinNormalValue)
                            return Math.Log(c / a) - Math.Log(b);

                        return Math.Log((c / a) / b);
                    }
                    Debug.Assert(c <= GammaSmall.UpperLimit);
                    return -Math.Log((GammaSmall.Tgamma(c) * a * b));
                }
                if (b < SmallX)
                    return Math.Log(Tgamma(a) / (b * Tgamma(c)));

                return Math.Log((Tgamma(a) / Tgamma(c)) * Tgamma(b));

            } else if (a >= StirlingGamma.LowerLimit) {

                if ( b >= StirlingGamma.LowerLimit )
                    return StirlingGamma.LogBeta(a, b);

                return StirlingGamma.LgammaDelta(a, b) + Lgamma(b);
            }

            return Lanczos.LogBeta(a, b);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Returns the Gamma Function = Γ(x)
        /// </summary>
        /// <param name="x">Tgamma function argument</param>
        public static double Tgamma(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("Tgamma(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x < 0)
                {
                    Policies.ReportDomainError("Tgamma(x: {0}): Requires x is not negative infinity", x);
                    return(double.NaN);
                }

                return(double.PositiveInfinity);
            }

            double result = 1;

            if (x <= 0)
            {
                if (IsInteger(x))
                {
                    Policies.ReportPoleError("Tgamma(x: {0}): Requires x != 0 and x be a non-negative integer", x);
                    return(double.NaN);
                }

                if (x >= -GammaSmall.UpperLimit)
                {
                    return(GammaSmall.Tgamma(x));
                }

                if (x <= -StirlingGamma.LowerLimit)
                {
                    const double MinX = -185;
                    if (x < MinX)
                    {
                        return(0);
                    }


                    // If we use the reflection formula directly for x < -NegMaxGamma, Tgamma will overflow;
                    // so, use recurrence to get x > -NegMaxGamma.
                    // The result is likely to be subnormal or zero.
                    double shiftedX       = x;
                    double recurrenceMult = 1.0;
                    double NegMaxGamma    = -(Math2.MaxFactorialIndex + 1);
                    while (shiftedX < NegMaxGamma)
                    {
                        recurrenceMult *= shiftedX;
                        shiftedX       += 1;
                    }

                    result = -(Math.PI / StirlingGamma.Tgamma(-shiftedX)) / (shiftedX * recurrenceMult * Math2.SinPI(shiftedX));
                    return(result);
                }

                // shift x to > 0:
                double product = 1;
                while (x < 0)
                {
                    product *= x++;
                }

                result /= product;

                // fall through
            }

            if (x < 1)
            {
                if (x <= GammaSmall.UpperLimit)
                {
                    result *= GammaSmall.Tgamma(x);
                }
                else
                {
                    // Γ(x) = Exp(LogΓ(x+1))/x
                    result *= Math.Exp(_Lgamma.Rational_1_3(x + 1, x, x - 1)) / x;
                }
            }
            else if (IsInteger(x) && (x <= Math2.FactorialTable.Length))
            {
                // Γ(n) = (n-1)!
                result *= Math2.FactorialTable[(int)x - 1];
            }
            else if (x < StirlingGamma.LowerLimit)
            {
                // ensure x is in [1, 3)
                // if x >= 3, use recurrence to shift x to [2, 3)

                while (x >= 3)
                {
                    result *= --x;
                }

                result *= Math.Exp(_Lgamma.Rational_1_3(x, x - 1, x - 2));
            }
            else
            {
                result *= StirlingGamma.Tgamma(x);
            }

            return(result);
        }