1 package org.djutils.polynomialroots; 2 3 import org.djutils.complex.Complex; 4 import org.djutils.complex.ComplexMath; 5 6 /** 7 * PolynomialRoots2 implements functions to find all roots of linear, quadratic, cubic and quartic polynomials, as well as 8 * higher order polynomials without restrictions on the order. <br> 9 * <br> 10 * Copyright (c) 2020-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See 11 * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is 12 * distributed under a three-clause BSD-style license, which can be found at 13 * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. <br> 14 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a> 15 * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a> 16 */ 17 public final class PolynomialRoots2 18 { 19 /** 20 * Do not instantiate. 21 */ 22 private PolynomialRoots2() 23 { 24 // Do not instantiate 25 } 26 27 /** 28 * Emulate the F77 sign function. 29 * @param a double; the value to optionally sign invert 30 * @param b double; the sign of which determines what to do 31 * @return double; if b >= 0 then a; else -a 32 */ 33 private static double sign(final double a, final double b) 34 { 35 return b >= 0 ? a : -a; 36 } 37 38 /** 39 * LINEAR POLYNOMIAL ROOT SOLVER. 40 * <p> 41 * Calculates the root of the linear polynomial:<br> 42 * q1 * x + q0<br> 43 * @param q1 double; coefficient of the x term 44 * @param q0 double; independent coefficient 45 * @return Complex[]; the roots of the equation 46 */ 47 public static Complex[] linearRoots(final double q1, final double q0) 48 { 49 if (q1 == 0) 50 { 51 return new Complex[] {}; // No roots; return empty array 52 } 53 return linearRoots(q0 / q1); 54 } 55 56 /** 57 * LINEAR POLYNOMIAL ROOT SOLVER. 58 * <p> 59 * Calculates the root of the linear polynomial:<br> 60 * x + q0<br> 61 * @param q0 double; independent coefficient 62 * @return Complex[]; the roots of the equation 63 */ 64 public static Complex[] linearRoots(final double q0) 65 { 66 return new Complex[] {new Complex(-q0, 0)}; 67 } 68 69 /** 70 * QUADRATIC POLYNOMIAL ROOT SOLVER 71 * <p> 72 * Calculates all real + complex roots of the quadratic polynomial:<br> 73 * q2 * x^2 + q1 * x + q0<br> 74 * The code checks internally if rescaling of the coefficients is needed to avoid overflow. 75 * <p> 76 * The order of the roots is as follows:<br> 77 * 1) For real roots, the order is according to their algebraic value on the number scale (largest positive first, largest 78 * negative last).<br> 79 * 2) Since there can be only one complex conjugate pair root, no order is necessary.<br> 80 * q1 : coefficient of x term q0 : independent coefficient 81 * @param q2 double; coefficient of the quadratic term 82 * @param q1 double; coefficient of the x term 83 * @param q0 double; independent coefficient 84 * @return Complex[]; the roots of the equation 85 */ 86 public static Complex[] quadraticRoots(final double q2, final double q1, final double q0) 87 { 88 if (q2 == 0) 89 { 90 return linearRoots(q1, q0); 91 } 92 return quadraticRoots(q1 / q2, q0 / q2); 93 } 94 95 /** 96 * QUADRATIC POLYNOMIAL ROOT SOLVER 97 * <p> 98 * Calculates all real + complex roots of the quadratic polynomial:<br> 99 * x^2 + q1 * x + q0<br> 100 * The code checks internally if rescaling of the coefficients is needed to avoid overflow. 101 * <p> 102 * The order of the roots is as follows:<br> 103 * 1) For real roots, the order is according to their algebraic value on the number scale (largest positive first, largest 104 * negative last).<br> 105 * 2) Since there can be only one complex conjugate pair root, no order is necessary.<br> 106 * q1 : coefficient of x term q0 : independent coefficient 107 * @param q1 double; coefficient of the x term 108 * @param q0 double; independent coefficient 109 * @return Complex[]; the roots of the equation 110 */ 111 public static Complex[] quadraticRoots(final double q1, final double q0) 112 { 113 boolean rescale; 114 115 double a0, a1; 116 double k = 0, x, y, z; 117 118 // Handle special cases. 119 if (q0 == 0.0 && q1 == 0.0) 120 { 121 // Two real roots at 0,0 122 return new Complex[] {Complex.ZERO, Complex.ZERO}; 123 } 124 else if (q0 == 0.0) 125 { 126 // Two real roots; one of these is 0,0 127 // x^2 + q1 * x == x * (x + q1) 128 Complex nonZeroRoot = new Complex(-q1); 129 return new Complex[] {q1 > 0 ? Complex.ZERO : nonZeroRoot, q1 <= 0 ? nonZeroRoot : Complex.ZERO}; 130 } 131 else if (q1 == 0.0) 132 { 133 x = Math.sqrt(Math.abs(q0)); 134 135 if (q0 < 0.0) 136 { 137 // Two real roots, symmetrically around 0 138 return new Complex[] {new Complex(x, 0), new Complex(-x, 0)}; 139 } 140 else 141 { 142 // Two complex roots, symmetrically around 0 143 return new Complex[] {new Complex(0, x), new Complex(0, -x)}; 144 } 145 } 146 else 147 { 148 // The general case. Do rescaling, if either squaring of q1/2 or evaluation of 149 // (q1/2)^2 - q0 will lead to overflow. This is better than to have the solver 150 // crashed. Note, that rescaling might lead to loss of accuracy, so we only 151 // invoke it when absolutely necessary. 152 final double sqrtLPN = Math.sqrt(Double.MAX_VALUE); // Square root of the Largest Positive Number 153 rescale = (q1 > sqrtLPN + sqrtLPN); // this detects overflow of (q1/2)^2 154 155 if (!rescale) 156 { 157 x = q1 * 0.5; // we are sure here that x*x will not overflow 158 rescale = (q0 < x * x - Double.MAX_VALUE); // this detects overflow of (q1/2)^2 - q0 159 } 160 161 if (rescale) 162 { 163 x = Math.abs(q1); 164 y = Math.sqrt(Math.abs(q0)); 165 166 if (x > y) 167 { 168 k = x; 169 z = 1.0 / x; 170 a1 = sign(1.0, q1); 171 a0 = (q0 * z) * z; 172 } 173 else 174 { 175 k = y; 176 a1 = q1 / y; 177 a0 = sign(1.0, q0); 178 } 179 } 180 else 181 { 182 a1 = q1; 183 a0 = q0; 184 } 185 // Determine the roots of the quadratic. Note, that either a1 or a0 might 186 // have become equal to zero due to underflow. But both cannot be zero. 187 x = a1 * 0.5; 188 y = x * x - a0; 189 190 if (y >= 0.0) 191 { 192 // Two real roots 193 y = Math.sqrt(y); 194 195 if (x > 0.0) 196 { 197 y = -x - y; 198 } 199 else 200 { 201 y = -x + y; 202 } 203 204 if (rescale) 205 { 206 y = y * k; // very important to convert to original 207 z = q0 / y; // root first, otherwise complete loss of 208 } 209 else // root due to possible a0 = 0 underflow 210 { 211 z = a0 / y; 212 } 213 return new Complex[] {new Complex(Math.max(y, z), 0), new Complex(Math.min(y, z), 0)}; 214 } 215 else 216 { 217 // Two complex roots (zero real roots) 218 y = Math.sqrt(-y); 219 220 if (rescale) 221 { 222 x *= k; 223 y *= k; 224 } 225 return new Complex[] {new Complex(-x, y), new Complex(-x, -y)}; 226 } 227 } 228 } 229 230 /** 231 * CUBIC POLYNOMIAL ROOT SOLVER. 232 * <p> 233 * Calculates all (real and complex) roots of the cubic polynomial:<br> 234 * a * x^3 + b * x^2 + c * x + d<br> 235 * The roots are found using the Newton-Raphson algorithm for the first (real) root, and then deflate the equation to a 236 * quadratic equation to find the other two roots. 237 * <p> 238 * The order of the roots is as follows: 1) For real roots, the order is according to their algebraic value on the number 239 * scale (largest positive first, largest negative last). 2) Since there can be only one complex conjugate pair root, no 240 * order is necessary. 3) All real roots precede the complex ones. 241 * @param a3 double; coefficient of the cubic term 242 * @param a2 double; coefficient of the quadratic term 243 * @param a1 double; coefficient of the linear term 244 * @param a0 double; coefficient of the independent term 245 * @return Complex[]; array of Complex with all the roots; there can be one, two or three roots. 246 */ 247 public static Complex[] cubicRootsNewtonFactor(final double a3, final double a2, final double a1, final double a0) 248 { 249 // corner case: a == 0 --> quadratic equation 250 if (a3 == 0.0) 251 { 252 return quadraticRoots(a2, a1, a0); 253 } 254 255 // corner case: d == 0 --> solve x * (ax^2 + bx + c) = 0 256 if (a0 == 0.0) 257 { 258 Complex[] result = quadraticRoots(a3, a2, a1); 259 if (result.length == 0) 260 { 261 return new Complex[] {Complex.ZERO, Complex.ZERO, Complex.ZERO}; 262 } 263 else if (result.length == 1) 264 { 265 return new Complex[] {Complex.ZERO, Complex.ZERO, result[0]}; 266 } 267 else 268 { 269 return new Complex[] {Complex.ZERO, result[0], result[1]}; 270 } 271 } 272 273 double argmax = maxAbs(a3, a2, a1, a0); 274 double d = a0 / argmax; 275 double c = a1 / argmax; 276 double b = a2 / argmax; 277 double a = a3 / argmax; 278 279 // find the first real root 280 double[] args = new double[] {d, c, b, a}; 281 double root1 = rootNewtonRaphson(args, 0.0); 282 283 // check and apply bisection if this did not work 284 if (Double.isNaN(root1) || f(args, root1) > 1E-9) 285 { 286 double min = -64.0; 287 double max = 64.0; 288 int s1, s2; 289 do 290 { 291 min *= 2.0; 292 max *= 2.0; 293 if (max > 1E64) 294 { 295 throw new RuntimeException( 296 String.format("cannot find first root for %fx^3 + %fx^2 + %fx + %f = 0", a, b, c, d)); 297 } 298 s1 = (int) Math.signum(f(args, min)); 299 s2 = (int) Math.signum(f(args, max)); 300 } 301 while (s1 != 0 && s2 != 0 && s1 == s2); 302 root1 = rootBisection(args, min, max, 0.0); 303 // if (Double.isNaN(root1)) 304 // { 305 // throw new RuntimeException( 306 // String.format("cannot find first root for %fx^3 + %fx^2 + %fx + %f = 0", a, b, c, d)); 307 // } 308 // if (Math.abs(f(args, root1)) > 1E-6) 309 // { 310 // throw new RuntimeException(String.format("f(first root) != 0 for [%fx^3 + %fx^2 + %fx + %f = 0, but %f]", a, b, 311 // c, d, f(args, root1))); 312 // } 313 } 314 315 /*- 316 * Use Factor theory to factor out root 1 (r): 317 * if the equation is ax^3 + bx^2 + cx + d, and there is a root r, the equation equals: 318 * (x-r) (ax^2 + (b+ra)x + (c+r(b+ra)) + (ar^3+br^2+cr+d)/(x-r) where the last term becomes zero 319 * We can then solve find the other two roots by solving ax^2 + (b+ra)x + (c+r(b+ra)) = 0 320 */ 321 322 Complex[] rootsQuadaratic = quadraticRoots(a, b + root1 * a, c + root1 * (b + root1 * a)); 323 // if (Math.abs(f(args, rootsQuadaratic[0]).norm()) > 1E-6) 324 // { 325 // throw new RuntimeException(String.format("f(second root) != 0 for [%fx^3 + %fx^2 + %fx + %f = 0], but %f", a, b, c, 326 // d, f(args, root1))); 327 // } 328 // if (Math.abs(f(args, rootsQuadaratic[1]).norm()) > 1E-6) 329 // { 330 // throw new RuntimeException(String.format("f(second root) != 0 for [%fx^3 + %fx^2 + %fx + %f = 0], but %f", a, b, c, 331 // d, f(args, root1))); 332 // } 333 334 return new Complex[] {new Complex(root1), rootsQuadaratic[0], rootsQuadaratic[1]}; 335 } 336 337 /** 338 * CUBIC POLYNOMIAL ROOT SOLVER. 339 * <p> 340 * Calculates all (real and complex) roots of the cubic polynomial:<br> 341 * a * x^3 + b * x^2 + c * x + d<br> 342 * The roots are found using Cardano's algorithm. 343 * <p> 344 * The order of the roots is as follows: 1) For real roots, the order is according to their algebraic value on the number 345 * scale (largest positive first, largest negative last). 2) Since there can be only one complex conjugate pair root, no 346 * order is necessary. 3) All real roots precede the complex ones. 347 * @param a double; coefficient of the cubic term 348 * @param b double; coefficient of the quadratic term 349 * @param c double; coefficient of the linear term 350 * @param d double; coefficient of the independent term 351 * @return Complex[]; array of Complex with all the roots; there can be one, two or three roots. 352 */ 353 @SuppressWarnings("checkstyle:localvariablename") 354 public static Complex[] cubicRootsCardano(final double a, final double b, final double c, final double d) 355 { 356 // corner case: a == 0 --> quadratic equation 357 if (a == 0.0) 358 { 359 return quadraticRoots(b, c, d); 360 } 361 362 // corner case: d == 0 --> solve x * (ax^2 + bx + c) = 0 363 if (d == 0.0) 364 { 365 Complex[] result = quadraticRoots(a, b, c); 366 if (result.length == 0) 367 { 368 return new Complex[] {Complex.ZERO, Complex.ZERO, Complex.ZERO}; 369 } 370 else if (result.length == 1) 371 { 372 return new Complex[] {Complex.ZERO, Complex.ZERO, result[0]}; 373 } 374 else 375 { 376 return new Complex[] {Complex.ZERO, result[0], result[1]}; 377 } 378 } 379 380 // other cases: use Cardano's formula 381 double D0 = b * b - 3.0 * a * c; 382 double D1 = 2.0 * b * b * b - 9.0 * a * b * c + 27.0 * a * a * d; 383 if (D0 == 0 && D1 == 0) 384 { 385 // one real solution 386 Complex root = new Complex(rootNewtonRaphson(new double[] {d, c, b, a}, 0.0)); 387 return new Complex[] {root, root, root}; 388 } 389 Complex r = ComplexMath.sqrt(new Complex(D1 * D1 - 4.0 * D0 * D0 * D0)); 390 Complex s = r.plus(D1).times(0.5); 391 if (s.re == 0.0 && s.im == 0.0) 392 { 393 s = r.times(-1.0).plus(D1).times(0.5); 394 } 395 Complex C = ComplexMath.cbrt(s); 396 Complex x1 = C.plus(b).plus((C.reciprocal().times(D0))).times(-1.0 / (3.0 * a)); 397 Complex x2 = C.rotate(Math.toRadians(120.0)).plus(b).plus((C.rotate(Math.toRadians(120.0)).reciprocal().times(D0))) 398 .times(-1.0 / (3.0 * a)); 399 Complex x3 = C.rotate(Math.toRadians(-120.0)).plus(b).plus((C.rotate(Math.toRadians(-120.0)).reciprocal().times(D0))) 400 .times(-1.0 / (3.0 * a)); 401 return new Complex[] {x1, x2, x3}; 402 } 403 404 /** 405 * CUBIC POLYNOMIAL ROOT SOLVER. 406 * <p> 407 * Calculates all (real and complex) roots of the cubic polynomial:<br> 408 * a3 * x^3 + a2 * x^2 + a1 * x + a0<br> 409 * The roots are found using Durand-Kerner's algorithm. 410 * @param a3 double; coefficient of the cubic term 411 * @param a2 double; coefficient of the quadratic term 412 * @param a1 double; coefficient of the linear term 413 * @param a0 double; coefficient of the independent term 414 * @return Complex[]; array of Complex with all the roots; there can be one, two or three roots. 415 */ 416 public static Complex[] cubicRootsDurandKerner(final double a3, final double a2, final double a1, final double a0) 417 { 418 // corner case: a3 == 0 --> quadratic equation 419 if (a3 == 0.0) 420 { 421 return quadraticRoots(a2, a1, a0); 422 } 423 424 // other cases: use Durand-Kerner's algorithm 425 return rootsDurandKerner(new Complex[] {new Complex(a0 / a3), new Complex(a1 / a3), new Complex(a2 / a3), Complex.ONE}); 426 } 427 428 /** 429 * CUBIC POLYNOMIAL ROOT SOLVER. 430 * <p> 431 * Calculates all (real and complex) roots of the cubic polynomial:<br> 432 * a3 * x^3 + a2 * x^2 + a1 * x + a0<br> 433 * The roots are found using Aberth-Ehrlich's algorithm. 434 * @param a3 double; coefficient of the cubic term 435 * @param a2 double; coefficient of the quadratic term 436 * @param a1 double; coefficient of the linear term 437 * @param a0 double; coefficient of the independent term 438 * @return Complex[]; array of Complex with all the roots; there can be one, two or three roots. 439 */ 440 public static Complex[] cubicRootsAberthEhrlich(final double a3, final double a2, final double a1, final double a0) 441 { 442 // corner case: a3 == 0 --> quadratic equation 443 if (a3 == 0.0) 444 { 445 return quadraticRoots(a2, a1, a0); 446 } 447 448 // other cases: use Durand-Kerner's algorithm 449 return rootsAberthEhrlich( 450 new Complex[] {new Complex(a0 / a3), new Complex(a1 / a3), new Complex(a2 / a3), Complex.ONE}); 451 } 452 453 /** 454 * QUADRATIC POLYNOMIAL ROOT SOLVER. 455 * <p> 456 * Calculates all (real and complex) roots of the cubic polynomial:<br> 457 * a4 * x^4 + a3 * x^3 + a2 * x^2 + a1 * x + a0<br> 458 * The roots are found using Durand-Kerner's algorithm. 459 * @param a4 double; coefficient of the quartic term 460 * @param a3 double; coefficient of the cubic term 461 * @param a2 double; coefficient of the quadratic term 462 * @param a1 double; coefficient of the linear term 463 * @param a0 double; coefficient of the independent term 464 * @return Complex[]; array of Complex with all the roots; always 4 roots are returned; there can be double roots 465 */ 466 public static Complex[] quarticRootsDurandKerner(final double a4, final double a3, final double a2, final double a1, 467 final double a0) 468 { 469 // corner case: a4 == 0 --> cubic equation 470 if (a4 == 0.0) 471 { 472 return cubicRootsDurandKerner(a3, a2, a1, a0); 473 } 474 475 return rootsDurandKerner(new Complex[] {new Complex(a0 / a4), new Complex(a1 / a4), new Complex(a2 / a4), 476 new Complex(a3 / a4), Complex.ONE}); 477 } 478 479 /** 480 * QUADRATIC POLYNOMIAL ROOT SOLVER. 481 * <p> 482 * Calculates all (real and complex) roots of the cubic polynomial:<br> 483 * a4 * x^4 + a3 * x^3 + a2 * x^2 + a1 * x + a0<br> 484 * The roots are found using Aberth-Ehrlich's algorithm. 485 * @param a4 double; coefficient of the quartic term 486 * @param a3 double; coefficient of the cubic term 487 * @param a2 double; coefficient of the quadratic term 488 * @param a1 double; coefficient of the linear term 489 * @param a0 double; coefficient of the independent term 490 * @return Complex[]; array of Complex with all the roots; always 4 roots are returned; there can be double roots 491 */ 492 public static Complex[] quarticRootsAberthEhrlich(final double a4, final double a3, final double a2, final double a1, 493 final double a0) 494 { 495 // corner case: a4 == 0 --> cubic equation 496 if (a4 == 0.0) 497 { 498 return cubicRootsAberthEhrlich(a3, a2, a1, a0); 499 } 500 501 return rootsAberthEhrlich(new Complex[] {new Complex(a0 / a4), new Complex(a1 / a4), new Complex(a2 / a4), 502 new Complex(a3 / a4), Complex.ONE}); 503 } 504 505 /** the number of steps in solving the equation with the Durand-Kerner method. */ 506 private static final int MAX_STEPS_DURAND_KERNER = 100; 507 508 /** 509 * Polynomial root finder using the Durand-Kerner method, with complex coefficients for the polynomial equation. Own 510 * implementation. See <a href="https://en.wikipedia.org/wiki/Durand%E2%80%93Kerner_method"> 511 * https://en.wikipedia.org/wiki/Durand%E2%80%93Kerner_method</a> for brief information. 512 * @param a Complex[]; the complex factors of the polynomial, where the index i indicate the factor for x^i, so the 513 * polynomial is<br> 514 * a[n]x^n + a[n-1]x^(n-1) + ... + a[2]a^2 + a[1]x + a[0] 515 * @return Complex[] all roots of the equation, where real roots are coded with Im = 0 516 */ 517 public static Complex[] rootsDurandKerner(final Complex[] a) 518 { 519 int n = a.length - 1; 520 double radius = 1 + maxAbs(a); 521 522 // initialize the initial values, not as a real number and not as a root of unity 523 Complex[] p = new Complex[n]; 524 p[0] = new Complex(Math.sqrt(radius), Math.cbrt(radius)); 525 double rot = 350.123 / n; 526 for (int i = 1; i < n; i++) 527 { 528 p[i] = p[0].rotate(rot * i); 529 } 530 531 double maxError = 1.0; 532 int count = 0; 533 while (maxError > 0 && count < MAX_STEPS_DURAND_KERNER) 534 { 535 maxError = 0.0; 536 count++; 537 for (int i = 0; i < n; i++) 538 { 539 Complex factors = Complex.ONE; 540 for (int j = 0; j < n; j++) 541 { 542 if (i != j) 543 { 544 factors = factors.times(p[i].minus(p[j])); 545 } 546 } 547 Complex newValue = p[i].minus(f(a, p[i]).divideBy(factors)); 548 if (!(newValue.equals(p[i]))) 549 { 550 double error = newValue.minus(p[i]).norm(); 551 if (error > maxError) 552 { 553 maxError = error; 554 } 555 p[i] = newValue; 556 } 557 } 558 } 559 560 // correct for 1 ulp above or below zero; make that value zero instead 561 for (int i = 0; i < n; i++) 562 { 563 if (Math.abs(p[i].im) == Double.MIN_VALUE) 564 { 565 p[i] = new Complex(p[i].re, 0.0); 566 } 567 if (Math.abs(p[i].re) == Double.MIN_VALUE) 568 { 569 p[i] = new Complex(0.0, p[i].im); 570 } 571 } 572 573 return p; 574 } 575 576 /** the number of steps in solving the equation with the Durand-Kerner method. */ 577 private static final int MAX_STEPS_ABERTH_EHRLICH = 100; 578 579 /** 580 * Polynomial root finder using the Aberth-Ehrlich method or Aberth method, with complex coefficients for the polynomial 581 * equation. Own implementation. See <a href="https://en.wikipedia.org/wiki/Aberth_method"> 582 * https://en.wikipedia.org/wiki/Aberth_method</a> for brief information. 583 * @param a Complex[]; the complex factors of the polynomial, where the index i indicate the factor for x^i, so the 584 * polynomial is<br> 585 * a[n]x^n + a[n-1]x^(n-1) + ... + a[2]a^2 + a[1]x + a[0] 586 * @return Complex[] all roots of the equation, where real roots are coded with Im = 0 587 */ 588 public static Complex[] rootsAberthEhrlich(final Complex[] a) 589 { 590 int n = a.length - 1; 591 double radius = 1 + maxAbs(a); 592 593 // initialize the initial values, not as a real number and not as a root of unity 594 Complex[] p = new Complex[n]; 595 p[0] = new Complex(Math.sqrt(radius), Math.cbrt(radius)); 596 double rot = 350.123 / n; 597 for (int i = 1; i < n; i++) 598 { 599 p[i] = p[0].rotate(rot * i); 600 } 601 602 double maxError = 1.0; 603 int count = 0; 604 while (maxError > 0 && count < MAX_STEPS_ABERTH_EHRLICH) 605 { 606 maxError = 0.0; 607 count++; 608 for (int i = 0; i < n; i++) 609 { 610 Complex sum = Complex.ZERO; 611 for (int j = 0; j < n; j++) 612 { 613 if (i != j) 614 { 615 sum = sum.plus(Complex.ONE.divideBy(p[i].minus(p[j]))); 616 } 617 } 618 Complex pDivPDer = f(a, p[i]).divideBy(fDerivative(a, p[i])); 619 Complex w = pDivPDer.divideBy(Complex.ONE.minus(pDivPDer.times(sum))); 620 double error = w.norm(); 621 if (error > maxError) 622 { 623 maxError = error; 624 } 625 p[i] = p[i].minus(w); 626 } 627 } 628 629 // correct for 1 ulp above or below zero; make that value zero instead 630 for (int i = 0; i < n; i++) 631 { 632 if (Math.abs(p[i].im) == Double.MIN_VALUE) 633 { 634 p[i] = new Complex(p[i].re, 0.0); 635 } 636 if (Math.abs(p[i].re) == Double.MIN_VALUE) 637 { 638 p[i] = new Complex(0.0, p[i].im); 639 } 640 } 641 642 return p; 643 } 644 645 /** the number of steps in approximating a real root with the Newton-Raphson method. */ 646 private static final int MAX_STEPS_NEWTON = 100; 647 648 /** 649 * Polynomial root finder using the Newton-Raphson method. See <a href="https://en.wikipedia.org/wiki/Newton%27s_method"> 650 * https://en.wikipedia.org/wiki/Newton%27s_method</a> for brief information about the Newton-Raphson or Newton method for 651 * root finding. 652 * @param a double[]; the factors of the polynomial, where the index i indicate the factor for x^i, so the polynomial is<br> 653 * a[n]x^n + a[n-1]x^(n-1) + ... + a[2]a^2 + a[1]x + a[0] 654 * @param allowedError double; the allowed absolute error in the result 655 * @return double the root of the equation that has been found on the basis of the start value, or NaN if not found within 656 * the allowed error bounds and the allowed number of steps. 657 */ 658 public static double rootNewtonRaphson(final double[] a, final double allowedError) 659 { 660 double x = 0.1232232323234; // take a a start point that has an almost zero chance of having f'(x) = 0. 661 double newValue = Double.NaN; // for testing convergence 662 double fx = -1; 663 for (int j = 0; j < MAX_STEPS_NEWTON; j++) 664 { 665 fx = f(a, x); 666 newValue = x - fx / fDerivative(a, x); 667 if (x == newValue || Math.abs(fx) <= allowedError) 668 { 669 return x; 670 } 671 x = newValue; 672 } 673 return Math.abs(fx) <= allowedError ? x : Double.NaN; 674 } 675 676 /** the number of steps in approximating a real root with the bisection method. */ 677 private static final int MAX_STEPS_BISECTION = 100; 678 679 /** 680 * Polynomial root finder using the bisection method combined with bisection to avoid cycles and the algorithm going out of 681 * bounds. Implementation based on Numerical Recipes in C, section 9.4, pp 365-366. See 682 * <a href="https://en.wikipedia.org/wiki/Newton%27s_method"> https://en.wikipedia.org/wiki/Newton%27s_method</a> for brief 683 * information about the Newton-Raphson or Newton method for root finding. 684 * @param a double[]; the factors of the polynomial, where the index i indicate the factor for x^i, so the polynomial is<br> 685 * a[n]x^n + a[n-1]x^(n-1) + ... + a[2]a^2 + a[1]x + a[0] 686 * @param startMin double; the lowest initial search value 687 * @param startMax double; the highest initial search value 688 * @param allowedError double; the allowed absolute error in the result 689 * @return double the root of the equation that has been found on the basis of the start values, or NaN if not found within 690 * the allowed error bounds and the allowed number of steps. 691 */ 692 public static double rootBisection(final double[] a, final double startMin, final double startMax, 693 final double allowedError) 694 { 695 int n = 0; 696 double xPrev = startMin; 697 double min = startMin; 698 double max = startMax; 699 double fmin = f(a, min); 700 while (n <= MAX_STEPS_BISECTION) 701 { 702 double x = (min + max) / 2.0; 703 double fx = f(a, x); 704 if (x == xPrev || Math.abs(fx) < allowedError) 705 { 706 return x; 707 } 708 if (Math.signum(fx) == Math.signum(fmin)) 709 { 710 min = x; 711 fmin = fx; 712 } 713 else 714 { 715 max = x; 716 } 717 xPrev = x; 718 n++; 719 } 720 return Double.NaN; 721 } 722 723 /** 724 * Return the max of the norm of the complex coefficients in an array. 725 * @param array Complex[] the array with complex numbers 726 * @return double; the highest value of the norm of the complex numbers in the array 727 */ 728 private static double maxAbs(final Complex[] array) 729 { 730 double m = Double.NEGATIVE_INFINITY; 731 for (Complex c : array) 732 { 733 if (c.norm() > m) 734 { 735 m = c.norm(); 736 } 737 } 738 return m; 739 } 740 741 /** 742 * Return the max of the absolute values of the coefficients in an array. 743 * @param values double[] the array with numbers 744 * @return double; the highest absolute value of the norm of the complex numbers in the array 745 */ 746 private static double maxAbs(final double... values) 747 { 748 double m = Double.NEGATIVE_INFINITY; 749 for (double d : values) 750 { 751 if (Math.abs(d) > m) 752 { 753 m = Math.abs(d); 754 } 755 } 756 return m; 757 } 758 759 /** 760 * Return the complex value of f(c) where f(x) = a[n]x^n + a[n-1]x^(n-1) + ... + a[2]a^2 + a[1]x + a[0]. 761 * @param a Complex[] the complex factors of the equation 762 * @param c Complex; the value for which to calculate f(c) 763 * @return Complex; f(c) 764 */ 765 private static Complex f(final Complex[] a, final Complex c) 766 { 767 Complex cPow = Complex.ONE; 768 Complex result = Complex.ZERO; 769 for (int i = 0; i < a.length; i++) 770 { 771 result = result.plus(cPow.times(a[i])); 772 cPow = cPow.times(c); 773 } 774 return result; 775 } 776 777 /** 778 * Return the real value of f(x) where f(x) = a[n]x^n + a[n-1]x^(n-1) + ... + a[2]a^2 + a[1]x + a[0]. 779 * @param a double[] the factors of the equation 780 * @param x double; the value for which to calculate f(x) 781 * @return double; f(x) 782 */ 783 private static double f(final double[] a, final double x) 784 { 785 double xPow = 1.0; 786 double result = 0.0; 787 for (int i = 0; i < a.length; i++) 788 { 789 result += xPow * a[i]; 790 xPow = xPow * x; 791 } 792 return result; 793 } 794 795 /** 796 * Return the complex value of f(c) where f(x) = a[n]x^n + a[n-1]x^(n-1) + ... + a[2]a^2 + a[1]x + a[0]. 797 * @param a double[] the complex factors of the equation 798 * @param c Complex; the value for which to calculate f(c) 799 * @return Complex; f(c) 800 */ 801 private static Complex f(final double[] a, final Complex c) 802 { 803 Complex cPow = Complex.ONE; 804 Complex result = Complex.ZERO; 805 for (int i = 0; i < a.length; i++) 806 { 807 result = result.plus(cPow.times(a[i])); 808 cPow = cPow.times(c); 809 } 810 return result; 811 } 812 813 /** 814 * Return the real value of f'(x) where f(x) = a[n]x^n + a[n-1]x^(n-1) + ... + a[2]a^2 + a[1]x + a[0].<br> 815 * The derivative function f'(x) = n.a[n]x^(n-1) + (n-1).a[n-1]x^(n-2) + ... + 2.a[2]x^1 + 1.a[1] 816 * @param a double[] the factors of the equation 817 * @param x double; the value for which to calculate f'(x) 818 * @return double; f'(x), the value of the derivative function at point x 819 */ 820 private static double fDerivative(final double[] a, final double x) 821 { 822 double xPow = 1.0; 823 double result = 0.0; 824 for (int i = 1; i < a.length; i++) 825 { 826 result += xPow * i * a[i]; 827 xPow = xPow * x; 828 } 829 return result; 830 } 831 832 /** 833 * Return the complex value of f'(c) where f(c) = a[n]c^n + a[n-1]c^(n-1) + ... + a[2]a^2 + a[1]c + a[0].<br> 834 * The derivative function f'(c) = n.a[n]c^(n-1) + (n-1).a[n-1]c^(n-2) + ... + 2.a[2]c^1 + 1.a[1] 835 * @param a double[] the factors of the equation 836 * @param c double; the value for which to calculate f'(c) 837 * @return Complex; f'(c), the value of the derivative function at point c 838 */ 839 private static Complex fDerivative(final Complex[] a, final Complex c) 840 { 841 Complex cPow = Complex.ONE; 842 Complex result = Complex.ZERO; 843 for (int i = 1; i < a.length; i++) 844 { 845 result = result.plus(cPow.times(a[i]).times(i)); 846 cPow = cPow.times(c); 847 } 848 return result; 849 } 850 851 /** 852 * @param args String[] not used 853 */ 854 public static void main(final String[] args) 855 { 856 double a = 0.001; 857 double b = 1000; 858 double c = 0; 859 double d = 1; 860 Complex[] roots = cubicRootsNewtonFactor(a, b, c, d); 861 for (Complex root : roots) 862 { 863 System.out.println("NewtonFactor " + root + " f(x) = " + f(new double[] {d, c, b, a}, root)); 864 } 865 System.out.println(); 866 roots = cubicRootsCardano(a, b, c, d); 867 for (Complex root : roots) 868 { 869 System.out.println("Cardano " + root + " f(x) = " + f(new double[] {d, c, b, a}, root)); 870 } 871 System.out.println(); 872 roots = cubicRootsAberthEhrlich(a, b, c, d); 873 for (Complex root : roots) 874 { 875 System.out.println("Aberth-Ehrlich " + root + " f(x) = " + f(new double[] {d, c, b, a}, root)); 876 } 877 System.out.println(); 878 roots = cubicRootsDurandKerner(a, b, c, d); 879 for (Complex root : roots) 880 { 881 System.out.println("Durand-Kerner " + root + " f(x) = " + f(new double[] {d, c, b, a}, root)); 882 } 883 System.out.println(); 884 roots = PolynomialRoots.cubicRoots(a, b, c, d); 885 for (Complex root : roots) 886 { 887 System.out.println("F77 " + root + " f(x) = " + f(new double[] {d, c, b, a}, root)); 888 } 889 } 890 }