1
+ #include <math.h>
2
+ #include <time.h>
3
+ #include <stdio.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+ #include <limits.h>
7
+ #include <complex.h>
8
+
9
+ /**
10
+ * Test the algorithm online:
11
+ * https://gist.github.com/kvedala/27f1b0b6502af935f6917673ec43bcd7
12
+ **/
13
+
14
+ /***
15
+ * Try the highly unstable Wilkinson's polynomial:
16
+ * ./numerical_methods/durand_kerner_roots.c 1 -210 20615 -1256850 53327946 -1672280820 40171771630 -756111184500 11310276995381 -135585182899530 1307535010540395 -10142299865511450 63030812099294896 -311333643161390640 1206647803780373360 -3599979517947607200 8037811822645051776 -12870931245150988800 13803759753640704000 -8752948036761600000 2432902008176640000
17
+ * */
18
+
19
+ #define ACCURACY 1e-10
20
+
21
+ /**
22
+ * define polynomial function
23
+ **/
24
+ long double complex function (double * coeffs , unsigned int degree , long double complex x )
25
+ {
26
+ long double complex out = 0. ;
27
+ unsigned int n ;
28
+
29
+ for (n = 0 ; n < degree ; n ++ )
30
+ out += coeffs [n ] * cpow (x , degree - n - 1 );
31
+
32
+ return out ;
33
+ }
34
+
35
+ static inline char * complex_str (long double complex x )
36
+ {
37
+ static char msg [50 ];
38
+ double r = creal (x );
39
+ double c = cimag (x );
40
+
41
+ sprintf (msg , "% 7.04g%+7.04gj" , r , c );
42
+
43
+ return msg ;
44
+ }
45
+
46
+ char check_termination (long double delta )
47
+ {
48
+ static long double past_delta = INFINITY ;
49
+ if (fabsl (past_delta - delta ) <= ACCURACY || delta < ACCURACY )
50
+ return 1 ;
51
+ past_delta = delta ;
52
+ return 0 ;
53
+ }
54
+
55
+ /***
56
+ * the comandline inputs are taken as coeffiecients of a polynomial
57
+ **/
58
+ int main (int argc , char * * argv )
59
+ {
60
+ double * coeffs = NULL ;
61
+ long double complex * s0 = NULL ;
62
+ unsigned int degree = 0 ;
63
+ unsigned int n , i ;
64
+
65
+ if (argc < 2 )
66
+ {
67
+ printf ("Please pass the coefficients of the polynomial as commandline arguments.\n" );
68
+ return 0 ;
69
+ }
70
+
71
+ degree = argc - 1 ; /*< detected polynomial degree */
72
+ coeffs = (double * )malloc (degree * sizeof (double )); /**< store all input coefficients */
73
+ s0 = (long double complex * )malloc ((degree - 1 ) * sizeof (long double complex )); /**< number of roots = degree-1 */
74
+
75
+ /* initialize random seed: */
76
+ srand (time (NULL ));
77
+
78
+ if (!coeffs || !s0 )
79
+ {
80
+ perror ("Unable to allocate memory!" );
81
+ if (coeffs )
82
+ free (coeffs );
83
+ if (s0 )
84
+ free (s0 );
85
+ return EXIT_FAILURE ;
86
+ }
87
+
88
+ #if defined(DEBUG ) || !defined(NDEBUG )
89
+ /**
90
+ * store intermediate values to a CSV file
91
+ **/
92
+ FILE * log_file = fopen ("durand_kerner.log.csv" , "wt" );
93
+ if (!log_file )
94
+ {
95
+ perror ("Unable to create a storage log file!" );
96
+ free (coeffs );
97
+ free (s0 );
98
+ return EXIT_FAILURE ;
99
+ }
100
+ fprintf (log_file , "iter#," );
101
+ #endif
102
+
103
+ printf ("Computing the roots for:\n\t" );
104
+ for (n = 0 ; n < degree ; n ++ )
105
+ {
106
+ coeffs [n ] = strtod (argv [n + 1 ], NULL );
107
+ if (n < degree - 1 && coeffs [n ] != 0 )
108
+ printf ("(%g) x^%d + " , coeffs [n ], degree - n - 1 );
109
+ else if (coeffs [n ] != 0 )
110
+ printf ("(%g) x^%d = 0\n" , coeffs [n ], degree - n - 1 );
111
+
112
+ double tmp ;
113
+ if (n > 0 )
114
+ coeffs [n ] /= tmp ; /* numerical errors less when the first coefficient is "1" */
115
+ else
116
+ {
117
+ tmp = coeffs [0 ];
118
+ coeffs [0 ] = 1 ;
119
+ }
120
+
121
+ /* initialize root approximations with random values */
122
+ if (n < degree - 1 )
123
+ {
124
+ s0 [n ] = (long double )rand () + (long double )rand () * I ;
125
+ #if defined(DEBUG ) || !defined(NDEBUG )
126
+ fprintf (log_file , "root_%d," , n );
127
+ #endif
128
+ }
129
+ }
130
+
131
+ #if defined(DEBUG ) || !defined(NDEBUG )
132
+ fprintf (log_file , "avg. correction" );
133
+ fprintf (log_file , "\n0," );
134
+ for (n = 0 ; n < degree - 1 ; n ++ )
135
+ fprintf (log_file , "%s," , complex_str (s0 [n ]));
136
+ #endif
137
+
138
+ double tol_condition = 1 ;
139
+ unsigned long iter = 0 ;
140
+
141
+ while (!check_termination (tol_condition ) && iter < INT_MAX )
142
+ {
143
+ long double complex delta = 0 ;
144
+ tol_condition = 0 ;
145
+ iter ++ ;
146
+
147
+ #if defined(DEBUG ) || !defined(NDEBUG )
148
+ fprintf (log_file , "\n%ld," , iter );
149
+ #endif
150
+
151
+ for (n = 0 ; n < degree - 1 ; n ++ )
152
+ {
153
+ long double complex numerator = function (coeffs , degree , s0 [n ]);
154
+ long double complex denominator = 1.0 ;
155
+ for (i = 0 ; i < degree - 1 ; i ++ )
156
+ if (i != n )
157
+ denominator *= s0 [n ] - s0 [i ];
158
+
159
+ delta = numerator / denominator ;
160
+
161
+ if (isnan (cabsl (delta )) || isinf (cabsl (delta )))
162
+ {
163
+ printf ("\n\nOverflow/underrun error - got value = %Lg" , cabsl (delta ));
164
+ goto end ;
165
+ }
166
+
167
+ s0 [n ] -= delta ;
168
+
169
+ tol_condition = fmaxl (tol_condition , fabsl (cabsl (delta )));
170
+
171
+ #if defined(DEBUG ) || !defined(NDEBUG )
172
+ fprintf (log_file , "%s," , complex_str (s0 [n ]));
173
+ #endif
174
+ }
175
+ // tol_condition /= (degree - 1);
176
+
177
+ #if defined(DEBUG ) || !defined(NDEBUG )
178
+ if (iter % 500 == 0 )
179
+ {
180
+ printf ("Iter: %lu\t" , iter );
181
+ for (n = 0 ; n < degree - 1 ; n ++ )
182
+ printf ("\t%s" , complex_str (s0 [n ]));
183
+ printf ("\t\tabsolute average change: %.4g\n" , tol_condition );
184
+ }
185
+
186
+ fprintf (log_file , "%.4g" , tol_condition );
187
+ #endif
188
+ }
189
+ end :
190
+
191
+ #if defined(DEBUG ) || !defined(NDEBUG )
192
+ fclose (log_file );
193
+ #endif
194
+
195
+ printf ("\nIterations: %lu\n" , iter );
196
+ for (n = 0 ; n < degree - 1 ; n ++ )
197
+ printf ("\t%s\n" , complex_str (s0 [n ]));
198
+ printf ("absolute average change: %.4g\n" , tol_condition );
199
+
200
+ free (coeffs );
201
+ free (s0 );
202
+
203
+ return 0 ;
204
+ }
0 commit comments