C# 用Cardano方法求解一个三次方程

C# 用Cardano方法求解一个三次方程,c#,algorithm,math,C#,Algorithm,Math,我正试图用Cardano方法找到一个由四个系数组成的三次方程的实根。问题是,我的实现找到的根实际上不起作用-通过在等式中插入它们进行测试会产生显著的错误(超过所需的10^-6)。算法是否执行错误,或者错误是由其他原因造成的,比如舍入精度 static double CubicRoot(double n) { return Math.Pow(Math.Abs(n), 1d / 3d) * Math.Sign(n); } public static List<

我正试图用Cardano方法找到一个由四个系数组成的三次方程的实根。问题是,我的实现找到的根实际上不起作用-通过在等式中插入它们进行测试会产生显著的错误(超过所需的10^-6)。算法是否执行错误,或者错误是由其他原因造成的,比如舍入精度

static double CubicRoot(double n)
    {
        return Math.Pow(Math.Abs(n), 1d / 3d) * Math.Sign(n);
    }


public static List<double> SolveCubic(double A, double B = 0, double C = 0, double D = 0)
    {
        List<double> output = new List<double>();
        if (A != 0)
        {

                double A1 = B / A;
                double A2 = C / A;
                double A3 = D / A;
                double P = -((A1 * A1) / 3) + A2;
                double Q = ((2.0 * A1 * A1 * A1) / 27.0) - ((A1 * A2) / 3.0) + A3;
                double cubeDiscr = Q * Q / 4.0 + P * P * P / 27.0;
                if (cubeDiscr > 0)
                {
                    double u = CubicRoot(-Q / 2.0 + Math.Sqrt(cubeDiscr));
                    double v = CubicRoot(-Q / 2.0 - Math.Sqrt(cubeDiscr));
                    output.Add(u + v - (A1 / 3.0));
                    return output;
                }
                else if (cubeDiscr == 0)
                {
                    double u = CubicRoot(-Q / 2.0);
                    output.Add(2u - (A1 / 3.0));
                    output.Add(-u - (A1 / 3.0));
                }
                else if (cubeDiscr < 0)
                {
                    double r = CubicRoot(Math.Sqrt(-(P * P * P / 27.0)));
                    double alpha = Math.Atan(Math.Sqrt(-cubeDiscr) / (-Q / 2.0));
                    output.Add(r * (Math.Cos(alpha / 3.0) + Math.Cos((6 * Math.PI - alpha) / 3.0)) - A1 / 3.0);
                    output.Add(r * (Math.Cos((2 * Math.PI + alpha) / 3.0) + Math.Cos((4 * Math.PI - alpha) / 3.0)) - A1 / 3.0);
                    output.Add(r * (Math.Cos((4 * Math.PI + alpha) / 3.0) + Math.Cos((2 * Math.PI - alpha) / 3.0)) - A1 / 3.0);
                }
        }
        return output;
    }
静态双立方体(双n)
{
返回数学功率(数学Abs(n),1d/3d)*数学符号(n);
}
公共静态列表(双A,双B=0,双C=0,双D=0)
{
列表输出=新列表();
如果(A!=0)
{
双A1=B/A;
双A2=C/A;
双A3=D/A;
双P=-((A1*A1)/3)+A2;
双Q=((2.0*A1*A1*A1)/27.0)-(A1*A2)/3.0)+A3;
双立方结构=Q*Q/4.0+P*P*P/27.0;
如果(cubeDiscr>0)
{
双u=CubicRoot(-Q/2.0+Math.Sqrt(cubeDiscr));
双v=CubicRoot(-Q/2.0-Math.Sqrt(cubeDiscr));
输出。添加(u+v-(A1/3.0));
返回输出;
}
else if(cubeDiscr==0)
{
双u=立方英尺(-Q/2.0);
输出。添加(2u-(A1/3.0));
输出。添加(-u-(A1/3.0));
}
否则如果(cubeDiscr<0)
{
双r=CubicRoot(数学Sqrt(-(P*P*P/27.0));
double alpha=Math.Atan(Math.Sqrt(-cubeDiscr)/(-Q/2.0));
Add(r*(Math.Cos(alpha/3.0)+Math.Cos((6*Math.PI-alpha)/3.0))-A1/3.0);
Add(r*(Math.Cos((2*Math.PI+alpha)/3.0)+Math.Cos((4*Math.PI-alpha)/3.0))-A1/3.0;
Add(r*(Math.Cos((4*Math.PI+alpha)/3.0)+Math.Cos((2*Math.PI-alpha)/3.0))-A1/3.0;
}
}
返回输出;
}
一些事情

  • Math.Sign
    将返回零对零,这恰好是您在本例中想要的结果,但可能您在代码或算法更改方面没有那么幸运
  • 您将遇到舍入问题,并且在应该执行时不会执行
    cubeDiscr==0
    分支。您可能存在舍入问题,并出于同样的原因执行错误的
    >0
    <0
    分支。改为在0的增量范围内进行测试(见下文)
  • 但是
    cubeDiscr==0
    分支是错误的,因为1)您没有计算
    v
    ,2)
    2u
    是一个值为2的
    UInt32
    ,而不是
    2*u
  • 计算alpha是错误的(见下文)
  • (可能还有更多,但我一眼就看到了这些)
