Пример #1
0
        /// <summary>
        /// Compute K{v}(x) and K{v+1}(x). Approximately O(v).
        /// <para>Multiply K{v}(x) and K{v+1}(x) by 2^binaryScale to get the true result</para>
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        static (double Kv, double Kvp1, int BScale) K_CF(double v, double x)
        {
            int binaryScale = 0;

            // Ku = K{u}, Ku1 = K{u+1}
            double Ku, Kup1;

            int    n = (int)Math.Floor(v + 0.5);
            double u = v - n;                              // -1/2 <= u < 1/2

            // for x in (0, 2], use Temme series
            // otherwise use scaled continued fraction K_CF2
            // to prevent Kv from underflowing too quickly for large x
            bool expScale = false;

            if (x <= 2)
            {
                (Ku, Kup1) = K_Temme(u, x);
            }
            else
            {
                expScale   = true;
                (Ku, Kup1) = K_CF2(u, x, expScale);
            }

            var(Kvp1, Kv, bScale) = Recurrence.ForwardK_B(u + 1, x, n, Kup1, Ku);


            if (expScale)
            {
                var sf = DoubleX.Ldexp(DoubleX.Exp(-x), bScale);
                Kv          = Kv * sf.Mantissa;
                Kvp1        = Kvp1 * sf.Mantissa;
                binaryScale = sf.Exponent;
            }
            else
            {
                binaryScale = bScale;
            }


            return(Kv, Kvp1, binaryScale);
        }
Пример #2
0
        //
        // Series form for BesselY as z -> 0, 
        // see: http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/06/01/04/01/01/0003/
        // This series is only useful when the second term is small compared to the first
        // otherwise we get catestrophic cancellation errors.
        //
        // Approximating tgamma(v) by v^v, and assuming |tgamma(-z)| < eps we end up requiring:
        // eps/2 * v^v(x/2)^-v > (x/2)^v or Math.Log(eps/2) > v Math.Log((x/2)^2/v)
        //


        /// <summary>
        /// Series form for Y{v}(x) as z -&gt; 0 and v is not an integer
        /// <para>Y{v}(z) = (-((z/2)^v Cos(πv) Γ(-v))/π)) * 0F1(; 1+v; -z^2/4) - ((z/2)^-v Γ(v))/π) * 0F1(; 1-v; -z^2/4)</para>
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        /// <seealso href="http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/26/01/02/0001/"/>
        public static DoubleX Y_SmallArgSeries(double v, double x)
        {
            Debug.Assert(v >= 0 && x >= 0);
            Debug.Assert( !Math2.IsInteger(v), "v cannot be an integer");


            DoubleX powDivGamma;
            double lnp = v * Math.Log(x / 2);
            if (v > DoubleLimits.MaxGamma || lnp < DoubleLimits.MinLogValue || lnp > DoubleLimits.MaxLogValue) {
                powDivGamma = DoubleX.Exp(lnp - Math2.Lgamma(v));
            } else {
                DoubleX p = Math.Pow(x / 2, v);
                DoubleX gam = Math2.Tgamma(v);
                powDivGamma = p / gam;
            }


            // Series 1: -((z/2)^-v Γ(v))/π) * 0F1(; 1-v; -z^2/4)

            double s1 = HypergeometricSeries.Sum0F1(1 - v, -x * x / 4);
            DoubleX result1 = -(s1/Math.PI)/ powDivGamma;

            // Series2: -((z/2)^v Cos(πv) Γ(-v))/π)) * 0F1(; 1+v; -z^2/4)
            // using the reflection formula: Γ(-v) = -π/(Γ(v) * v * Sin(π * v)) 
            // prefix = (z/2)^v * Cos(π * v)/(Γ(v) * v * Sin(π * v)) 
            // prefix = (z/2)^v * Cot(π * v) / (Γ(v) * v) 

            DoubleX result2 = DoubleX.Zero;
            double cot = Math2.CotPI(v);
            if (cot != 0) {
                double s2 = HypergeometricSeries.Sum0F1(v + 1, -x * x / 4);
                result2 = powDivGamma * cot * s2 / v; // Do this all in DoubleX
            }

            return (result1 - result2);
        }
Пример #3
0
        /// <summary>
        /// Returns K{n}(x) for integer order
        /// </summary>
        /// <param name="n"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double KN(int n, double x)
        {
            if (x < 0)
            {
                Policies.ReportDomainError("BesselK(v: {0}, x: {1}): Requires x >= 0 for real result", n, x);
                return(double.NaN);
            }
            if (x == 0)
            {
                return(double.PositiveInfinity);
            }


            // even function
            // K{-n}(z) = K{n}(z)
            if (n < 0)
            {
                n = -n;
            }

            if (n == 0)
            {
                return(K0(x));
            }

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

            double v = n;

            // Hankel is fast, and reasonably accurate, saving us from many recurrences.
            if (x >= HankelAsym.IKMinX(v))
            {
                return(HankelAsym.K(v, x));
            }

            // the uniform expansion is here as a last resort
            // to limit the number of recurrences, but it is less accurate.
            if (UniformAsym.IsIKAvailable(v, x))
            {
                return(UniformAsym.K(v, x));
            }

            // Since K{v}(x) has a (e^-x)/sqrt(x) multiplier
            // using recurrence can underflow too quickly for large x,
            // so, use a scaled version
            double result;

            if (x > 1)
            {
                double prev    = K0(x, true);
                double current = K1(x, true);

                // for large v and x this number can get very large
                // maximum observed K(1000,10) = 2^6211

                var(Kv, _, binaryScale) = Recurrence.ForwardK_B(1, x, n - 1, current, prev);


                // Compute: value * 2^(binaryScale) * e^-x

                if (x < -DoubleX.MinLogValue)
                {
                    DoubleX exs = DoubleX.Ldexp(DoubleX.Exp(-x), binaryScale);
                    result = Math2.Ldexp(Kv * exs.Mantissa, exs.Exponent);
                }
                else
                {
                    result = Math.Exp(-x + Math.Log(Kv) + binaryScale * Constants.Ln2);
                }
            }
            else
            {
                double prev    = K0(x);
                double current = K1(x);

                result = Recurrence.ForwardK(1, x, n - 1, current, prev).Kvpn;
            }


            return(result);
        }