C#,数值计算——椭圆积分与雅可比椭圆函数(Elliptic Integrals and Jacobian Elliptic Functions)的计算方法与源程序

可爱的科尔鸭

using System;

namespace Legalsoft.Truffer
{
    /// <summary>
    /// Elliptic Integrals and Jacobian Elliptic Functions
    /// </summary>
    public class Elliptic
    {
        public Elliptic()
        {
        }

        private static double rc(double x, double y)
        {
            const double ERRTOL = 0.0012;
            const double THIRD = 1.0 / 3.0;
            const double C1 = 0.3;
            const double C2 = 1.0 / 7.0;
            const double C3 = 0.375;
            const double C4 = 9.0 / 22.0;
            const double TINY = 5.0 * float.MinValue;
            const double BIG = 0.2 * double.MaxValue;

            double COMP1 = 2.236 / Math.Sqrt(TINY);
            double COMP2 = Globals.SQR(TINY * BIG) / 25.0;

            //if (x < 0.0 || y == 0.0 || (x + Math.Abs(y)) < TINY || (x + Math.Abs(y)) > BIG || (y < -COMP1 && x > 0.0 && x < COMP2))
            if (x < 0.0 || Math.Abs(y) <= float.Epsilon || (x + Math.Abs(y)) < TINY || (x + Math.Abs(y)) > BIG || (y < -COMP1 && x > 0.0 && x < COMP2))
            {
                throw new Exception("invalid arguments in rc");
            }

            double xt;
            double yt;
            double w;
            if (y > 0.0)
            {
                xt = x;
                yt = y;
                w = 1.0;
            }
            else
            {
                xt = x - y;
                yt = -y;
                w = Math.Sqrt(x) / Math.Sqrt(xt);
            }

            double s;
            double ave;
            do
            {
                double alamb = 2.0 * Math.Sqrt(xt) * Math.Sqrt(yt) + yt;
                xt = 0.25 * (xt + alamb);
                yt = 0.25 * (yt + alamb);
                ave = THIRD * (xt + yt + yt);
                s = (yt - ave) / ave;
            } while (Math.Abs(s) > ERRTOL);
            return w * (1.0 + s * s * (C1 + s * (C2 + s * (C3 + s * C4)))) / Math.Sqrt(ave);
        }


        private static double rd(double x, double y, double z)
        {
            const double ERRTOL = 0.0015;
            const double C1 = 3.0 / 14.0;
            const double C2 = 1.0 / 6.0;
            const double C3 = 9.0 / 22.0;
            const double C4 = 3.0 / 26.0;
            const double C5 = 0.25 * C3;
            const double C6 = 1.5 * C4;
            double TINY = 2.0 * Math.Pow(double.MaxValue, -2.0 / 3.0);
            double BIG = 0.1 * ERRTOL * Math.Pow(float.MinValue, -2.0 / 3.0);

            double ave;
            double delx;
            double dely;
            double delz;
            if (Math.Min(x, y) < 0.0 || Math.Min(x + y, z) < TINY || Math.Max(Math.Max(x, y), z) > BIG)
            {
                throw new Exception("invalid arguments in rd");
            }
            double xt = x;
            double yt = y;
            double zt = z;
            double sum = 0.0;
            double fac = 1.0;
            do
            {
                double sqrtx = Math.Sqrt(xt);
                double sqrty = Math.Sqrt(yt);
                double sqrtz = Math.Sqrt(zt);
                double alamb = sqrtx * (sqrty + sqrtz) + sqrty * sqrtz;
                sum += fac / (sqrtz * (zt + alamb));
                fac = 0.25 * fac;
                xt = 0.25 * (xt + alamb);
                yt = 0.25 * (yt + alamb);
                zt = 0.25 * (zt + alamb);
                ave = 0.2 * (xt + yt + 3.0 * zt);
                delx = (ave - xt) / ave;
                dely = (ave - yt) / ave;
                delz = (ave - zt) / ave;
            } while (Math.Max(Math.Max(Math.Abs(delx), Math.Abs(dely)), Math.Abs(delz)) > ERRTOL);
            double ea = delx * dely;
            double eb = delz * delz;
            double ec = ea - eb;
            double ed = ea - 6.0 * eb;
            double ee = ed + ec + ec;
            return 3.0 * sum + fac * (1.0 + ed * (-C1 + C5 * ed - C6 * delz * ee) + delz * (C2 * ee + delz * (-C3 * ec + delz * C4 * ea))) / (ave * Math.Sqrt(ave));
        }

