继续我的最后一个问题:在 C# 中计算四次方程的根时遇到问题

Continuation to my last question : Having trouble in calculating roots of a quartic equation in C#

这个帖子是我的的延续。
我和小伙伴正在尝试做一个可以自动计算的多功能计算器 二次、三次甚至四次方程的根。 但是,我们 运行 遇到了一些问题,以前是三次方程,现在是 四次方程

与上一个主题不同,我们将提供图像,包括我们程序计算的答案和WolframAlpha计算的答案,而不是将它们放在此处的文本中。
四次方程将表示为

ax^4 + bx^3 + cx^2 + dx + e

Our method of finding the roots of a quartic equation. (在另一个网站的另一个线程中找到)

a=1 b=2 c=-41 d=-42 e=360
a=1 b=-0.25 c=-0.85 d=1.45 e=-4.35
a=1 b=2 c=3 d=4 e=5

如图所示,程序计算的判别式始终正确。但是,方程的根有时会导致错误 (NaN),并且根的性质也会导致错误,但与根一样频繁。

根据以上提供的所有信息,我们希望知道以下问题的答案:

如前一帖所述,我们才刚刚开始学习C#。
为了您的方便,我们对一些代码行做了注释。

代码(C#):

using System;
using System.Numerics;

namespace ComplexNumbers
{
    public class ComplexNo
    {
        public static void Main()
        {
            // prompting for coefficients and constant
            Console.WriteLine("Quartic 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 fourth coefficient : ");
            double d = Convert.ToDouble(Console.ReadLine());
            Console.Write("Insert constant : ");
            double e = Convert.ToDouble(Console.ReadLine());
            // discriminant
            double discriminant = 256 * Math.Pow(a, 3) * Math.Pow(e, 3) - (192 * Math.Pow(a, 2) * b * d * Math.Pow(e, 2)) -
                (128 * Math.Pow(a, 2) * Math.Pow(c, 2) * Math.Pow(e, 2)) + (144 * Math.Pow(a, 2) * c * Math.Pow(d, 2) * e) -
                (27 * Math.Pow(a, 2) * Math.Pow(d, 4)) + (144 * a * Math.Pow(b, 2) * c * Math.Pow(e, 2)) -
                (6 * a * Math.Pow(b, 2) * Math.Pow(d, 2) * e) - (80 * a * b * Math.Pow(c, 2) * d * e) +
                (18 * a * b * c * Math.Pow(d, 3)) + (16 * a * Math.Pow(c, 4) * e) - (4 * a * Math.Pow(c, 3) * Math.Pow(d, 2)) -
                (27 * Math.Pow(b, 4) * Math.Pow(e, 2)) + (18 * Math.Pow(b, 3) * c * d * e) -
                (4 * Math.Pow(b, 3) * Math.Pow(d, 3)) - (4 * Math.Pow(b, 2) * Math.Pow(c, 3) * e) +
                (Math.Pow(b, 2) * Math.Pow(c, 2) * Math.Pow(d, 2));

            // different parts of the roots presented in p1-p6
            double p1 = (2 * Math.Pow(c, 3)) - (9 * b * c * d) + (27 * a * Math.Pow(d, 2)) + (27 * Math.Pow(b, 2) * e) -
                (72 * a * c * e);
            double p2 = p1 + Math.Sqrt(-4.0 * Math.Pow(Math.Pow(c, 2) - (3 * b * d) + (12 * a * e), 3) + Math.Pow(p1, 2));
            double p3 = ((Math.Pow(c, 2) - (3 * b * d) + (12 * a * e)) / (3 * a * Math.Cbrt(p2 / 2))) + (Math.Cbrt(p2 / 2) / (3 * a));
            double p4 = Math.Sqrt((Math.Pow(b, 2) / (4 * Math.Pow(a, 2))) - (2 * c / (3 * a)) + p3);
            double p5 = (Math.Pow(b, 2) / (2 * Math.Pow(a, 2))) - (4 * c / (3 * a)) - p3;
            double p6 = ((-(Math.Pow(b, 3) / Math.Pow(a, 3))) + (4 * b * c / Math.Pow(a, 2)) - (8 * d / a)) / (4 * p4);
            // calculating roots with p1-p6
            Complex x1 = (-(b / (4 * a))) - (p4 / 2) - (Math.Sqrt(p5 - p6) / 2);
            Complex x2 = (-(b / (4 * a))) - (p4 / 2) + (Math.Sqrt(p5 - p6) / 2);
            Complex x3 = (-(b / (4 * a))) + (p4 / 2) - (Math.Sqrt(p5 - p6) / 2);
            Complex x4 = (-(b / (4 * a))) + (p4 / 2) + (Math.Sqrt(p5 - p6) / 2);

            // polynomials used for testing the nature of roots
            double P = (8 * a * c) - (3 * Math.Pow(b, 2));
            double R = Math.Pow(b, 3) + (8 * d * Math.Pow(a, 2)) - (4 * a * b * c);
            double deltazero = Math.Pow(c, 2) - (3 * b * d) + (12 * a * e);
            double D = (64 * Math.Pow(a, 3) * e) - (16 * Math.Pow(a, 2) * Math.Pow(c, 2)) +
                (16 * a * Math.Pow(b, 2) * c) - (16 * Math.Pow(a, 2) * b * d) - (3 * Math.Pow(b, 4));

            // results
            Console.WriteLine(" ");
            Console.WriteLine("Solve for " + a + "x^4 " + " + " + b + "x^3 " + " + " + c + "x^2 " + " + " + d + "x " + " + " + e + " :");
            Console.WriteLine(" ");
            Console.WriteLine("Results :");
            Console.WriteLine("First Root :");
            string x1rp = Convert.ToString(Math.Round(x1.Real, 5)); // rounding up both real and unreal parts
            string x1ip = Convert.ToString(Math.Round(x1.Imaginary, 5));
            if (x1.Imaginary == 0)
            {
                Console.WriteLine(x1rp);
            }
            else if (x1.Imaginary < 0) // testing if unreal part is negative
            {
                Console.WriteLine(x1rp + " - " + Math.Round(Complex.Abs(x1.Imaginary)) + "i");
            }
            else
            {
                Console.WriteLine(x1rp + " + " + x1ip + "i");
            }
            string x2rp = Convert.ToString(Math.Round(x2.Real, 5)); // rounding up both real and unreal parts
            string x2ip = Convert.ToString(Math.Round(x2.Imaginary, 5));
            if (x2.Imaginary == 0)
            {
                Console.WriteLine(x2rp);
            }
            else if (x2.Imaginary < 0) // testing if unreal part is negative
            {
                Console.WriteLine(x2rp + " - " + Math.Round(Complex.Abs(x2.Imaginary)) + "i");
            }
            else
            {
                Console.WriteLine(x2rp + " + " + x2ip + "i");
            }
            string x3rp = Convert.ToString(Math.Round(x3.Real, 5)); // rounding up both real and unreal parts
            string x3ip = Convert.ToString(Math.Round(x3.Imaginary, 5));
            if (x3.Imaginary == 0)
            {
                Console.WriteLine(x3rp);
            }
            else if (x3.Imaginary < 0) // testing if unreal part is negative
            {
                Console.WriteLine(x3rp + " - " + Math.Round(Complex.Abs(x3.Imaginary)) + "i");
            }
            else
            {
                Console.WriteLine(x3rp + " + " + x3ip + "i");
            }
            string x4rp = Convert.ToString(Math.Round(x4.Real, 5)); // rounding up both real and unreal parts
            string x4ip = Convert.ToString(Math.Round(x4.Imaginary, 5));
            if (x4.Imaginary == 0)
            {
                Console.WriteLine(x4rp);
            }
            else if (x4.Imaginary < 0) // testing if unreal part is negative
            {
                Console.WriteLine(x4rp + " - " + Math.Round(Complex.Abs(x4.Imaginary)) + "i");
            }
            else
            {
                Console.WriteLine(x4rp + " + " + x4ip + "i");
            }

            // discriminant tests
            if (discriminant == 0) // if delta = 0
            {
                if (P < 0 && D < 0 && deltazero != 0)
                {
                    string result4 = "The equation yields a pair of double roots and two real simple roots.";
                    Console.WriteLine(result4);
                }
                else if (D > 0 || (P > 0 && (D != 0 || R != 0)))
                {
                    string result5 = "The equation yields a pair of double roots and two complex conjugate roots.";
                    Console.WriteLine(result5);
                }
                else if (deltazero == 0 && D != 0)
                {
                    string result6 = "The equation yields a triple root and a simple root.";
                    Console.WriteLine(result6);
                }
                else if (D == 0)
                {
                    if (P < 0)
                    {
                        string result7 = "The equation yields two pairs of real double roots.";
                        Console.WriteLine(result7);
                    }
                    else if (P > 0 && R == 0)
                    {
                        string result8 = "The equation yields two pairs of double complex conjugate roots.";
                        Console.WriteLine(result8);
                    }
                    else if (deltazero == 0)
                    {
                        string result9pt2 = Convert.ToString(-(b / (4 * a)));
                        string result9 = "All of the roots of this equation are equal to -b/4a";
                        Console.WriteLine(result9 + "(" + result9pt2 + ")");
                    }
                    else
                    {
                        Console.WriteLine("ERROR");
                    }
                }
                else
                {
                    Console.WriteLine("ERROR");
                }
            }
            else if (discriminant > 0) // if delta > 0
            {
                if (P < 0 && D < 0)
                {
                    string result2 = "The equation yields four distinct real roots.";
                    Console.WriteLine(result2);
                }
                else if (P > 0 && D > 0)
                {
                    string result3 = "The equation yields two pairs of unreal complex conjugate roots.";
                    Console.WriteLine(result3);
                }
                else
                {
                    Console.WriteLine("ERROR");
                }
            }
            else if (discriminant < 0) // if delta < 0
            {
                string result1 = "The equation yields two distinct real roots and two complex conjugate non-real roots.";
                Console.WriteLine(result1);
            }

            // discriminant of equation
            Console.WriteLine(" ");
            Console.WriteLine("Discriminant :");
            Console.WriteLine(discriminant);

            Console.ReadLine();
        }
    }
}

====================================== ==================
编辑#1
我试过了 another method to solve the roots.
然而,虚部对于最后2根仍然是错误的。
如果有人能指出我的错误,我将不胜感激。

// different parts of the roots presented in p1-p6
            double p = ((8 * a * c) - (3 * Math.Pow(b, 2))) / (8 * Math.Pow(a, 2));
            double q = (Math.Pow(b, 3) - (4 * a * b * c) + (8 * Math.Pow(a, 2) * d)) / (8 * Math.Pow(a, 3));
            double D0 = Math.Pow(c, 2) - (3 * b * d) + (12 * a * e);
            double D1 = (2 * Math.Pow(c, 3)) - (9 * b * c * d) + (27 * Math.Pow(b, 2) * e) + (27 * a * Math.Pow(d, 2)) - (72 * a * c * e);
            Complex Q = Complex.Pow((D1 + Complex.Sqrt(Complex.Pow(D1, 2) - (4 * Complex.Pow(D0, 3)))) / 2.0, 1.0 / 3.0);
            Complex S = 1.0 / 2.0 * Complex.Sqrt((-2.0 / 3.0 * p) + (1.0 / (3 * a) * (Q + (D0 / Q))));
            // calculating roots with p1-p6
            Complex x1 = (-(b / 4 * a)) - S + (1.0 / 2.0 * Complex.Sqrt((-4.0 * Complex.Pow(S, 2)) - (2 * p) + (q / S)));
            Complex x2 = (-(b / 4 * a)) - S - (1.0 / 2.0 * Complex.Sqrt((-4.0 * Complex.Pow(S, 2)) - (2 * p) + (q / S)));
            Complex x3 = (-(b / 4 * a)) + S + (1.0 / 2.0 * Complex.Sqrt((-4.0 * Complex.Pow(S, 2)) - (2 * p) + (q / S)));
            Complex x4 = (-(b / 4 * a)) + S - (1.0 / 2.0 * Complex.Sqrt((-4.0 * Complex.Pow(S, 2)) - (2 * p) + (q / S)));

====================================== ==================
编辑#2
问题已解决。
该程序现在能够求解四次方程。造成上述问题的原因其实是我在代码行中的小错别字

我从 here 得到了一个有效的算法。好像比你的简单。

            double D0 = c * c - 3 * b * d + 12 * a * e;
            double D1 = 2 * c * c * c - 9 * b * c * d + 27 * b * b * e + 27 * a * d * d - 72 * a * c * e;
            double p = (8 * a * c - 3 * b * b) / (8 * a * a);
            double q = (b * b * b - 4 * a * b * c + 8 * a * a * d) / (8 * a * a * a);
            Complex Q = Complex.Pow((D1 + Complex.Sqrt(D1 * D1 - 4 * D0 * D0 * D0)) / 2, 1.0 / 3.0);
            Complex S = Complex.Sqrt(-2 * p / 3 + (Q + D0 / Q) / (3 * a)) / 2;
            Complex u = Complex.Sqrt(-4 * S * S - 2 * p + q / S) / 2;
            Complex v = Complex.Sqrt(-4 * S * S - 2 * p - q / S) / 2;
            Complex x1 = -b / (4 * a) - S + u;
            Complex x2 = -b / (4 * a) - S - u;
            Complex x3 = -b / (4 * a) + S + v;
            Complex x4 = -b / (4 * a) + S - v;

此外,根据 this page,您可以使用这个代替非常长的判别式来获取其符号:

            double discriminantSign = c * c - 3 * b * d + 12 * a * e;

我还注意到 4 个错别字,你在应用 Math.Round 函数时忘记提到小数点后 5 位,所以你的结果四舍五入为整数。

编辑:如果您的虚部仍有问题,那是因为您没有更正错别字:

     Console.WriteLine(x1rp + " - " + Math.Round(Complex.Abs(x1.Imaginary)) + "i");

进入:

     Console.WriteLine(x1rp + " - " + Math.Round(Complex.Abs(x1.Imaginary), 5) + "i");

除上述 4 行外,其他地方都保留 5 位小数。所以它正在四舍五入整数。全部替换。