实现三角方程解的Cardano方法

时间:2018-04-05 11:57:41

标签: c# algorithm math

我试图通过使用描述here的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;
    }

1 个答案:

答案 0 :(得分:2)

一些事情

  • Math.Sign将在零上返回零,在这种情况下恰好是您想要的,但也许您对代码或算法更改并不是那么幸运。
  • 您应该有舍入问题而不应该执行cubeDiscr == 0分支。由于同样的原因,您可能有舍入问题并执行了错误的> 0< 0分支。在零之间测试(参见下文)。
  • 但是cubeDiscr == 0分支是错误的,因为1)您没有计算v和2)2uUInt32,其值为2,而不是2*u
  • 计算alpha错误(见下文)
  • (可能会有更多,但我只能快速浏览一下)

计算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;
 }