        private static double rf(double x, double y, double z)
        {
            const double ERRTOL = 0.0025;
            const double THIRD = 1.0 / 3.0;
            const double C1 = 1.0 / 24.0;
            const double C2 = 0.1;
            const double C3 = 3.0 / 44.0;
            const double C4 = 1.0 / 14.0;
            double TINY = 5.0 * float.MinValue;
            double BIG = 0.2 * double.MaxValue;

            double ave;
            double delx;
            double dely;
            double delz;
            if (Math.Min(Math.Min(x, y), z) < 0.0 || Math.Min(Math.Min(x + y, x + z), y + z) < TINY || Math.Max(Math.Max(x, y), z) > BIG)
            {
                throw new Exception("invalid arguments in rf");
            }
            double xt = x;
            double yt = y;
            double zt = z;
            do
            {
                double sqrtx = Math.Sqrt(xt);
                double sqrty = Math.Sqrt(yt);
                double sqrtz = Math.Sqrt(zt);
                double alamb = sqrtx * (sqrty + sqrtz) + sqrty * sqrtz;
                xt = 0.25 * (xt + alamb);
                yt = 0.25 * (yt + alamb);
                zt = 0.25 * (zt + alamb);
                ave = THIRD * (xt + yt + zt);
                delx = (ave - xt) / ave;
                dely = (ave - yt) / ave;
                delz = (ave - zt) / ave;
            } while (Math.Max(Math.Max(Math.Abs(delx), Math.Abs(dely)), Math.Abs(delz)) > ERRTOL);
            double e2 = delx * dely - delz * delz;
            double e3 = delx * dely * delz;
            return (1.0 + (C1 * e2 - C2 - C3 * e3) * e2 + C4 * e3) / Math.Sqrt(ave);
        }

        private static double rj(double x, double y, double z, double p)
        {
            const double ERRTOL = 0.0015;
            const double C1 = 3.0 / 14.0;
            const double C2 = 1.0 / 3.0;
            const double C3 = 3.0 / 22.0;
            const double C4 = 3.0 / 26.0;
            double C5 = 0.75 * C3;
            double C6 = 1.5 * C4;
            double C7 = 0.5 * C2;
            double C8 = C3 + C3;
            double TINY = Math.Pow(5.0 * float.MinValue, 1.0 / 3.0);
            double BIG = 0.3 * Math.Pow(0.2 * double.MaxValue, 1.0 / 3.0);
            double a = 0.0;

            double ave;
            double b = 0.0;
            double delp;
            double delx;
            double dely;
            double delz;
            double rcx = 0.0;
            if (Math.Min(Math.Min(x, y), z) < 0.0 || Math.Min(Math.Min(x + y, x + z), Math.Min(y + z, Math.Abs(p))) < TINY || Math.Max(Math.Max(x, y), Math.Max(z, Math.Abs(p))) > BIG)
            {
                throw new Exception("invalid arguments in rj");
            }
            double sum = 0.0;
            double fac = 1.0;
            double xt;
            double yt;
            double zt;
            double pt;
            if (p > 0.0)
            {
                xt = x;
                yt = y;
                zt = z;
                pt = p;
            }
            else
            {
                xt = Math.Min(Math.Min(x, y), z);
                zt = Math.Max(Math.Max(x, y), z);
                yt = x + y + z - xt - zt;
                a = 1.0 / (yt - p);
                b = a * (zt - yt) * (yt - xt);
                pt = yt + b;
                double rho = xt * zt / yt;
                double tau = p * pt / yt;
                rcx = rc(rho, tau);
            }

            do
            {
                double sqrtx = Math.Sqrt(xt);
                double sqrty = Math.Sqrt(yt);
                double sqrtz = Math.Sqrt(zt);
                double alamb = sqrtx * (sqrty + sqrtz) + sqrty * sqrtz;
                double alpha = Globals.SQR(pt * (sqrtx + sqrty + sqrtz) + sqrtx * sqrty * sqrtz);
                double beta = pt * Globals.SQR(pt + alamb);
                sum += fac * rc(alpha, beta);
                fac = 0.25 * fac;
                xt = 0.25 * (xt + alamb);
                yt = 0.25 * (yt + alamb);
                zt = 0.25 * (zt + alamb);
                pt = 0.25 * (pt + alamb);
                ave = 0.2 * (xt + yt + zt + pt + pt);
                delx = (ave - xt) / ave;
                dely = (ave - yt) / ave;
                delz = (ave - zt) / ave;
                delp = (ave - pt) / ave;
            } while (Math.Max(Math.Max(Math.Abs(delx), Math.Abs(dely)), Math.Max(Math.Abs(delz), Math.Abs(delp))) > ERRTOL);
            double ea = delx * (dely + delz) + dely * delz;
            double eb = delx * dely * delz;
            double ec = delp * delp;
            double ed = ea - 3.0 * ec;
            double ee = eb + 2.0 * delp * (ea - ec);
            double ans = 3.0 * sum + fac * (1.0 + ed * (-C1 + C5 * ed - C6 * ee) + eb * (C7 + delp * (-C8 + delp * C4)) + delp * ea * (C2 - delp * C3) - C2 * delp * ec) / (ave * Math.Sqrt(ave));
            if (p <= 0.0)
            {
                ans = a * (b * ans + 3.0 * (rcx - rf(xt, yt, zt)));
            }
            return ans;
        }


