public static double[] Solve(double c, double d) { if (d < 0) { return(Solve(c, -d).Reverse().Select(x => - x).ToArray()); } // 3重解の場合 (c = d = 0) を含む if (d == 0) { return c >= 0 ? new[] { 0D } } : new[] { -Sqrt(-c), 0D, Sqrt(-c) }; // この式では誤差が大きくなることがあります。 var det3 = -4 * c * c * c - 27 * d * d; // 重解の場合 if (det3.EqualsNearly(0, 8)) { return new[] { -2 * Sqrt(-c / 3), Sqrt(-c / 3) } } ; // 負の実数解 var x1 = SolveNegative(); if (det3 < 0) { return new[] { x1 } } ; // f(x) = (x - x_1) (x^2 + x_1 x + x_1^2 + c) var sqrt_det2 = Sqrt(-3 * x1 * x1 - 4 * c); return(new[] { x1, (-x1 - sqrt_det2) / 2, (-x1 + sqrt_det2) / 2 }); double SolveNegative() { var f = CubicEquation1.CreateFunction(c, d); var f1 = CubicEquation1.CreateDerivative(c); var x0 = -1D; while (f(x0) > 0) { x0 *= 2; } return(NewtonMethod.Solve(f, f1, x0)); } } } }
public static double[] Solve(double c, double d) { if (d < 0) { return(Solve(c, -d).Reverse().Select(x => - x).ToArray()); } // 自明解 if (d == 0 && c >= 0) { return new[] { 0D } } ; // 負の実数解 var x1 = SolveNegative(); // f(x) = (x - x_1) (x^2 + x_1 x + x_1^2 + c) var det = -3 * x1 * x1 - 4 * c; if (det < 0) { return new[] { x1 } } ; if (det == 0) { return new[] { x1, -x1 / 2 } } ; return(new[] { x1, (-x1 - Sqrt(det)) / 2, (-x1 + Sqrt(det)) / 2 }); double SolveNegative() { var f = CubicEquation1.CreateFunction(c, d); var f1 = CubicEquation1.CreateDerivative(c); var x0 = -1D; while (f(x0) > 0) { x0 *= 2; } return(NewtonMethod.Solve(f, f1, x0)); } } } }
public void Solve_2() { void Test(double c, double d) { var actual = target2(c, d); var det = -4 * c * c * c - 27 * d * d; Assert.AreEqual(c == 0 & d == 0 || det < 0 ? 1 : det == 0 ? 2 : 3, actual.Length); var f = CubicEquation1.CreateFunction(c, d); foreach (var x in actual) { Assert2.AreNearlyEqual(0, f(x)); } } for (int c = -100; c <= 100; c++) { for (int d = 0; d <= 100; d++) { Test(c, d); } } for (int c1 = 1; c1 <= 20; c1++) { for (double c2 = -20; c2 <= 20; c2++) { for (int d1 = 1; d1 <= 20; d1++) { for (double d2 = -20; d2 <= 20; d2++) { Test(c2 / c1, d2 / d1); } } } } }
public void Solve_1() { void Test(double a, double b, double c, double d) { var actual = target1(a, b, c, d); var f = CubicEquation1.CreateFunction(a, b, c, d); foreach (var x in actual) { Assert2.AreNearlyEqual(0, f(x), -9); } } // A case with error for the determinant. Test(20, 8, 0, 0); Test(6, 47, 0, 0); Test(5, 33, 0, 0); Test(2, 49, 0, 0); Test(5, 50, -6, 5); for (int a = -15; a <= 15; a++) { if (a != 0) { for (int b = -15; b <= 15; b++) { for (int c = -15; c <= 15; c++) { for (int d = -15; d <= 15; d++) { Test(a, b, c, d); } } } } } }