Пример #1
0
        /// <summary>
        /// Returns the partial derivative of IBeta(a,b,x) with respect to <paramref name="x"/>
        /// <para>IbetaDerivative(a, b, x) = ∂( I<sub>x</sub>(a,b) )/∂x = (1-x)^(b-1)*x^(a-1)/B(a,b)</para>
        /// </summary>
        /// <param name="a">Requires a ≥ 0 and not both a and b are zero</param>
        /// <param name="b">Requires b ≥ 0 and not both a and b are zero</param>
        /// <param name="x">Defined for 0 ≤ x ≤ 1</param>
        public static double IbetaDerivative(double a, double b, double x)
        {
            if (!(a > 0) ||
                !(b > 0) ||
                !(x >= 0 && x <= 1))
            {
                Policies.ReportDomainError("IbetaDerivative(a: {0}, b: {1}, x: {2}): Requires a,b >= 0; x in [0,1]", a, b, x);
                return(double.NaN);
            }

            //
            // Now the corner cases:
            //
            if (x == 0)
            {
                if (a > 1)
                {
                    return(0);
                }
                if (a == 1)
                {
                    return(1 / Math2.Beta(a, b));
                }

                return(double.PositiveInfinity);
            }
            if (x == 1)
            {
                if (b > 1)
                {
                    return(0);
                }
                if (b == 1)
                {
                    return(1 / Math2.Beta(a, b));
                }

                return(double.PositiveInfinity);
            }

            // handle denorm x
            if (x < DoubleLimits.MinNormalValue)
            {
                double logValue = a * Math.Log(x) + b * Math2.Log1p(-x) - Math.Log(x * (1 - x));
                logValue -= Math2.LogBeta(a, b);
                double result = Math.Exp(logValue);
#if EXTRA_DEBUG
                Debug.WriteLine("IbetaDerivative(a: {0}, b: {1}, x: {2}): Denorm = {3}", a, b, x, result);
#endif
                return(result);
            }

            //
            // Now the regular cases:
            //
            double mult = (1 - x) * x;
            double f1   = _Ibeta.PowerTerms(a, b, x, 1 - x, true, 1 / mult);

            return(f1);
        }
Пример #2
0
        /// <summary>
        /// Returns the binomial coefficient
        /// <para>BinomialCoefficient(n,k) = n! / (k!(n-k)!)</para>
        /// </summary>
        /// <param name="n">Requires n ≥ 0</param>
        /// <param name="k">Requires k in [0,n]</param>
        public static double BinomialCoefficient(int n, int k)
        {
            if ((n < 0) ||
                (k < 0 || k > n))
            {
                Policies.ReportDomainError("BinomialCoefficient(n: {0},k: {1}): Requires n >= 0; k in [0,n]", n, k);
                return(double.NaN);
            }

            if (k == 0 || k == n)
            {
                return(1);
            }
            if (k == 1 || k == n - 1)
            {
                return(n);
            }

            double result;

            if (n < Math2.FactorialTable.Length)
            {
                // Use fast table lookup:
                result = (FactorialTable[n] / FactorialTable[n - k]) / FactorialTable[k];
            }
            else
            {
                // Use the beta function:
                if (k < n - k)
                {
                    result = k * Math2.Beta(k, n - k + 1);
                }
                else
                {
                    result = (n - k) * Math2.Beta(k + 1, n - k);
                }

                if (result == 0)
                {
                    return(double.PositiveInfinity);
                }

                result = 1 / result;
            }
            // convert to nearest integer:
            return(Math.Ceiling(result - 0.5));
        }
Пример #3
0
        /// <summary>
        /// Returns the Incomplete Beta Complement
        /// <para>Betac(a, b, x) = B(a,b) - B<sub>x</sub>(a,b)</para>
        /// </summary>
        /// <param name="a">Requires a ≥ 0 and not (a == b == 0)</param>
        /// <param name="b">Requires b ≥ 0 and not (a == b == 0)</param>
        /// <param name="x">0 ≤ x ≤ 1</param>
        public static double Betac(double a, double b, double x)
        {
            if ((!(a > 0) || double.IsInfinity(a)) ||
                (!(b > 0) || double.IsInfinity(b)) ||
                !(x >= 0 && x <= 1))
            {
                Policies.ReportDomainError("Betac(a: {0}, b: {1}, x: {2}): Requires finite a,b > 0; x in [0,1]", a, b, x);
                return(double.NaN);
            }

            // special values
            if (x == 0)
            {
                return(Math2.Beta(a, b));
            }
            if (x == 1)
            {
                return(0);
            }

            return(_Ibeta.BetaImp(a, b, x, true));
        }
