C#,数值计算——分数阶的贝塞尔函数(Bessel functions of fractional order)源代码

分数阶微积分这一重要的数学分支,其诞生在1695年,几乎和经典微积分同时出现。那一年,德国数学家Leibniz 和法国数学家L'Hopital 通信,探讨当导数的阶变为1/2时,其意义是什么?当时Leibniz也不知道定义与意义,只是回复道:“”这会导致悖论,终有一天将会是一个很有用的结果”。分数阶微积分狭义上主要包括分数阶微分与分数阶积分,广义上同时包括分数阶差分与分数阶和商。由于近一些年分数阶微积分的理论成功应用到各大领域中,人们逐渐发现分数阶微积分能够刻画自然科学以及工程应用领域一些非经典现象。分数阶微积分比较热门领域包括:分数阶数值算法,分数阶同步等问题。

using System;

namespace Legalsoft.Truffer
{
    /// <summary>
    /// Bessel functions of fractional order
    /// </summary>
    public class Bessel
    {
        private const int NUSE1 = 7;
        private const int NUSE2 = 8;
        //private double[] c1 { get; set; } = new double[7]; NUSE1];
        //private double[] c2 { get; set; } = new double[8]; NUSE2];
        private static double[] c1 = {-1.142022680371168e0,6.5165112670737e-3,
      3.087090173086e-4,-3.4706269649e-6,6.9437664e-9,3.67795e-11,
      -1.356e-13};
        private static double[] c2 = {1.843740587300905e0,-7.68528408447867e-2,
      1.2719271366546e-3,-4.9717367042e-6,-3.31261198e-8,2.423096e-10,
      -1.702e-13,-1.49e-15};
        private double xjy { get; set; }
        private double nujy { get; set; }
        private double xik { get; set; }
        private double nuik { get; set; }
        private double xai { get; set; }
        private double xsph { get; set; }
        private double jo { get; set; }
        private double yo { get; set; }
        private double jpo { get; set; }
        private double ypo { get; set; }
        private double io { get; set; }
        private double ko { get; set; }
        private double ipo { get; set; }
        private double kpo { get; set; }
        private double aio { get; set; }
        private double bio { get; set; }
        private double aipo { get; set; }
        private double bipo { get; set; }
        private double sphjo { get; set; }
        private double sphyo { get; set; }
        private double sphjpo { get; set; }
        private double sphypo { get; set; }
        private int sphno { get; set; }

        public Bessel()
        {
            this.xjy = 9.99e99;
            this.nujy = 9.99e99;
            this.xik = 9.99e99;
            this.nuik = 9.99e99;
            this.xai = 9.99e99;
            this.sphno = -9999;
        }

        public double jnu(double nu, double x)
        {
            if (nu != nujy || x != xjy)
            {
                besseljy(nu, x);
            }
            return jo;
        }

        public double ynu(double nu, double x)
        {
            if (nu != nujy || x != xjy)
            {
                besseljy(nu, x);
            }
            return yo;
        }

        public double inu(double nu, double x)
        {
            if (nu != nuik || x != xik)
            {
                besselik(nu, x);
            }
            return io;
        }

        public double knu(double nu, double x)
        {
            if (nu != nuik || x != xik)
            {
                besselik(nu, x);
            }
            return ko;
        }


        public double chebev(double[] c, int m, double x)
        {
            double d = 0.0;
            double dd = 0.0;
            double sv;
            int j;
            for (j = m - 1; j > 0; j--)
            {
                sv = d;
                d = 2.0 * x * d - dd + c[j];
                dd = sv;
            }
            return x * d - dd + 0.5 * c[0];
        }

