C#, Numerical Calculation - Gamma Function (Gamma Function) calculation method and source code

using System;

namespace Legalsoft.Truffer
{
    public class Gamma : Gauleg18
    {
        private const int ASWITCH = 100;
        private const double EPS = float.Epsilon;
        private const double FPMIN = float.MinValue;// / float.Epsilon;
        private double gln { get; set; }

        public double gammp(double a, double x)
        {
            if (x < 0.0 || a <= 0.0)
            {
                throw new Exception("bad args in gammp");
            }
            //if (x == 0.0)
            if (Math.Abs(x) <= float.Epsilon)
            {
                return 0.0;
            }
            else if ((int)a >= ASWITCH)
            {
                return gammpapprox(a, x, 1);
            }
            else if (x < a + 1.0)
            {
                return gser(a, x);
            }
            else
            {
                return 1.0 - gcf(a, x);
            }
        }

        public double gammq(double a, double x)
        {
            if (x < 0.0 || a <= 0.0)
            {
                throw new Exception("bad args in gammq");
            }
            //if (x == 0.0)
            if (Math.Abs(x) <= float.Epsilon)
            {
                return 1.0;
            }
            else if ((int)a >= ASWITCH)
            {
                return gammpapprox(a, x, 0);
            }
            else if (x < a + 1.0)
            {
                return 1.0 - gser(a, x);
            }
            else
            {
                return gcf(a, x);
            }
        }

        public double gser(double a, double x)
        {
            gln = Globals.gammln(a);
            double ap = a;
            double del = 1.0 / a;
                double sum = 1.0 / a;
            for (; ; )
            {
                ++ap;
                del *= x / ap;
                sum += del;
                if (Math.Abs(del) < Math.Abs(sum) * EPS)
                {
                    return sum * Math.Exp(-x + a * Math.Log(x) - gln);
                }
            }
        }

        public double gcf(double a, double x)
        {
            gln = Globals.gammln(a);
            double b = x + 1.0 - a;
            double c = 1.0 / FPMIN;
            double d = 1.0 / b;
            double h = d;
            for (int i = 1; ; i++)
            {
                double an = -i * (i - a);
                b += 2.0;
                d = an * d + b;
                if (Math.Abs(d) < FPMIN)
                {
                    d = FPMIN;
                }
                c = b + an / c;
                if (Math.Abs(c) < FPMIN)
                {
                    c = FPMIN;
                }
                d = 1.0 / d;
                double del = d * c;
                h *= del;
                if (Math.Abs(del - 1.0) <= EPS)
                {
                    break;
                }
            }
            return Math.Exp(-x + a * Math.Log(x) - gln) * h;
        }

        public double gammpapprox(double a, double x, int psig)
        {
            double a1 = a - 1.0;
            double lna1 = Math.Log(a1);
            double sqrta1 = Math.Sqrt(a1);
            gln = Globals.gammln(a);
            double xu;
            if (x > a1)
            {
                xu = Math.Max(a1 + 11.5 * sqrta1, x + 6.0 * sqrta1);
            }
            else
            {
                xu = Math.Max(0.0, Math.Min(a1 - 7.5 * sqrta1, x - 5.0 * sqrta1));
            }
            double sum = 0;
            for (int j = 0; j < ngau; j++)
            {
                double t = x + (xu - x) * y[j];
                sum += w[j] * Math.Exp(-(t - a1) + a1 * (Math.Log(t) - lna1));
            }
            double ans = sum * (xu - x) * Math.Exp(a1 * (lna1 - 1.0) - gln);
            return (psig != 0 ? (x > a1 ? 1.0 - ans : -ans) : (x > a1 ? ans : 1.0 + ans));
        }

        public double invgammp(double p, double a)
        {
            double lna1 = 0.0;
            double afac = 0.0;
            double a1 = a - 1;
            const double EPS = 1.0e-8;
            gln = Globals.gammln(a);
            if (a <= 0.0)
            {
                throw new Exception("a must be pos in invgammap");
            }
            if (p >= 1.0)
            {
                return Math.Max(100.0, a + 100.0 * Math.Sqrt(a));
            }
            if (p <= 0.0)
            {
                return 0.0;
            }
            double t;
            double x;
            if (a > 1.0)
            {
                lna1 = Math.Log(a1);
                afac = Math.Exp(a1 * (lna1 - 1.0) - gln);
                double pp = (p < 0.5) ? p : 1.0 - p;
                t = Math.Sqrt(-2.0 * Math.Log(pp));
                x = (2.30753 + t * 0.27061) / (1.0 + t * (0.99229 + t * 0.04481)) - t;
                if (p < 0.5)
                {
                    x = -x;
                }
                x = Math.Max(1.0e-3, a * Math.Pow(1.0 - 1.0 / (9.0 * a) - x / (3.0 * Math.Sqrt(a)), 3));
            }
            else
            {
                t = 1.0 - a * (0.253 + a * 0.12);
                if (p < t)
                {
                    x = Math.Pow(p / t, 1.0 / a);
                }
                else
                {
                    x = 1.0 - Math.Log(1.0 - (p - t) / (1.0 - t));
                }
            }
            for (int j = 0; j < 12; j++)
            {
                if (x <= 0.0)
                {
                    return 0.0;
                }
                double err = gammp(a, x) - p;
                if (a > 1.0)
                {
                    t = afac * Math.Exp(-(x - a1) + a1 * (Math.Log(x) - lna1));
                }
                else
                {
                    t = Math.Exp(-x + a1 * Math.Log(x) - gln);
                }
                double u = err / t;
                x -= (t = u / (1.0 - 0.5 * Math.Min(1.0, u * ((a - 1.0) / x - 1))));
                if (x <= 0.0)
                {
                    x = 0.5 * (x + t);
                }
                if (Math.Abs(t) < EPS * x)
                {
                    break;
                }
            }
            return x;
        }
    }
}

Guess you like

Origin blog.csdn.net/beijinghorn/article/details/131788582