使用 C# 求三次方程的根时遇到问题

Having trouble in finding roots of a cubic equation with C#

最近我和小伙伴们决定做一个多功能计算器来自动求 二次方程的根。 毕竟这并不是那么具有挑战性,所以我们决定进入下一个阶段,为三次方程制作一个计算器。

(ax^3 + bx^2 + cx + d)

然而,我们遇到了一些小问题,无论我们怎么努力,结果还是一样。 我们是编码方面的初学者,所以我们不确定我们是否真的在这里犯了一些愚蠢的错误 但至少我们想向别人学习一些东西。

基本上,我们尝试了很多不同三次方程的组合,甚至重新编码了整个东西。问题是我们得到的结果总是错误的,但只针对二阶根和三阶根的实部。

为了更好的理解,我们尝试了9x^3 + 8x^2 + 7x + 6,作为例子。
根据三次方程式计算器网站,正确答案是

(The website)

First root = -0.87285
Second root = -0.00802 + 0.87391 i
Third root = -0.00802 - 0.87391 i

然而,我们对这个等式的结果是:

First root = -0.87285
Second root = -0.2963 + 0.87391 i
Third root = -0.2963 + -0.87391 i

很明显只有部分二阶和三阶根是错误的。
我们曾尝试寻找类似的帖子来提供帮助,但这些帖子对我们来说有点太难理解,或者这些问题与我们的问题不一样。
我们期待找到此问题的解决方案和原因

我们已将求三次方程根的公式分为 5 个部分。 (rootp1-rootp5)
公式按照维基百科三次方程中文版页面中的公式进行编码。
(The formulas)
我们还将根的实部四舍五入为 5 位数。

(有些代码行可能是多余的,但正如我所提到的,我们是编码新手)

