Пример #1
0
        /// <summary>
        /// Implements the Exponential Series for integer n
        /// <para>Sum = ((-z)^(n-1)/(n-1)!) * (ψ(n)-Log(z)) - Σ( (k==n-1) ? 0 : ((-1)^k * z^k)/((k-n+1) * k!)) k={0,∞}</para>
        /// </summary>
        /// <param name="n"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        /// <see href="http://functions.wolfram.com/GammaBetaErf/ExpIntegralE/06/01/04/01/02/0005/"/>
        public static double En_Series(int n, double z)
        {
            Debug.Assert(n > 0);

            const double DefaultTolerance = 2 * DoubleLimits.MachineEpsilon;

            // the sign of the summation is negative
            // so negate these
            double sum  = (n > 1) ? -1.0 / (1.0 - n) : 0;
            double term = -1.0;

            int k = 1;

            for (; k < n - 1; k++)
            {
                term *= -z / k;
                if (term == 0)
                {
                    break;
                }
                sum += term / (k - n + 1);
            }

            sum += Math.Pow(-z, n - 1) * (Math2.Digamma(n) - Math.Log(z)) / Math2.Factorial(n - 1);

            if (term == 0)
            {
                return(sum);
            }

            // skip n-1 term
            term *= -z / k;

            for (int i = 0; i < Policies.MaxSeriesIterations; i++)
            {
                double prevSum = sum;

                k++;
                term *= -z / k;
                double delta = term / (k - n + 1);
                sum += delta;

                if (Math.Abs(delta) <= Math.Abs(prevSum) * DefaultTolerance)
                {
                    return(sum);
                }
            }


            Policies.ReportConvergenceError("Series did not converge in {0} iterations", Policies.MaxSeriesIterations);
            return(double.NaN);
        }
Пример #2
0
        /// <summary>
        /// Returns Y{n}(z) for positive integer order and z &lt;= sqrt(eps)
        /// </summary>
        /// <param name="n"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        static DoubleX YN_SmallArg(int n, double z)
        {
            Debug.Assert(n >= 0);
            Debug.Assert(z <= DoubleLimits.RootMachineEpsilon._2);


            //
            // See http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/06/01/04/01/02/
            //
            // Note that when called we assume that x < epsilon and n is a positive integer.
            //

            if (n == 0)
            {
                return(new DoubleX((2 / Math.PI) * (Math.Log(z / 2) + Constants.EulerMascheroni)));
            }

            if (n == 1)
            {
                //return (z / Math.PI) * Math.Log(z / 2) - 2 / (Math.PI * z) - (z / (2 * Math.PI)) * (1 - 2 * Constants.EulerMascheroni);
                return(new DoubleX((z / 2 * (2 * Math.Log(z / 2) - (1 - 2 * Constants.EulerMascheroni)) - 2 / z) / Math.PI));
            }

            if (n == 2)
            {
                double z2 = z * z;
                return(new DoubleX((z2 / 4 * (Math.Log(z / 2) - (3 - 4 * Constants.EulerMascheroni)) - (4 / z2)) / Math.PI));
            }

            // To double check the maximum x value on Mathematica
            // BesselYDiff[n_, x_] := Block[{ a = BesselY[n, x], b = -(Factorial[n - 1]/Pi)*(x/2)^(-n)}, (b - a)/a]
            // Table[Block[{eps = 2^-52}, N[BesselYDiff[n, Sqrt[eps]], 20]], {n, 3, 100}]

            double num = -((Math2.Factorial(n - 1) / Math.PI));
            double den = Math.Pow(z / 2, n);

            if (double.IsInfinity(num) || den == 0)
            {
                return(DoubleX.NegativeInfinity);
            }

            DoubleX result = new DoubleX(num) / new DoubleX(den);

            return(result);
        }