Пример #4
0
        static double IbetaLargeASmallBSeries(double a, double b, double x, double y, bool normalised, double mult)
        {
            Debug.Assert(a >= 15, "Requires a >= 15: a= " + a);
            Debug.Assert(b >= 0 && b <= 1, "Requires b >= 0 && b <= 1: b= " + b);

            Debug.Assert(mult >= 0);

            //
            // Routine for a > 15, b < 1
            //
            // Begin by figuring out how large our table of Pn's should be,
            // quoted accuracies are "guestimates" based on empiracal observation.
            // Note that the table size should never exceed the size of our
            // tables of factorials.
            //

            const int Pn_size = 30; // 16-20 digit accuracy


            //
            // This is DiDonato and Morris's BGRAT routine, see Eq's 9 through 9.6.
            //
            // Some values we'll need later, these are Eq 9.1:
            //
            double bm1 = b - 1;
            double t   = a + bm1 / 2;
            double lx  = (y < 0.35) ? Math2.Log1p(-y) : Math.Log(x);
            double u   = -t * lx;
            // and from from 9.2:

            double prefix;
            double j; // = Math2.GammaQ(b, u) / IgammaPrefixRegularized(b, u);

            if (-u > DoubleLimits.MinLogValue)
            {
                double xpt = (y < 0.35) ? Math.Exp(-u) : Math.Pow(x, t);

                // h = u^b * e^-u
                double h = Math.Pow(u, b) * xpt; //IgammaPrefix(b, u);
                j = Math2.Tgamma(b, u) / h;

                prefix = Math.Pow(-lx, b) * xpt;
                if (normalised)
                {
                    prefix /= Math2.Beta(a, b);
                }
                prefix *= mult;
            }
            else
            {
                // if we use the previous equation, we'll end up with denorm/denorm or 0/0
                // So, use the asymptotic expansion of Γ(a, x) and cancel terms
                // Γ(b, u) = e^-u * u^(a-1) * TgammaAsymSeries(b, u)
                j = _Igamma.Asym_SeriesLargeZ(b, u) / u;

                // use logs, though we'll probably get zero
                double lVal = -u + Math.Log(Math.Pow(-lx, b) * mult);
                if (normalised)
                {
                    lVal -= Math2.LogBeta(a, b);
                }
                prefix = Math.Exp(lVal);
            }

            if (prefix == 0)
            {
                return(0);
            }

            //
            // now we need the quantity Pn, unfortunatately this is computed
            // recursively, and requires a full history of all the previous values
            // so no choice but to declare a big table and hope it's big enough...
            //
            double[] p = new double[Pn_size];
            p[0] = 1;  // see 9.3.


            //
            // Now we can start to pull things together and evaluate the sum in Eq 9:
            //
            double sum = j;  // Value at N = 0
            // some variables we'll need:
            uint   tnp1 = 1; // 2*N+1
            double lx2  = lx / 2;

            lx2 *= lx2;
            double lxp = 1;
            double t4  = 4 * t * t;
            double b2n = b;

            for (int n = 1; n < p.Length; ++n)
            {
                /*
                 * // debugging code, enable this if you want to determine whether
                 * // the table of Pn's is large enough...
                 * //
                 * static int max_count = 2;
                 * if(n > max_count)
                 * {
                 *  max_count = n;
                 *  Debug.WriteLine("Max iterations in BGRAT was {0}", n);
                 * }
                 */
                //
                // begin by evaluating the next Pn from Eq 9.4:
                //
                tnp1 += 2;
                p[n]  = 0;
                double mbn  = b - n;
                int    tmp1 = 3;
                for (int m = 1; m < n; ++m)
                {
                    mbn   = m * b - n;
                    p[n] += mbn * p[n - m] / Math2.FactorialTable[tmp1];
                    tmp1 += 2;
                }
                p[n] /= n;
                p[n] += bm1 / Math2.FactorialTable[tnp1];
                //
                // Now we want Jn from Jn-1 using Eq 9.6:
                //
                j    = (b2n * (b2n + 1) * j + (u + b2n + 1) * lxp) / t4;
                lxp *= lx2;
                b2n += 2;
                //
                // pull it together with Eq 9:
                //
                double r = p[n] * j;

                double prevSum = sum;
                sum += r;
                if (prevSum == sum)
                {
                    break;
                }
            }

            return(prefix * sum);
        }