代码(C#):

using System;
using System.Numerics;

namespace ComplexNumbers
{
    public class ComplexNo
    {
        public static void Main()
        {
            Console.WriteLine("Cubic Equation Calculator :");
            Console.Write("Insert first coefficient : ");
            double a = Convert.ToDouble(Console.ReadLine());
            Console.Write("Insert second coefficient : ");
            double b = Convert.ToDouble(Console.ReadLine());
            Console.Write("Insert third coefficient : ");
            double c = Convert.ToDouble(Console.ReadLine());
            Console.Write("Insert constant : ");
            double d = Convert.ToDouble(Console.ReadLine());

                Console.WriteLine(" ");
                Console.WriteLine("Solve for " + a + "x" + "^3 " + b + "x^2 " + c + "x " + d + " :");
                double rootp1 = -(b / (3 * a));
                double rootp2 = (b * c / (6 * Math.Pow(a, 2))) - (Math.Pow(b, 3) / (27 * Math.Pow(a, 3))) - (d / (2 * a));
                double rootp3 = (c / (3 * a)) - (Math.Pow(b, 2) / (9 * Math.Pow(a, 2)));
                Complex root1 = rootp1 + Math.Cbrt(rootp2 + Math.Sqrt(Math.Pow(rootp2, 2) + Math.Pow(rootp3, 3))) +
                                 Math.Cbrt(rootp2 - Math.Sqrt(Math.Pow(rootp2, 2) + Math.Pow(rootp3, 3)));
                Complex rootp4 = new Complex(-1 / 2, Math.Sqrt(3) / 2);
                Complex rootp5 = new Complex(-1 / 2, -(Math.Sqrt(3) / 2));
                Complex root2 = rootp1 + (rootp4 * Math.Cbrt(rootp2 + Math.Sqrt(Math.Pow(rootp2, 2) + Math.Pow(rootp3, 3)))) +
                                 (rootp5 * Math.Cbrt(rootp2 - Math.Sqrt(Math.Pow(rootp2, 2) + Math.Pow(rootp3, 3))));
                Complex root3 = rootp1 + (rootp5 * Math.Cbrt(rootp2 + Math.Sqrt(Math.Pow(rootp2, 2) + Math.Pow(rootp3, 3)))) +
                                 (rootp4 * Math.Cbrt(rootp2 - Math.Sqrt(Math.Pow(rootp2, 2) + Math.Pow(rootp3, 3))));

                Console.WriteLine(" ");
                Console.WriteLine("Results :");
                Console.WriteLine("First Root :");
                string root1rp = Convert.ToString(Math.Round(root1.Real, 5));
                string root1ip = Convert.ToString(Math.Round(root1.Imaginary, 5));
                Console.WriteLine(root1rp + " + " + root1ip + "i");
                Console.WriteLine("Second Root :");
                string root2rp = Convert.ToString(Math.Round(root2.Real, 5));
                string root2ip = Convert.ToString(Math.Round(root2.Imaginary, 5));
                Console.WriteLine(root2rp + " + " + root2ip + "i");
                Console.WriteLine("Third Root :");
                string root3rp = Convert.ToString(Math.Round(root3.Real, 5));
                string root3ip = Convert.ToString(Math.Round(root3.Imaginary, 5));
                Console.WriteLine(root3rp + " + " + root3ip + "i");

                Console.ReadLine();
        }
    }
}

(很抱歉让这个话题这么长而且我的语法不好)

我承认我没有尝试过您的代码,但您的原始计算中可能存在数学转换问题。试试这个:

double rootp2 = ((double)b * (double)c / (6D * Math.Pow(a, 2D))) - (Math.Pow(b, 3D) / (27D * Math.Pow(a, 3D))) - ((double)d / (2D * (double)a));

如果这有所不同,您必须通过其他计算传播类似的更改(将所有变量转换为 (double) 并将内联常量转换为双精度 D)。

问题出在这一行 Complex rootp4 = new Complex(-1 / 2, Math.Sqrt(3) / 2);-1/2 使用整数除法计算结果为 0。

此代码有效。

        Console.WriteLine(" ");
        Console.WriteLine("Solve for " + a + "x" + "^3 " + b + "x^2 " + c + "x " + d + " :");
        double minusBover3a = -(b / (3 * a));
        double rootp2 = (b * c / (6 * Math.Pow(a, 2))) - (Math.Pow(b, 3) / (27 * Math.Pow(a, 3))) - (d / (2 * a));
        double rootp3 = (c / (3 * a)) - (Math.Pow(b, 2) / (9 * Math.Pow(a, 2)));

        double bigCubeRootPlus = Math.Cbrt(rootp2 + Math.Sqrt(Math.Pow(rootp2, 2) + Math.Pow(rootp3, 3)));
        double bigCubeRootMinus = Math.Cbrt(rootp2 - Math.Sqrt(Math.Pow(rootp2, 2) + Math.Pow(rootp3, 3)));
        // ** THIS IS THE PROBLEM.  "-1/2" uses integer division, so this complex has 0 for real part
        Complex complexPlus = new Complex(-1.0 / 2, Math.Sqrt(3) / 2);
        Complex complexMinus = new Complex(-1.0 / 2, -(Math.Sqrt(3) / 2));

        Complex root1 = minusBover3a + bigCubeRootPlus + bigCubeRootMinus;
        Complex root2 = minusBover3a + complexPlus * bigCubeRootPlus + complexMinus * bigCubeRootMinus;
        Complex root3 = minusBover3a + complexMinus * bigCubeRootPlus + complexPlus * bigCubeRootMinus;

以下是您的代码的替代方法:

    //  from https://www.daniweb.com/programming/software-development/
    //  code/454493/solving-the-cubic-equation-using-the-complex-struct

    //  algorithm described in
    //  https://en.wikipedia.org/wiki/Cubic_equation#General_cubic_formula
    const int NRoots = 3;

    double SquareRootof3 = Math.Sqrt(3);
    // the 3 cubic roots of 1
    var CubicUnity = new List<Complex>(NRoots)
                { new Complex(1, 0), 
                  new Complex(-0.5, -SquareRootof3 / 2.0), 
                  new Complex(-0.5, SquareRootof3 / 2.0) };
    // intermediate calculations
    double DELTA = 18 * a * b * c * d 
                 - 4 * b * b * b * d 
                 + b * b * c * c 
                 - 4 * a * c * c * c 
                 - 27 * a * a * d * d;
    double DELTA0 = b * b - 3 * a * c;
    double DELTA1 = 2 * b * b * b 
                  - 9 * a * b * c 
                  + 27 * a * a * d;
    Complex DELTA2 = -27 * a * a * DELTA;
    Complex C = Complex.Pow((DELTA1 + Complex.Pow(DELTA2, 0.5)) / 2, 1 / 3.0);

    for (int i = 0; i < NRoots; i++)
    {
        Complex M = CubicUnity[i] * C;
        Complex Root = -1.0 / (3 * a) * (b + M + DELTA0 / M);
        Console.WriteLine();
        Console.WriteLine($"Root {i+1}:");
        Console.WriteLine($"Real      {Root.Real:0.#####}");
        Console.WriteLine($"Imaginary {Root.Imaginary:0.#####}i");
    }