Пример #3
0
        /// <summary>
        /// Returns the rising factorial:
        /// <para>(x)_{n} = Γ(x+n)/Γ(x)</para>
        /// </summary>
        /// <returns>
        /// <para> if n > 0, x*(x+1)*(x+2)...*(x+(n-1))</para>
        /// <para> if n == 0, 1</para>
        /// <para> if n &lt; 0, 1/((x-1)*(x-2)*...*(x-n))</para>
        /// </returns>
        public static double FactorialRising(double x, int n)
        {
            // This section uses the following notation:
            // falling factorial: (x)_{_n}
            // rising factorial: (x)_{n}

            // Standard eqn for the rising factorial:
            // (x)_{n} = Γ(x+n)/Γ(x)

            double result = double.NaN;

            // (x)_{0} = 1
            if (n == 0)
            {
                return(1);
            }

            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("FactorialRising(x: {0}, n: {1}): Requires x not NaN", x, n);
                return(double.NaN);
            }


            if (n == int.MinValue)
            {
                // avoid -int.MinValue error
                // this will overflow/underflow, but here it is
                if (x > 0 && IsInteger(x) && (x + n) <= 0)
                {
                    Policies.ReportPoleError("FactorialRising(x: {0}, n: {1}): Divide by Zero", x, n);
                    return(double.NaN);
                }

                // (x)_{n} = (x)_{n+1}/(x+n)
                return(FactorialRising(x, n + 1) / (x + n));
            }


            if (x == 0)
            {
                // (0)_{n} = 0, n > 0
                if (n > 0)
                {
                    return(0);
                }

                // (0)_{-n} = (-1)^n / n!
                result = Math2.Factorial(-n);
                if (IsOdd(n))
                {
                    result = -result;
                }
                return(1 / result);
            }

            if (x < 0)
            {
                // For x less than zero, we really have a falling
                // factorial, modulo a possible change of sign.
                // Note: the falling factorial isn't defined for negative n
                // so cover that first

                if (n < -x)
                {
                    // handle (n < 0 || ( n > 0 && n < -x)) directly
                    // (x)_{n} = Γ(1-x)/Γ(1-x-n)
                    result = Math2.TgammaDeltaRatio(1 - x, -n);
                }
                else
                {
                    Debug.Assert(n > 0);
                    result = FactorialFalling(-x, n);
                }

                if (IsOdd(n))
                {
                    result = -result;
                }

                return(result);
            }


            // x > 0

            // standard case: (n > 0 || (n < 0 && x > -n))
            // (x)_{n} = Γ(x+n)/Γ(x)
            if (-x < n)
            {
                return(1 / Math2.TgammaDeltaRatio(x, n));
            }

            Debug.Assert((x + n) <= 0);

            // (x)_{n} = (-1)^n *  Γ(1-x)/Γ(1-x-n)
            if (x < 1)
            {
                result = TgammaDeltaRatio(1 - x, -n);
                if (IsOdd(n))
                {
                    result = -result;
                }
                return(result);
            }

            // x >= 1
            result = FactorialFalling(x - 1, -n);
            if (result == 0)
            {
                if (IsInteger(x))
                {
                    Policies.ReportPoleError("FactorialRising(x: {0}, n: {1}): Divide by Zero", x, n);
                    return(double.NaN);
                }

                return(double.PositiveInfinity);
            }
            return(1 / result);
        }
Пример #4
0
        /// <summary>
        /// Returns the falling factorial:
        /// <para>(x)_{_n} = x*(x-1)*(x-2)...*(x-(n-1)) = Γ(x+1)/Γ(x-n+1)</para>
        /// </summary>
        /// <param name="x"></param>
        /// <param name="n">Requires n ≥ 0</param>
        /// <returns>
        /// <para>if n > 0, x*(x-1)*(x-2)...*(x-(n-1))</para>
        /// <para>if n == 0, 1</para>
        /// </returns>
        public static double FactorialFalling(double x, int n)
        {
            // This section uses the following notation:
            // falling factorial: (x)_{_n}
            // rising factorial: (x)_{n}

            // Standard eqn for the falling factorial:
            // (x)_{_n} = Γ(x+1)/Γ(x+1-n)

            double result = double.NaN;

            // (x)_{_0} = 1
            if (n == 0)
            {
                return(1);
            }

            if ((double.IsNaN(x)) ||
                (n < 0))
            {
                Policies.ReportDomainError("FactorialFalling(x: {0}, n: {1}): Requires finite x; n >= 0", x, n);
                return(double.NaN);
            }

            if (n == 1)
            {
                return(x);
            }

            if (x == 0)
            {
                return(0);
            }
            if (x < 0)
            {
                //
                // For x < 0 we really have a rising factorial
                // modulo a possible change of sign:
                // (x)_{_n} = (-1)^n * (-x)_{n}
                result = FactorialRising(-x, n);
                return(IsOdd(n) ? -result : result);
            }

            // standard case: Γ(x+1)/Γ(x+1-n)
            if (x > n - 1)
            {
                // (n)_{_n} = n!
                if (x == n)
                {
                    return(Math2.Factorial(n));
                }

                // (n)_{_n+1} = n!, n>1
                if (x == n + 1)
                {
                    Debug.Assert(n > 1);
                    return(Math2.Factorial(n + 1));
                }

                return(Math2.TgammaDeltaRatio(x + 1, -n));
            }


            Debug.Assert(x <= n - 1);


            if (x < 1)
            {
                int m = n - 1;

                double MaxFactorial = Math2.MaxFactorialIndex;
                if (m < MaxFactorial)
                {
                    result = x / TgammaDeltaRatio(1 - x, m);
                }
                else
                {
                    // try not to overflow/underflow too soon

                    double m2 = m - MaxFactorial;
                    result = (x / TgammaDeltaRatio(1 - x, MaxFactorial)) / TgammaDeltaRatio(1 - x + MaxFactorial, m2);
                }


                return(IsOdd(m) ? -result : result);
            }

            //
            // x+1-n will be negative and TgammaDeltaRatio won't
            // handle it, split the product up into three parts:
            //
            double xp1 = x + 1;
            int    n2  = (int)xp1;

            if (n2 == xp1)
            {
                return(0);
            }
            result  = Math2.TgammaDeltaRatio(xp1, -n2);
            x      -= n2;
            result *= x;
            ++n2;
            if (n2 < n)
            {
                result *= FactorialFalling(x - 1, n - n2);
            }
            return(result);
        }