TheAlgorithms/C++
1.0.0
All the algorithms implemented in C++
Toggle main menu visibility
Loading...
Searching...
No Matches
durand_kerner_roots.cpp
Go to the documentation of this file.
1
31
32
#include <algorithm>
33
#include <cassert>
34
#include <cmath>
35
#include <complex>
36
#include <cstdint>
37
#include <cstdlib>
38
#include <ctime>
39
#include <fstream>
40
#include <iostream>
41
#include <valarray>
42
#ifdef _OPENMP
43
#include <omp.h>
44
#endif
45
46
#define ACCURACY 1e-10
47
54
std::complex<double>
poly_function
(
const
std::valarray<double> &coeffs,
55
std::complex<double> x) {
56
double
real = 0.f, imag = 0.f;
57
int
n;
58
59
// #ifdef _OPENMP
60
// #pragma omp target teams distribute reduction(+ : real, imag)
61
// #endif
62
for
(n = 0; n < coeffs.size(); n++) {
63
std::complex<double> tmp =
64
coeffs[n] * std::pow(x, coeffs.size() - n - 1);
65
real += tmp.real();
66
imag += tmp.imag();
67
}
68
69
return
std::complex<double>(real, imag);
70
}
71
77
const
char
*
complex_str
(
const
std::complex<double> &x) {
78
#define MAX_BUFF_SIZE 50
79
static
char
msg[MAX_BUFF_SIZE];
80
81
std::snprintf(msg, MAX_BUFF_SIZE,
"% 7.04g%+7.04gj"
, x.real(), x.imag());
82
83
return
msg;
84
}
85
92
bool
check_termination
(
long
double
delta) {
93
static
long
double
past_delta = INFINITY;
94
if
(std::abs(past_delta - delta) <=
ACCURACY
|| delta <
ACCURACY
)
95
return
true
;
96
past_delta = delta;
97
return
false
;
98
}
99
110
std::pair<uint32_t, double>
durand_kerner_algo
(
111
const
std::valarray<double> &coeffs,
112
std::valarray<std::complex<double>> *roots,
bool
write_log =
false
) {
113
long
double
tol_condition = 1;
114
uint32_t iter = 0;
115
int
n;
116
std::ofstream log_file;
117
118
if
(write_log) {
119
/*
120
* store intermediate values to a CSV file
121
*/
122
log_file.open(
"durand_kerner.log.csv"
);
123
if
(!log_file.is_open()) {
124
perror(
"Unable to create a storage log file!"
);
125
std::exit(EXIT_FAILURE);
126
}
127
log_file <<
"iter#,"
;
128
129
for
(n = 0; n < roots->size(); n++) log_file <<
"root_"
<< n <<
","
;
130
131
log_file <<
"avg. correction"
;
132
log_file <<
"\n0,"
;
133
for
(n = 0; n < roots->size(); n++)
134
log_file <<
complex_str
((*roots)[n]) <<
","
;
135
}
136
137
bool
break_loop =
false
;
138
while
(!
check_termination
(tol_condition) && iter < INT16_MAX &&
139
!break_loop) {
140
tol_condition = 0;
141
iter++;
142
break_loop =
false
;
143
144
if
(log_file.is_open())
145
log_file <<
"\n"
<< iter <<
","
;
146
147
#ifdef _OPENMP
148
#pragma omp parallel for shared(break_loop, tol_condition)
149
#endif
150
for
(n = 0; n < roots->size(); n++) {
151
if
(break_loop)
152
continue
;
153
154
std::complex<double> numerator, denominator;
155
numerator =
poly_function
(coeffs, (*roots)[n]);
156
denominator = 1.0;
157
for
(
int
i = 0; i < roots->size(); i++)
158
if
(i != n)
159
denominator *= (*roots)[n] - (*roots)[i];
160
161
std::complex<long double> delta = numerator / denominator;
162
163
if
(std::isnan(std::abs(delta)) || std::isinf(std::abs(delta))) {
164
std::cerr <<
"\n\nOverflow/underrun error - got value = "
165
<< std::abs(delta) <<
"\n"
;
166
// return std::pair<uint32_t, double>(iter, tol_condition);
167
break_loop =
true
;
168
}
169
170
(*roots)[n] -= delta;
171
172
#ifdef _OPENMP
173
#pragma omp critical
174
#endif
175
tol_condition = std::max(tol_condition, std::abs(std::abs(delta)));
176
}
177
// tol_condition /= (degree - 1);
178
179
if
(break_loop)
180
break
;
181
182
if
(log_file.is_open()) {
183
for
(n = 0; n < roots->size(); n++)
184
log_file <<
complex_str
((*roots)[n]) <<
","
;
185
}
186
187
#if defined(DEBUG) || !defined(NDEBUG)
188
if
(iter % 500 == 0) {
189
std::cout <<
"Iter: "
<< iter <<
"\t"
;
190
for
(n = 0; n < roots->size(); n++)
191
std::cout <<
"\t"
<<
complex_str
((*roots)[n]);
192
std::cout <<
"\t\tabsolute average change: "
<< tol_condition
193
<<
"\n"
;
194
}
195
#endif
196
197
if
(log_file.is_open())
198
log_file << tol_condition;
199
}
200
201
return
std::pair<uint32_t, long double>(iter, tol_condition);
202
}
203
208
void
test1
() {
209
const
std::valarray<double> coeffs = {1, 0, 4};
// x^2 - 2 = 0
210
std::valarray<std::complex<double>> roots(2);
211
std::valarray<std::complex<double>> expected = {
212
std::complex<double>(0., 2.),
213
std::complex<double>(0., -2.)
// known expected roots
214
};
215
216
/* initialize root approximations with random values */
217
for
(
int
n = 0; n < roots.size(); n++) {
218
roots[n] = std::complex<double>(std::rand() % 100, std::rand() % 100);
219
roots[n] -= 50.f;
220
roots[n] /= 25.f;
221
}
222
223
auto
result =
durand_kerner_algo
(coeffs, &roots,
false
);
224
225
for
(
int
i = 0; i < roots.size(); i++) {
226
// check if approximations are have < 0.1% error with one of the
227
// expected roots
228
bool
err1 =
false
;
229
for
(
int
j = 0; j < roots.size(); j++)
230
err1 |= std::abs(std::abs(roots[i] - expected[j])) < 1e-3;
231
assert(err1);
232
}
233
234
std::cout <<
"Test 1 passed! - "
<< result.first <<
" iterations, "
235
<< result.second <<
" accuracy"
236
<<
"\n"
;
237
}
238
243
void
test2
() {
244
const
std::valarray<double> coeffs = {
// 0.015625 x^3 - 1 = 0
245
1. / 64., 0., 0., -1.};
246
std::valarray<std::complex<double>> roots(3);
247
const
std::valarray<std::complex<double>> expected = {
248
std::complex<double>(4., 0.), std::complex<double>(-2., 3.46410162),
249
std::complex<double>(-2., -3.46410162)
// known expected roots
250
};
251
252
/* initialize root approximations with random values */
253
for
(
int
n = 0; n < roots.size(); n++) {
254
roots[n] = std::complex<double>(std::rand() % 100, std::rand() % 100);
255
roots[n] -= 50.f;
256
roots[n] /= 25.f;
257
}
258
259
auto
result =
durand_kerner_algo
(coeffs, &roots,
false
);
260
261
for
(
int
i = 0; i < roots.size(); i++) {
262
// check if approximations are have < 0.1% error with one of the
263
// expected roots
264
bool
err1 =
false
;
265
for
(
int
j = 0; j < roots.size(); j++)
266
err1 |= std::abs(std::abs(roots[i] - expected[j])) < 1e-3;
267
assert(err1);
268
}
269
270
std::cout <<
"Test 2 passed! - "
<< result.first <<
" iterations, "
271
<< result.second <<
" accuracy"
272
<<
"\n"
;
273
}
274
275
/***
276
* Main function.
277
* The comandline input arguments are taken as coeffiecients of a
278
*polynomial. For example, this command
279
* ```sh
280
* ./durand_kerner_roots 1 0 -4
281
* ```
282
* will find roots of the polynomial \f$1\cdot x^2 + 0\cdot x^1 + (-4)=0\f$
283
**/
284
int
main
(
int
argc,
char
**argv) {
285
/* initialize random seed: */
286
std::srand(std::time(
nullptr
));
287
288
if
(argc < 2) {
289
test1
();
// run tests when no input is provided
290
test2
();
// and skip tests when input polynomial is provided
291
std::cout <<
"Please pass the coefficients of the polynomial as "
292
"commandline "
293
"arguments.\n"
;
294
return
0;
295
}
296
297
int
n, degree = argc - 1;
// detected polynomial degree
298
std::valarray<double> coeffs(degree);
// create coefficiencts array
299
300
// number of roots = degree - 1
301
std::valarray<std::complex<double>> s0(degree - 1);
302
303
std::cout <<
"Computing the roots for:\n\t"
;
304
for
(n = 0; n < degree; n++) {
305
coeffs[n] = strtod(argv[n + 1],
nullptr
);
306
if
(n < degree - 1 && coeffs[n] != 0)
307
std::cout <<
"("
<< coeffs[n] <<
") x^"
<< degree - n - 1 <<
" + "
;
308
else
if
(coeffs[n] != 0)
309
std::cout <<
"("
<< coeffs[n] <<
") x^"
<< degree - n - 1
310
<<
" = 0\n"
;
311
312
/* initialize root approximations with random values */
313
if
(n < degree - 1) {
314
s0[n] = std::complex<double>(std::rand() % 100, std::rand() % 100);
315
s0[n] -= 50.f;
316
s0[n] /= 50.f;
317
}
318
}
319
320
// numerical errors less when the first coefficient is "1"
321
// hence, we normalize the first coefficient
322
{
323
double
tmp = coeffs[0];
324
coeffs /= tmp;
325
}
326
327
clock_t end_time, start_time = clock();
328
auto
result
=
durand_kerner_algo
(coeffs, &s0,
true
);
329
end_time = clock();
330
331
std::cout <<
"\nIterations: "
<<
result
.first <<
"\n"
;
332
for
(n = 0; n < degree - 1; n++)
333
std::cout <<
"\t"
<<
complex_str
(s0[n]) <<
"\n"
;
334
std::cout <<
"absolute average change: "
<<
result
.second <<
"\n"
;
335
std::cout <<
"Time taken: "
336
<<
static_cast<
double
>
(end_time - start_time) / CLOCKS_PER_SEC
337
<<
" sec\n"
;
338
339
return
0;
340
}
check_termination
bool check_termination(long double delta)
Definition
durand_kerner_roots.cpp:92
durand_kerner_algo
std::pair< uint32_t, double > durand_kerner_algo(const std::valarray< double > &coeffs, std::valarray< std::complex< double > > *roots, bool write_log=false)
Definition
durand_kerner_roots.cpp:110
complex_str
const char * complex_str(const std::complex< double > &x)
Definition
durand_kerner_roots.cpp:77
poly_function
std::complex< double > poly_function(const std::valarray< double > &coeffs, std::complex< double > x)
Definition
durand_kerner_roots.cpp:54
ACCURACY
#define ACCURACY
Definition
durand_kerner_roots.cpp:46
math::fibonacci_sum::result
uint64_t result(uint64_t n)
Definition
fibonacci_sum.cpp:77
main
int main()
Main function.
Definition
generate_parentheses.cpp:110
test2
void test2(const std::string &text)
Self test 2 - using 8x8 randomly generated key.
Definition
hill_cipher.cpp:505
test1
void test1(const std::string &text)
Self test 1 - using 3x3 randomly generated key.
Definition
hill_cipher.cpp:470
numerical_methods
durand_kerner_roots.cpp
Generated by
1.18.0