关于计算α:

double alpha = Math.Atan(Math.Sqrt(-cubeDiscr) / (-Q / 2.0));
不一样

double alpha = Math.Atan(Math.Sqrt(-d) / q * 2.0);
if (q > 0)                         // if q > 0 the angle becomes  PI + alpha
    alpha = Math.PI + alpha;
使用该页面中包含的代码有什么问题

public double Xroot(double a, double x)
{
    double i = 1;
    if (a < 0)
        i = -1;
    return (i * Math.Exp( Math.Log(a*i)/x));
}

public int Calc_Cardano()  // solve cubic equation according to cardano
{
    double p, q, u, v;
    double r, alpha;
    int res;
    res = 0;
    if (a1 != 0)
    {
        a = b / a1;
        b = c / a1;
        c = d / a1;

        p = -(a * a / 3.0) + b;
        q = (2.0 / 27.0 * a * a * a) - (a * b / 3.0) + c;
        d = q * q / 4.0 + p * p * p / 27.0;
        if (Math.Abs(d) < Math.Pow(10.0, -11.0))
            d = 0;
        // 3 cases D > 0, D == 0 and D < 0
        if (d > 1e-20)
        {
            u = Xroot(-q / 2.0 + Math.Sqrt(d), 3.0);
            v = Xroot(-q / 2.0 - Math.Sqrt(d), 3.0);
            x1.real = u + v - a / 3.0;
            x2.real = -(u + v) / 2.0 - a / 3.0;
            x2.imag = Math.Sqrt(3.0) / 2.0 * (u - v);
            x3.real = x2.real;
            x3.imag = -x2.imag;
            res = 1;
        }
        if (Math.Abs(d) <= 1e-20)
        {
            u = Xroot(-q / 2.0, 3.0);
            v = Xroot(-q / 2.0, 3.0);
            x1.real = u + v - a / 3.0;
            x2.real = -(u + v) / 2.0 - a / 3.0;
            res = 2;
        }
        if (d < -1e-20)
        {
            r = Math.Sqrt(-p * p * p / 27.0);
            alpha = Math.Atan(Math.Sqrt(-d) / q * 2.0);
            if (q > 0)                         // if q > 0 the angle becomes  PI + alpha
                alpha = Math.PI + alpha;

            x1.real = Xroot(r, 3.0) * (Math.Cos((6.0 * Math.PI - alpha) / 3.0) + Math.Cos(alpha / 3.0)) - a / 3.0;
            x2.real = Xroot(r, 3.0) * (Math.Cos((2.0 * Math.PI + alpha) / 3.0) + Math.Cos((4.0 * Math.PI - alpha) / 3.0)) - a / 3.0;
            x3.real = Xroot(r, 3.0) * (Math.Cos((4.0 * Math.PI + alpha) / 3.0) + Math.Cos((2.0 * Math.PI - alpha) / 3.0)) - a / 3.0;
            res = 3;
        }
    }
    else
        res = 0;
    return res;
 }