Пример #5
0
        /// <summary>
        /// Implementation for the non-regularized cases: Bx(a,b) and B(a,b)-Bx(a,b)
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="x"></param>
        /// <param name="invert"></param>
        /// <returns></returns>
        public static double BetaImp(double a, double b, double x, bool invert)
        {
            Debug.Assert(a > 0);
            Debug.Assert(b > 0 && b < int.MaxValue);
            Debug.Assert(x > 0 && x < 1);

            const bool normalised = false;
            double     fract;
            double     y = 1 - x;


            if (a <= 1 && b <= 1)
            {
                if (x > 0.5)
                {
                    Utility.Swap(ref a, ref b);
                    Utility.Swap(ref x, ref y);
                    invert = !invert;
                }

                // Both a,b < 1:
                if ((a >= Math.Min(0.2, b)) || (Math.Pow(x, a) <= 0.9))
                {
                    fract = Series1(a, b, x, y, false);
                    return((invert) ? Math2.Beta(a, b) - fract : fract);
                }


                Utility.Swap(ref a, ref b);
                Utility.Swap(ref x, ref y);
                invert = !invert;

                if (y >= 0.3)
                {
                    fract = Series1(a, b, x, y, false);
                    return((invert) ? Math2.Beta(a, b) - fract : fract);
                }

                // Sidestep on a, and then use the series representation:
                double prefix = RisingFactorialRatio(a + b, a, 20);
                fract  = IbetaAStep(a, b, x, y, 20, normalised);
                fract += IbetaLargeASmallBSeries(a + 20, b, x, y, normalised, prefix);
                return((invert) ? Math2.Beta(a, b) - fract : fract);
            }


            if (a <= 1 || b <= 1)
            {
                if (x > 0.5)
                {
                    Utility.Swap(ref a, ref b);
                    Utility.Swap(ref x, ref y);
                    invert = !invert;
                }

                // One of a, b < 1 only:
                if ((b <= 1) || ((x < 0.1) && (Math.Pow(b * x, a) <= 0.7)))
                {
                    fract = Series1(a, b, x, y, false);
                    return((invert) ? Math2.Beta(a, b) - fract : fract);
                }

                Utility.Swap(ref a, ref b);
                Utility.Swap(ref x, ref y);
                invert = !invert;

                if (y >= 0.3)
                {
                    fract = Series1(a, b, x, y, false);
                    return((invert) ? Math2.Beta(a, b) - fract : fract);
                }

                if (a >= 15)
                {
                    fract = IbetaLargeASmallBSeries(a, b, x, y, normalised, 1);
                    return((invert) ? Math2.Beta(a, b) - fract : fract);
                }

                // Sidestep to improve errors:
                double prefix = RisingFactorialRatio(a + b, a, 20);
                fract  = IbetaAStep(a, b, x, y, 20, normalised);
                fract += IbetaLargeASmallBSeries(a + 20, b, x, y, normalised, prefix);
                return((invert) ? Math2.Beta(a, b) - fract : fract);
            }


            // Both a,b >= 1:
            double lambda = (a < b) ? a - (a + b) * x : (a + b) * y - b;

            if (lambda < 0)
            {
                Utility.Swap(ref a, ref b);
                Utility.Swap(ref x, ref y);
                invert = !invert;
            }

            if (b >= 40)
            {
                fract = Fraction2(a, b, x, y, normalised);
                return(invert ? Math2.Beta(a, b) - fract : fract);
            }

            /*
             * if( IsInteger(a) && IsInteger(b)) {
             *  // relate to the binomial distribution and use a finite sum:
             *  double k = a - 1;
             *  double n = b + k;
             *  fract = binomial_ccdf(n, k, x, y);
             *  if ( !invert)
             *      return Math2.Beta(a, b) * fract;
             *  return Math2.Beta(a, b) * (1.0 - fract);
             * }
             */

            if (b * x <= 0.7)
            {
                fract = Series1(a, b, x, y, false);
                return((invert) ? Math2.Beta(a, b) - fract : fract);
            }

            if (a > 15)
            {
                // sidestep so we can use the series representation:
                int n = (int)Math.Floor(b);
                if (n == b)
                {
                    --n;
                }
                double bbar   = b - n;
                double prefix = RisingFactorialRatio(a + bbar, bbar, n);
                fract  = IbetaAStep(bbar, a, y, x, n, normalised);
                fract += IbetaLargeASmallBSeries(a, bbar, x, y, normalised, 1);
                fract /= prefix;
                return(invert ? Math2.Beta(a, b) - fract : fract);
            }


            fract = Fraction2(a, b, x, y, normalised);
            return(invert ? Math2.Beta(a, b) - fract : fract);
        }