        public void besseljy(double nu, double x)
        {
            const int MAXIT = 10000;
            const double EPS = float.Epsilon;
            const double FPMIN = float.MinValue;//  float.MinValue / EPS;
            const double XMIN = 2.0;

            if (x <= 0.0 || nu < 0.0)
            {
                throw new Exception("bad arguments in besseljy");
            }
            int nl = (x < XMIN ? (int)(nu + 0.5) : Math.Max(0, (int)(nu - x + 1.5)));
            double xmu = nu - nl;
            double xmu2 = xmu * xmu;
            double xi = 1.0 / x;
            double xi2 = 2.0 * xi;
            double w = xi2 / Math.PI;
            int isign = 1;
            double h = nu * xi;
            if (h < FPMIN)
            {
                h = FPMIN;
            }
            double b = xi2 * nu;
            double d = 0.0;
            double c = h;
            int i = 0;
            for (; i < MAXIT; i++)
            {
                b += xi2;
                d = b - d;
                if (Math.Abs(d) < FPMIN)
                {
                    d = FPMIN;
                }
                c = b - 1.0 / c;
                if (Math.Abs(c) < FPMIN)
                {
                    c = FPMIN;
                }
                d = 1.0 / d;
                double del = c * d;
                h = del * h;
                if (d < 0.0)
                {
                    isign = -isign;
                }
                if (Math.Abs(del - 1.0) <= EPS)
                {
                    break;
                }
            }
            if (i >= MAXIT)
            {
                throw new Exception("x too large in besseljy; try asymptotic expansion");
            }
            double rjl = isign * FPMIN;
            double rjpl = h * rjl;
            double rjl1 = rjl;
            double rjp1 = rjpl;
            double fact = nu * xi;
            for (int l = nl - 1; l >= 0; l--)
            {
                double rjtemp = fact * rjl + rjpl;
                fact -= xi;
                rjpl = fact * rjtemp - rjl;
                rjl = rjtemp;
            }
            //if (rjl == 0.0)
            if (Math.Abs(rjl) <= float.Epsilon)
            {
                rjl = EPS;
            }

            double f = rjpl / rjl;
            double rjmu;
            double rytemp;
            double ry1;
            double rymu;
            if (x < XMIN)
            {
                double x2 = 0.5 * x;
                double pimu = Math.PI * xmu;
                fact = (Math.Abs(pimu) < EPS ? 1.0 : pimu / Math.Sin(pimu));
                d = -Math.Log(x2);
                double e = xmu * d;
                double fact2 = (Math.Abs(e) < EPS ? 1.0 : Math.Sinh(e) / e);
                double xx = 8.0 * Globals.SQR(xmu) - 1.0;
                double gam1 = chebev(c1, NUSE1, xx);
                double gam2 = chebev(c2, NUSE2, xx);
                double gampl = gam2 - xmu * gam1;
                double gammi = gam2 + xmu * gam1;
                double ff = 2.0 / Math.PI * fact * (gam1 * Math.Cosh(e) + gam2 * fact2 * d);
                e = Math.Exp(e);
                double p = e / (gampl * Math.PI);
                double q = 1.0 / (e * Math.PI * gammi);
                double pimu2 = 0.5 * pimu;
                double fact3 = (Math.Abs(pimu2) < EPS ? 1.0 : Math.Sin(pimu2) / pimu2);
                double r = Math.PI * pimu2 * fact3 * fact3;
                c = 1.0;
                d = -x2 * x2;
                double sum = ff + r * q;
                double sum1 = p;
                for (i = 1; i <= MAXIT; i++)
                {
                    ff = (i * ff + p + q) / (i * i - xmu2);
                    c *= (d / i);
                    p /= (i - xmu);
                    q /= (i + xmu);
                    double del = c * (ff + r * q);
                    sum += del;
                    double del1 = c * p - i * del;
                    sum1 += del1;
                    if (Math.Abs(del) < (1.0 + Math.Abs(sum)) * EPS)
                    {
                        break;
                    }
                }
                if (i > MAXIT)
                {
                    throw new Exception("bessy series failed to converge");
                }
                rymu = -sum;
                ry1 = -sum1 * xi2;
                double rymup = xmu * xi * rymu - ry1;
                rjmu = w / (rymup - f * rymu);
            }
            else
            {
                double a = 0.25 - xmu2;
                double p = -0.5 * xi;
                double q = 1.0;
                double br = 2.0 * x;
                double bi = 2.0;
                fact = a * xi / (p * p + q * q);
                double cr = br + q * fact;
                double ci = bi + p * fact;
                double den = br * br + bi * bi;
                double dr = br / den;
                double di = -bi / den;
                double dlr = cr * dr - ci * di;
                double dli = cr * di + ci * dr;
                double temp = p * dlr - q * dli;
                q = p * dli + q * dlr;
                p = temp;
                for (i = 1; i < MAXIT; i++)
                {
                    a += 2 * i;
                    bi += 2.0;
                    dr = a * dr + br;
                    di = a * di + bi;
                    if (Math.Abs(dr) + Math.Abs(di) < FPMIN)
                    {
                        dr = FPMIN;
                    }
                    fact = a / (cr * cr + ci * ci);
                    cr = br + cr * fact;
                    ci = bi - ci * fact;
                    if (Math.Abs(cr) + Math.Abs(ci) < FPMIN)
                    {
                        cr = FPMIN;
                    }
                    den = dr * dr + di * di;
                    dr /= den;
                    di /= -den;
                    dlr = cr * dr - ci * di;
                    dli = cr * di + ci * dr;
                    temp = p * dlr - q * dli;
                    q = p * dli + q * dlr;
                    p = temp;
                    if (Math.Abs(dlr - 1.0) + Math.Abs(dli) <= EPS)
                    {
                        break;
                    }
                }
                if (i >= MAXIT)
                {
                    throw new Exception("cf2 failed in besseljy");
                }
                double gam = (p - f) / q;
                rjmu = Math.Sqrt(w / ((p - f) * gam + q));
                rjmu = Globals.SIGN(rjmu, rjl);
                rymu = rjmu * gam;
                double rymup = rymu * (p + q / gam);
                ry1 = xmu * xi * rymu - rymup;
            }
            fact = rjmu / rjl;
            jo = rjl1 * fact;
            jpo = rjp1 * fact;
            for (i = 1; i <= nl; i++)
            {
                rytemp = (xmu + i) * xi2 * ry1 - rymu;
                rymu = ry1;
                ry1 = rytemp;
            }
            yo = rymu;
            ypo = nu * xi * rymu - ry1;
            xjy = x;
            nujy = nu;
        }

