C#,数值计算——多种积分(Integrals)的计算方法与源代码

using System;

namespace Legalsoft.Truffer
{
    public class Integrals
    {
        public Integrals()
        {
        }

        /// <summary>
        /// Exponential Integrals
        /// </summary>
        /// <param name="n"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static double expint(int n, double x)
        {
            const int MAXIT = 100;
            const double EULER = 0.577215664901533;
            const double EPS = float.Epsilon; //numeric_limits<double>.epsilon();
            const double BIG = double.MaxValue * EPS;

            int nm1 = n - 1;
            //if (n < 0 || x < 0.0 || (x == 0.0 && (n == 0 || n == 1)))
            if (n < 0 || x < 0.0 || (Math.Abs(x) <= float.Epsilon && (n == 0 || n == 1)))
            {
                throw new Exception("bad arguments in expint");
            }
            double ans;
            if (n == 0)
            {
                ans = Math.Exp(-x) / x;
            }
            else
            {
                //if (x == 0.0)
                if (Math.Abs(x) <= float.Epsilon)
                {
                    ans = 1.0 / nm1;
                }
                else
                {
                    if (x > 1.0)
                    {
                        double b = x + n;
                        double c = BIG;
                        double d = 1.0 / b;
                        double h = d;
                        for (int i = 1; i <= MAXIT; i++)
                        {
                            double a = -i * (nm1 + i);
                            b += 2.0;
                            d = 1.0 / (a * d + b);
                            c = b + a / c;
                            double del = c * d;
                            h *= del;
                            if (Math.Abs(del - 1.0) <= EPS)
                            {
                                ans = h * Math.Exp(-x);
                                return ans;
                            }
                        }
                        throw new Exception("continued fraction failed in expint");
                    }
                    else
                    {
                        ans = (nm1 != 0 ? 1.0 / nm1 : -Math.Log(x) - EULER);
                        double fact = 1.0;
                        double del;
                        for (int i = 1; i <= MAXIT; i++)
                        {
                            fact *= -x / i;
                            if (i != nm1)
                            {
                                del = -fact / (i - nm1);
                            }
                            else
                            {
                                double psi = -EULER;
                                for (int ii = 1; ii <= nm1; ii++)
                                {
                                    psi += 1.0 / ii;
                                }
                                del = fact * (-Math.Log(x) + psi);
                            }
                            ans += del;
                            if (Math.Abs(del) < Math.Abs(ans) * EPS)
                            {
                                return ans;
                            }
                        }
                        throw new Exception("series failed in expint");
                    }
                }
            }
            return ans;
        }

        /// <summary>
        /// Exponential Integrals
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static double ei(double x)
        {
            const int MAXIT = 100;
            const double EULER = 0.577215664901533;
            const double EPS = float.Epsilon; //numeric_limits<double>.epsilon();
            const double FPMIN = float.MinValue;

            if (x <= 0.0)
            {
                throw new Exception("Bad argument in ei");
            }
            if (x < FPMIN)
            {
                return Math.Log(x) + EULER;
            }
            if (x <= -Math.Log(EPS))
            {
                double sum = 0.0;
                double fact = 1.0;
                int k = 1;
                for (; k <= MAXIT; k++)
                {
                    fact *= x / k;
                    double term = fact / k;
                    sum += term;
                    if (term < EPS * sum)
                    {
                        break;
                    }
                }
                if (k > MAXIT)
                {
                    throw new Exception("Series failed in ei");
                }
                return sum + Math.Log(x) + EULER;
            }
            else
            {
                double sum = 0.0;
                double term = 1.0;
                for (int k = 1; k <= MAXIT; k++)
                {
                    double prev = term;
                    term *= k / x;
                    if (term < EPS)
                    {
                        break;
                    }
                    if (term < prev)
                    {
                        sum += term;
                    }
                    else
                    {
                        sum -= prev;
                        break;
                    }
                }
                return Math.Exp(x) * (1.0 + sum) / x;
            }
        }

        /// <summary>
        /// Fermi-Dirac integrals
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static Complex frenel(double x)
        {
            const int MAXIT = 100;
            const double PIBY2 = (Math.PI / 2.0);
            const double XMIN = 1.5;
            const double EPS = float.Epsilon; //numeric_limits<double>.epsilon();
            const double FPMIN = float.MinValue;
            const double BIG = double.MaxValue * EPS;

            Complex cs = new Complex();
            double ax = Math.Abs(x);
            if ((ax) < Math.Sqrt(FPMIN))
            {
                cs = new Complex(ax);
            }
            else if (ax <= XMIN)
            {
                double sum = 0.0;
                double sums = 0.0;
                double sumc = ax;
                double sign = 1.0;
                double fact = PIBY2 * ax * ax;
                bool odd = true;
                double term = ax;
                int n = 3;
                int k = 1;
                for (; k <= MAXIT; k++)
                {
                    term *= fact / k;
                    sum += sign * term / n;
                    double test = Math.Abs(sum) * EPS;
                    if (odd)
                    {
                        sign = -sign;
                        sums = sum;
                        sum = sumc;
                    }
                    else
                    {
                        sumc = sum;
                        sum = sums;
                    }
                    if (term < test)
                    {
                        break;
                    }
                    odd = !odd;
                    n += 2;
                }
                if (k > MAXIT)
                {
                    throw new Exception("series failed in frenel");
                }
                cs = new Complex(sumc, sums);
            }
            else
            {
                double pix2 = Math.PI * ax * ax;
                Complex b = new Complex(1.0, -pix2);
                Complex cc = new Complex(BIG);
                Complex h = 1.0 / b;
                Complex d = h;
                int n = -1;
                int k = 2;
                for (; k <= MAXIT; k++)
                {
                    n += 2;
                    int a = -n * (n + 1);
                    b += 4.0;
                    d = 1.0 / (a * d + b);
                    cc = b + a / cc;
                    Complex del = cc * d;
                    h *= del;
                    if (Math.Abs((del.re) - 1.0) + Math.Abs((del.im)) <= EPS)
                    {
                        break;
                    }
                }
                if (k > MAXIT)
                {
                    throw new Exception("cf failed in frenel");
                }
                h = h * new Complex(ax, -ax);
                cs = new Complex(0.5, 0.5) * (new Complex(1.0) - new Complex(Math.Cos(0.5 * pix2), Math.Sin(0.5 * pix2)) * h);
            }
            if (x < 0.0)
            {
                cs = -cs;
            }
            return cs;
        }