        private static double ellf(double phi, double ak)
        {
            double s = Math.Sin(phi);
            return s * rf(Globals.SQR(Math.Cos(phi)), (1.0 - s * ak) * (1.0 + s * ak), 1.0);
        }

        private static double elle(double phi, double ak)
        {
            double s = Math.Sin(phi);
            double cc = Globals.SQR(Math.Cos(phi));
            double q = (1.0 - s * ak) * (1.0 + s * ak);
            return s * (rf(cc, q, 1.0) - (Globals.SQR(s * ak)) * rd(cc, q, 1.0) / 3.0);
        }

        private static double ellpi(double phi, double en, double ak)
        {
            double s = Math.Sin(phi);
            double enss = en * s * s;
            double cc = Globals.SQR(Math.Cos(phi));
            double q = (1.0 - s * ak) * (1.0 + s * ak);
            return s * (rf(cc, q, 1.0) - enss * rj(cc, q, 1.0, 1.0 + enss) / 3.0);
        }

        private static void sncndn(double uu, double emmc, ref double sn, ref double cn, ref double dn)
        {
            const double CA = 1.0e-8;
            int l = 0;
            double c = 0.0;
            double d = 0.0;
            double[] em = new double[13];
            double[] en = new double[13];
            double emc = emmc;
            double u = uu;
            if (emc != 0.0)
            {
                bool bo = (emc < 0.0);
                if (bo)
                {
                    d = 1.0 - emc;
                    emc /= -1.0 / d;
                    u *= (d = Math.Sqrt(d));
                }
                double a = 1.0;
                dn = 1.0;
                for (int i = 0; i < 13; i++)
                {
                    l = i;
                    em[i] = a;
                    en[i] = (emc = Math.Sqrt(emc));
                    c = 0.5 * (a + emc);
                    if (Math.Abs(a - emc) <= CA * a)
                    {
                        break;
                    }
                    emc *= a;
                    a = c;
                }
                u *= c;
                sn = Math.Sin(u);
                cn = Math.Cos(u);
                if (sn != 0.0)
                {
                    a = cn / sn;
                    c *= a;
                    for (int ii = l; ii >= 0; ii--)
                    {
                        double b = em[ii];
                        a *= c;
                        c *= dn;
                        dn = (en[ii] + a) / (b + a);
                        a = c / b;
                    }
                    a = 1.0 / Math.Sqrt(c * c + 1.0);
                    sn = (sn >= 0.0 ? a : -a);
                    cn = c * sn;
                }
                if (bo)
                {
                    a = dn;
                    dn = cn;
                    cn = a;
                    sn /= d;
                }
            }
            else
            {
                cn = 1.0 / Math.Cosh(u);
                dn = cn;
                sn = Math.Tanh(u);
            }
        }
    }
}

猜你喜欢

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