        public void besselik(double nu, double x)
        {
            const int MAXIT = 10000;
            const double EPS = float.Epsilon;
            const double FPMIN = float.MinValue;//  float.MinValue / EPS;
            const double XMIN = 2.0;

            if (x <= 0.0 || nu < 0.0)
            {
                throw new Exception("bad arguments in besselik");
            }
            int nl = (int)(nu + 0.5);
            double xmu = nu - nl;
            double xmu2 = xmu * xmu;
            double xi = 1.0 / x;
            double xi2 = 2.0 * xi;
            double h = nu * xi;
            if (h < FPMIN)
            {
                h = FPMIN;
            }
            double b = xi2 * nu;
            double d = 0.0;
            double c = h;
            int i = 0;
            for (; i < MAXIT; i++)
            {
                b += xi2;
                d = 1.0 / (b + d);
                c = b + 1.0 / c;
                double del = c * d;
                h = del * h;
                if (Math.Abs(del - 1.0) <= EPS)
                {
                    break;
                }
            }
            if (i >= MAXIT)
            {
                throw new Exception("x too large in besselik; try asymptotic expansion");
            }
            double ril = FPMIN;
            double ripl = h * ril;
            double ril1 = ril;
            double rip1 = ripl;
            double fact = nu * xi;
            for (int l = nl - 1; l >= 0; l--)
            {
                double ritemp = fact * ril + ripl;
                fact -= xi;
                ripl = fact * ritemp + ril;
                ril = ritemp;
            }

            double f = ripl / ril;
            double rk1;
            double rkmu;
            if (x < XMIN)
            {
                double x2 = 0.5 * x;
                double pimu = Math.PI * xmu;
                fact = (Math.Abs(pimu) < EPS ? 1.0 : pimu / Math.Sin(pimu));
                d = -Math.Log(x2);
                double e = xmu * d;
                double fact2 = (Math.Abs(e) < EPS ? 1.0 : Math.Sinh(e) / e);
                double xx = 8.0 * Globals.SQR(xmu) - 1.0;
                double gam1 = chebev(c1, NUSE1, xx);
                double gam2 = chebev(c2, NUSE2, xx);
                double gampl = gam2 - xmu * gam1;
                double gammi = gam2 + xmu * gam1;
                double ff = fact * (gam1 * Math.Cosh(e) + gam2 * fact2 * d);
                double sum = ff;
                e = Math.Exp(e);
                double p = 0.5 * e / gampl;
                double q = 0.5 / (e * gammi);
                c = 1.0;
                d = x2 * x2;
                double sum1 = p;
                for (i = 1; i <= MAXIT; i++)
                {
                    ff = (i * ff + p + q) / (i * i - xmu2);
                    c *= (d / i);
                    p /= (i - xmu);
                    q /= (i + xmu);
                    double del = c * ff;
                    sum += del;
                    double del1 = c * (p - i * ff);
                    sum1 += del1;
                    if (Math.Abs(del) < Math.Abs(sum) * EPS)
                    {
                        break;
                    }
                }
                if (i > MAXIT)
                {
                    throw new Exception("bessk series failed to converge");
                }
                rkmu = sum;
                rk1 = sum1 * xi2;
            }
            else
            {
                b = 2.0 * (1.0 + x);
                d = 1.0 / b;
                h = d;
                double delh = d;
                double q1 = 0.0;
                double q2 = 1.0;
                double a1 = 0.25 - xmu2;
                double q = c = a1;
                double a = -a1;
                double s = 1.0 + q * delh;
                for (i = 1; i < MAXIT; i++)
                {
                    a -= 2 * i;
                    c = -a * c / (i + 1.0);
                    double qnew = (q1 - b * q2) / a;
                    q1 = q2;
                    q2 = qnew;
                    q += c * qnew;
                    b += 2.0;
                    d = 1.0 / (b + a * d);
                    delh = (b * d - 1.0) * delh;
                    h += delh;
                    double dels = q * delh;
                    s += dels;
                    if (Math.Abs(dels / s) <= EPS)
                    {
                        break;
                    }
                }
                if (i >= MAXIT)
                {
                    throw new Exception("besselik: failure to converge in cf2");
                }
                h = a1 * h;
                rkmu = Math.Sqrt(Math.PI / (2.0 * x)) * Math.Exp(-x) / s;
                rk1 = rkmu * (xmu + x + 0.5 - h) * xi;
            }
            double rkmup = xmu * xi * rkmu - rk1;
            double rimu = xi / (f * rkmu - rkmup);
            io = (rimu * ril1) / ril;
            ipo = (rimu * rip1) / ril;
            for (i = 1; i <= nl; i++)
            {
                double rktemp = (xmu + i) * xi2 * rk1 + rkmu;
                rkmu = rk1;
                rk1 = rktemp;
            }
            ko = rkmu;
            kpo = nu * xi * rkmu - rk1;
            xik = x;
            nuik = nu;
        }