        /// <summary>
        /// cosine and sine integrals
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static Complex cisi(double x)
        {
            const int MAXIT = 100;
            const double EULER = 0.577215664901533;
            const double PIBY2 = 1.570796326794897;
            const double TMIN = 2.0;
            double EPS = float.Epsilon;
            double FPMIN = float.MinValue * 4.0;// numeric_limits<double>.min() * 4.0;
            double BIG = double.MaxValue * EPS;
            Complex cs;

            double t = Math.Abs(x);
            //if ((t) == 0.0)
            if (Math.Abs(t) <= float.Epsilon)
            {
                return new Complex(-BIG, -BIG);
            }
            if (t > TMIN)
            {
                Complex b = new Complex(1.0, t);
                Complex c = new Complex(BIG, 0.0);
                Complex d = 1.0 / b;
                Complex h = d;
                int i;
                for (i = 1; i < MAXIT; i++)
                {
                    double a = -i * i;
                    b = b + 2.0;
                    d = 1.0 / (a * d + b);
                    c = (b + a / c);
                    Complex del = (c * d);
                    h *= del;
                    if (Math.Abs((del.re) - 1.0) + Math.Abs((del.im)) <= EPS)
                    {
                        break;
                    }
                }
                if (i >= MAXIT)
                {
                    throw new Exception("cf failed in cisi");
                }
                h = new Complex(Math.Cos(t), -Math.Sin(t)) * h;
                cs = -h.conj() + new Complex(0.0, PIBY2);
            }
            else
            {
                double err;
                double sign;
                double fact;
                double sum;
                double sumc;
                double sums;
                if (t < Math.Sqrt(FPMIN))
                {
                    sumc = 0.0;
                    sums = t;
                }
                else
                {
                    sum = sums = sumc = 0.0;
                    sign = fact = 1.0;
                    bool odd = true;
                    int k;
                    for (k = 1; k <= MAXIT; k++)
                    {
                        fact *= t / k;
                        double term = fact / k;
                        sum += sign * term;
                        err = term / Math.Abs(sum);
                        if (odd)
                        {
                            sign = -sign;
                            sums = sum;
                            sum = sumc;
                        }
                        else
                        {
                            sumc = sum;
                            sum = sums;
                        }
                        if (err < EPS)
                        {
                            break;
                        }
                        odd = !odd;
                    }
                    if (k > MAXIT)
                    {
                        throw new Exception("maxits exceeded in cisi");
                    }
                }
                cs = new Complex(sumc + Math.Log(t) + EULER, sums);
            }
            if (x < 0.0)
            {
                cs = cs.conj();
            }
            return cs;
        }

        /// <summary>
        /// Dawson's integral
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double dawson(double x)
        {
            const double H = 0.4;
            const double A1 = 2.0 / 3.0;
            const double A2 = 0.4;
            const double A3 = 2.0 / 7.0;
            const int NMAX = 6;
            double[] c = new double[NMAX];

            for (int i = 0; i < NMAX; i++)
            {
                c[i] = Math.Exp(-Globals.SQR((2.0 * i + 1.0) * H));
            }

            double ans;
            if (Math.Abs(x) < 0.2)
            {
                double x2 = x * x;
                ans = x * (1.0 - A1 * x2 * (1.0 - A2 * x2 * (1.0 - A3 * x2)));
            }
            else
            {
                double xx = Math.Abs(x);
                int n0 = 2 * (int)(0.5 * xx / H + 0.5);
                double xp = xx - n0 * H;
                double e1 = Math.Exp(2.0 * xp * H);
                double e2 = e1 * e1;
                double d1 = n0 + 1;
                double d2 = d1 - 2.0;
                double sum = 0.0;
                for (int i = 0; i < NMAX; i++, d1 += 2.0, d2 -= 2.0, e1 *= e2)
                {
                    sum += c[i] * (e1 / d1 + 1.0 / (d2 * e1));
                }
                ans = 0.5641895835 * Globals.SIGN(Math.Exp(-xp * xp), x) * sum;
            }
            return ans;
        }

        public static double invxlogx(double y)
        {
            const double ooe = 0.367879441171442322;
            double to = 0.0;
            if (y >= 0.0 || y <= -ooe)
            {
                throw new Exception("no such inverse value");
            }
            double u;
            if (y < -0.2)
            {
                u = Math.Log(ooe - Math.Sqrt(2 * ooe * (y + ooe)));
            }
            else
            {
                u = -10.0;
            }
            double t;
            do
            {
                u += (t = (Math.Log(y / u) - u) * (u / (1.0 + u)));
                if (t < 1.0e-8 && Math.Abs(t + to) < 0.01 * Math.Abs(t))
                {
                    break;
                }
                to = t;
            } while (Math.Abs(t / u) > 1.0e-15);
            return Math.Exp(u);
        }

    }
}
 

猜你喜欢

转载自blog.csdn.net/beijinghorn/article/details/131894065