公共双x根(双a,双x)
{
双i=1;
if(a<0)
i=-1;
返回(i*Math.Exp(Math.Log(a*i)/x));
}
public int Calc_Cardano()//根据Cardano解三次方程
{
双p,q,u,v;
双r,α;
国际关系;
res=0;
如果(a1!=0)
{
a=b/a1;
b=c/a1;
c=d/a1;
p=-(a*a/3.0)+b;
q=(2.0/27.0*a*a*a)-(a*b/3.0)+c;
d=q*q/4.0+p*p*p/27.0;
如果(数学Abs(d)0,D==0,D<0
如果(d>1e-20)
{
u=Xroot(-q/2.0+数学Sqrt(d),3.0);
v=Xroot(-q/2.0-数学Sqrt(d),3.0);
x1.real=u+v-a/3.0;
x2.real=-(u+v)/2.0-a/3.0;
x2.imag=数学Sqrt(3.0)/2.0*(u-v);
x3.real=x2.real;
x3.imag=-x2.imag;
res=1;
}
if(Math.Abs(d)0)//如果q>0,角度变成PI+alpha
alpha=Math.PI+alpha;
x1.real=Xroot(r,3.0)*(Math.Cos((6.0*Math.PI-alpha)/3.0)+Math.Cos(alpha/3.0))-a/3.0;
x2.real=Xroot(r,3.0)*(Math.Cos((2.0*Math.PI+alpha)/3.0)+Math.Cos((4.0*Math.PI-alpha)/3.0))-a/3.0;
x3.real=Xroot(r,3.0)*(Math.Cos((4.0*Math.PI+alpha)/3.0)+Math.Cos((2.0*Math.PI-alpha)/3.0))-a/3.0;
res=3;
}
}
其他的
res=0;
返回res;
}
一些事情

  • Math.Sign
    将返回零对零,这恰好是您在本例中想要的结果,但可能您在代码或算法更改方面没有那么幸运
  • 您将遇到舍入问题,并且在应该执行时不会执行
    cubeDiscr==0
    分支。您可能存在舍入问题,并出于同样的原因执行错误的
    >0
    <0
    分支。改为在0的增量范围内进行测试(见下文)
  • 但是
    cubeDiscr==0
    分支是错误的,因为1)您没有计算
    v
    ,2)
    2u
    是一个值为2的
    UInt32
    ,而不是
    2*u
  • 计算alpha是错误的(见下文)
  • (可能还有更多,但我一眼就看到了这些)
关于计算α:

double alpha = Math.Atan(Math.Sqrt(-cubeDiscr) / (-Q / 2.0));
不一样

double alpha = Math.Atan(Math.Sqrt(-d) / q * 2.0);
if (q > 0)                         // if q > 0 the angle becomes  PI + alpha
    alpha = Math.PI + alpha;
使用该页面中包含的代码有什么问题

public double Xroot(double a, double x)
{
    double i = 1;
    if (a < 0)
        i = -1;
    return (i * Math.Exp( Math.Log(a*i)/x));
}

public int Calc_Cardano()  // solve cubic equation according to cardano
{
    double p, q, u, v;
    double r, alpha;
    int res;
    res = 0;
    if (a1 != 0)
    {
        a = b / a1;
        b = c / a1;
        c = d / a1;

        p = -(a * a / 3.0) + b;
        q = (2.0 / 27.0 * a * a * a) - (a * b / 3.0) + c;
        d = q * q / 4.0 + p * p * p / 27.0;
        if (Math.Abs(d) < Math.Pow(10.0, -11.0))
            d = 0;
        // 3 cases D > 0, D == 0 and D < 0
        if (d > 1e-20)
        {
            u = Xroot(-q / 2.0 + Math.Sqrt(d), 3.0);
            v = Xroot(-q / 2.0 - Math.Sqrt(d), 3.0);
            x1.real = u + v - a / 3.0;
            x2.real = -(u + v) / 2.0 - a / 3.0;
            x2.imag = Math.Sqrt(3.0) / 2.0 * (u - v);
            x3.real = x2.real;
            x3.imag = -x2.imag;
            res = 1;
        }
        if (Math.Abs(d) <= 1e-20)
        {
            u = Xroot(-q / 2.0, 3.0);
            v = Xroot(-q / 2.0, 3.0);
            x1.real = u + v - a / 3.0;
            x2.real = -(u + v) / 2.0 - a / 3.0;
            res = 2;
        }
        if (d < -1e-20)
        {
            r = Math.Sqrt(-p * p * p / 27.0);
            alpha = Math.Atan(Math.Sqrt(-d) / q * 2.0);
            if (q > 0)                         // if q > 0 the angle becomes  PI + alpha
                alpha = Math.PI + alpha;

            x1.real = Xroot(r, 3.0) * (Math.Cos((6.0 * Math.PI - alpha) / 3.0) + Math.Cos(alpha / 3.0)) - a / 3.0;
            x2.real = Xroot(r, 3.0) * (Math.Cos((2.0 * Math.PI + alpha) / 3.0) + Math.Cos((4.0 * Math.PI - alpha) / 3.0)) - a / 3.0;
            x3.real = Xroot(r, 3.0) * (Math.Cos((4.0 * Math.PI + alpha) / 3.0) + Math.Cos((2.0 * Math.PI - alpha) / 3.0)) - a / 3.0;
            res = 3;
        }
    }
    else
        res = 0;
    return res;
 }
公共双x根(双a,双x)
{
双i=1;
if(a<0)
i=-1;
返回(i*Math.Exp(Math.Log(a*i)/x));
}
public int Calc_Cardano()//根据Cardano解三次方程
{
双p,q,u,v;
双r,α;
国际关系;
res=0;
如果(a1!=0)
{
a=b/a1;
b=c