/// <summary> /// Solves the equation \f$a0 + a1 x + a2 x^2 + a3 x^3 = 0\f$, returning all real values of /// \f$x\f$ for which the equation is true. See https://en.wikipedia.org/wiki/Cubic_equation for the /// algorithm used. /// </summary> public static IEnumerable <double> Solve(double a0, double a1, double a2, double a3) { DebugUtil.AssertFinite(a3, nameof(a3)); DebugUtil.AssertFinite(a2, nameof(a2)); DebugUtil.AssertFinite(a1, nameof(a1)); DebugUtil.AssertFinite(a0, nameof(a0)); if (Math.Abs(a3) <= 0.005) { foreach (double v in QuadraticFunction.Solve(a0, a1, a2)) { CubicFunction f = new CubicFunction(a0, a1, a2, a3); yield return(f.NewtonRaphson(v)); } yield break; } double delta0 = a2 * a2 - 3.0 * a3 * a1; double delta1 = 2.0 * a2 * a2 * a2 - 9.0 * a3 * a2 * a1 + 27.0 * a3 * a3 * a0; Complex p1 = Complex.Sqrt(delta1 * delta1 - 4.0 * delta0 * delta0 * delta0); // The sign we choose in the next equation is arbitrary. To prevent a divide-by-zero down the line, if p2 is // zero, we must choose the opposite sign to make it nonzero: Complex p2 = delta1 + p1; Complex c = Complex.Pow(0.5 * p2, (1.0 / 3.0)); Complex xi = -0.5 + 0.5 * Complex.Sqrt(-3.0); List <Complex> roots = new List <Complex> { -1.0 / (3.0 * a3) * (a2 + c + delta0 / c), -1.0 / (3.0 * a3) * (a2 + xi * c + delta0 / (xi * c)), -1.0 / (3.0 * a3) * (a2 + xi * xi * c + delta0 / (xi * xi * c)), }; foreach (Complex root in roots) { if (Math.Abs(root.Imaginary) <= 0.05) { DebugUtil.AssertFinite(root.Real, nameof(root.Real)); yield return(root.Real); } } }
/// <summary> /// Solves the equation \f$d + c x + b x^2 + a x^3 = 0\f$, returning all real values of /// \f$x\f$ for which the equation is true. See https://en.wikipedia.org/wiki/Cubic_equation for the /// algorithm used. /// </summary> public static IEnumerable <double> Solve(double d, double c, double b, double a) { DebugUtil.AssertFinite(a, nameof(a)); DebugUtil.AssertFinite(b, nameof(b)); DebugUtil.AssertFinite(c, nameof(c)); DebugUtil.AssertFinite(d, nameof(d)); if (Math.Abs(a) <= 0.005f) { foreach (double v in QuadraticFunction.Solve(d, c, b)) { yield return(v); } yield break; } double delta0 = b * b - 3.0 * a * c; double delta1 = 2.0 * b * b * b - 9.0 * a * b * c + 27.0 * a * a * d; Complex p1 = Complex.Sqrt(delta1 * delta1 - 4.0 * delta0 * delta0 * delta0); // The sign we choose in the next equation is arbitrary. To prevent a divide-by-zero down the line, if p2 is // zero, we must choose the opposite sign to make it nonzero: Complex p2 = delta1 + p1; Complex C = Complex.Pow(0.5 * p2, (1.0 / 3.0)); Complex xi = -0.5 + 0.5 * Complex.Sqrt(-3.0); List <Complex> roots = new List <Complex> { -1.0 / (3.0 * a) * (b + C + delta0 / C), -1.0 / (3.0 * a) * (b + xi * C + delta0 / (xi * C)), -1.0 / (3.0 * a) * (b + xi * xi * C + delta0 / (xi * xi * C)), }; foreach (Complex root in roots) { if (Math.Abs(root.Imaginary) <= 0.05) { DebugUtil.AssertFinite(root.Real, nameof(root.Real)); yield return(root.Real); } } }
/// <summary> /// Solves a general equation \f$a_0 + a_1 x + a_2 x^2 = 0\f$, returning all real values of /// \f$x\f$ for which the equation is true using the 'abc-formula'. /// </summary> public static IEnumerable <double> Solve(double a0, double a1, double a2) { DebugUtil.AssertFinite(a0, nameof(a0)); DebugUtil.AssertFinite(a1, nameof(a1)); DebugUtil.AssertFinite(a2, nameof(a2)); if (Math.Abs(a2) <= 0.005) { if (Math.Abs(a1) <= 0.005) { // There are no roots found: yield break; } else { // There is a single root, found from solving the linear equation with a1=0. We find a point close // to the root using the linear approximation, after which we approach the true solution using the // Newton-Raphson method: QuadraticFunction f = new QuadraticFunction(a0, a1, a2); yield return(f.NewtonRaphson(-a0 / a1)); } yield break; } double x1 = 0.5 * (-a1 + Math.Sqrt(a1 * a1 - 4.0 * a0 * a2)) / a2; double x2 = 0.5 * (-a1 - Math.Sqrt(a1 * a1 - 4.0 * a0 * a2)) / a2; if (Double.IsNaN(x1) == false) { yield return(x1); } if (Double.IsNaN(x2) == false) { yield return(x2); } }
/// <summary> /// Solves the equation \f$a_0 + a_1 x + a_2 x^2 = 0\f$, returning all real values of /// \f$x\f$ for which the equation is true using the 'abc-formula' using the parameters /// in this instance of QuadraticFunction. /// </summary> public IEnumerable <float> Roots() { return(QuadraticFunction.Solve(a0, a1, a2)); }