1 package org.djutils.math.polynomialroots;
2
3 import org.djutils.math.complex.Complex;
4 import org.djutils.math.complex.ComplexMath;
5
6
7
8
9
10
11
12
13
14
15
16
17 public final class PolynomialRoots2
18 {
19
20
21
22 private PolynomialRoots2()
23 {
24
25 }
26
27
28
29
30
31
32
33 private static double sign(final double a, final double b)
34 {
35 return b >= 0 ? a : -a;
36 }
37
38
39
40
41
42
43
44
45
46
47 public static Complex[] linearRoots(final double q1, final double q0)
48 {
49 if (q1 == 0)
50 {
51 return new Complex[] {};
52 }
53 return linearRoots(q0 / q1);
54 }
55
56
57
58
59
60
61
62
63
64 public static Complex[] linearRoots(final double q0)
65 {
66 return new Complex[] {new Complex(-q0, 0)};
67 }
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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
97
98
99
100
101
102
103
104
105
106
107
108
109
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
119 if (q0 == 0.0 && q1 == 0.0)
120 {
121
122 return new Complex[] {Complex.ZERO, Complex.ZERO};
123 }
124 else if (q0 == 0.0)
125 {
126
127
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
138 return new Complex[] {new Complex(x, 0), new Complex(-x, 0)};
139 }
140 else
141 {
142
143 return new Complex[] {new Complex(0, x), new Complex(0, -x)};
144 }
145 }
146 else
147 {
148
149
150
151
152 final double sqrtLPN = Math.sqrt(Double.MAX_VALUE);
153 rescale = (q1 > sqrtLPN + sqrtLPN);
154
155 if (!rescale)
156 {
157 x = q1 * 0.5;
158 rescale = (q0 < x * x - Double.MAX_VALUE);
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
186
187 x = a1 * 0.5;
188 y = x * x - a0;
189
190 if (y >= 0.0)
191 {
192
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;
207 z = q0 / y;
208 }
209 else
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
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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247 public static Complex[] cubicRootsNewtonFactor(final double a3, final double a2, final double a1, final double a0)
248 {
249
250 if (a3 == 0.0)
251 {
252 return quadraticRoots(a2, a1, a0);
253 }
254
255
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
280 double[] args = new double[] {d, c, b, a};
281 double root1 = rootNewtonRaphson(args, 0.0);
282
283
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
304
305
306
307
308
309
310
311
312
313 }
314
315
316
317
318
319
320
321
322 Complex[] rootsQuadaratic = quadraticRoots(a, b + root1 * a, c + root1 * (b + root1 * a));
323
324
325
326
327
328
329
330
331
332
333
334 return new Complex[] {new Complex(root1), rootsQuadaratic[0], rootsQuadaratic[1]};
335 }
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353 @SuppressWarnings("checkstyle:localvariablename")
354 public static Complex[] cubicRootsCardano(final double a, final double b, final double c, final double d)
355 {
356
357 if (a == 0.0)
358 {
359 return quadraticRoots(b, c, d);
360 }
361
362
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
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
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
406
407
408
409
410
411
412
413
414
415
416 public static Complex[] cubicRootsDurandKerner(final double a3, final double a2, final double a1, final double a0)
417 {
418
419 if (a3 == 0.0)
420 {
421 return quadraticRoots(a2, a1, a0);
422 }
423
424
425 return rootsDurandKerner(new Complex[] {new Complex(a0 / a3), new Complex(a1 / a3), new Complex(a2 / a3), Complex.ONE});
426 }
427
428
429
430
431
432
433
434
435
436
437
438
439
440 public static Complex[] cubicRootsAberthEhrlich(final double a3, final double a2, final double a1, final double a0)
441 {
442
443 if (a3 == 0.0)
444 {
445 return quadraticRoots(a2, a1, a0);
446 }
447
448
449 return rootsAberthEhrlich(
450 new Complex[] {new Complex(a0 / a3), new Complex(a1 / a3), new Complex(a2 / a3), Complex.ONE});
451 }
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466 public static Complex[] quarticRootsDurandKerner(final double a4, final double a3, final double a2, final double a1,
467 final double a0)
468 {
469
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
481
482
483
484
485
486
487
488
489
490
491
492 public static Complex[] quarticRootsAberthEhrlich(final double a4, final double a3, final double a2, final double a1,
493 final double a0)
494 {
495
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
506 private static final int MAX_STEPS_DURAND_KERNER = 100;
507
508
509
510
511
512
513
514
515
516
517 public static Complex[] rootsDurandKerner(final Complex[] a)
518 {
519 int n = a.length - 1;
520 double radius = 1 + maxAbs(a);
521
522
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
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
577 private static final int MAX_STEPS_ABERTH_EHRLICH = 100;
578
579
580
581
582
583
584
585
586
587
588 public static Complex[] rootsAberthEhrlich(final Complex[] a)
589 {
590 int n = a.length - 1;
591 double radius = 1 + maxAbs(a);
592
593
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
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
646 private static final int MAX_STEPS_NEWTON = 100;
647
648
649
650
651
652
653
654
655
656
657
658 public static double rootNewtonRaphson(final double[] a, final double allowedError)
659 {
660 double x = 0.1232232323234;
661 double newValue = Double.NaN;
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
677 private static final int MAX_STEPS_BISECTION = 100;
678
679
680
681
682
683
684
685
686
687
688
689
690
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
725
726
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
743
744
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
761
762
763
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
779
780
781
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
797
798
799
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
815
816
817
818
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
834
835
836
837
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
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 }