        public void airy(double x)
        {
            const double ONOVRT = 0.577350269189626;
            const double THR = 1.0 / 3.0;
            const double TWOTHR = 2.0 * THR;

            double absx = Math.Abs(x);
            double rootx = Math.Sqrt(absx);
            double z = TWOTHR * absx * rootx;
            if (x > 0.0)
            {
                besselik(THR, z);
                aio = rootx * ONOVRT * ko / Math.PI;
                bio = rootx * (ko / Math.PI + 2.0 * ONOVRT * io);
                besselik(TWOTHR, z);
                aipo = -x * ONOVRT * ko / Math.PI;
                bipo = x * (ko / Math.PI + 2.0 * ONOVRT * io);
            }
            else if (x < 0.0)
            {
                besseljy(THR, z);
                aio = 0.5 * rootx * (jo - ONOVRT * yo);
                bio = -0.5 * rootx * (yo + ONOVRT * jo);
                besseljy(TWOTHR, z);
                aipo = 0.5 * absx * (ONOVRT * yo + jo);
                bipo = 0.5 * absx * (ONOVRT * jo - yo);
            }
            else
            {
                aio = 0.355028053887817;
                bio = aio / ONOVRT;
                aipo = -0.258819403792807;
                bipo = -aipo / ONOVRT;
            }
        }

        public double airy_ai(double x)
        {
            if (x != xai)
            {
                airy(x);
            }
            return aio;
        }

        public double airy_bi(double x)
        {
            if (x != xai)
            {
                airy(x);
            }
            return bio;
        }

        public void sphbes(int n, double x)
        {
            const double RTPIO2 = 1.253314137315500251;

            if (n < 0 || x <= 0.0)
            {
                throw new Exception("bad arguments in sphbes");
            }
            double order = n + 0.5;
            besseljy(order, x);
            double factor = RTPIO2 / Math.Sqrt(x);
            sphjo = factor * jo;
            sphyo = factor * yo;
            sphjpo = factor * jpo - sphjo / (2.0 * x);
            sphypo = factor * ypo - sphyo / (2.0 * x);
            sphno = n;
        }

        public double sphbesj(int n, double x)
        {
            if (n != sphno || x != xsph)
            {
                sphbes(n, x);
            }
            return sphjo;
        }

        public double sphbesy(int n, double x)
        {
            if (n != sphno || x != xsph)
            {
                sphbes(n, x);
            }
            return sphyo;
        }
    }
}
 

猜你喜欢

转载自blog.csdn.net/beijinghorn/article/details/131605522
今日推荐