Last active
September 9, 2021 14:15
-
-
Save thomasantony/6471125 to your computer and use it in GitHub Desktop.
An implementation of an improved & simplified Brent's Method.
Calculates root of a function f(x) in the interval [a,b]. Zhang, Z. (2011). An Improvement to the Brent’s Method. International Journal of Experimental Algorithms (IJEA), (2), 21–26. Retrieved from http://www.cscjournals.org/csc/manuscript/Journals/IJEA/volume2/Issue1/IJEA-7.pdf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template <class T> | |
inline void __swap(T *a, T *b) | |
{ | |
T temp = *a; | |
*a = *b; | |
*b = temp; | |
} | |
/* | |
An implementation of an improved & simplified Brent's Method. | |
Calculates root of a function f(x) in the interval [a,b]. | |
Zhang, Z. (2011). An Improvement to the Brent’s Method. International Journal of Experimental Algorithms (IJEA), (2), 21–26. | |
Retrieved from http://www.cscjournals.org/csc/manuscript/Journals/IJEA/volume2/Issue1/IJEA-7.pdf | |
f -> Function pointer to the function to be evaluated | |
a -> Starting point of interval | |
b -> Ending point of interval | |
root -> This will contain the root when the functiuon is complete | |
tol -> tolerance . Set to a low value like 1e-6 | |
Returns zero on success | |
Returns -1 on failure | |
*/ | |
template <class F> | |
int cpu_brent_improved(double (*)(double), double a, double b, double& root, double tol) | |
{ | |
double s, c, f_s, f_a, f_b, f_c; | |
f_a = f(a); | |
f_b = f(b); | |
if(a>b || f_a * f_b >= 0) // The algorithm assumes a<b | |
{ | |
return -1; // Root not bound by the given guesses | |
} | |
do{ | |
c = (a+b)/2; | |
f_c = f(c); | |
if(fabs(f_a-f_c) > tol && fabs(f_b-f_c) > tol) // f(a)!=f(c) and f(b)!=f(c) | |
{ | |
// Inverse quadratic interpolation | |
s = a*f_b*f_c/((f_a-f_b)*(f_a-f_c)) + b*f_a*f_c/((f_b-f_a)*(f_b-f_c)) + c*f_a*f_b/((f_c-f_a)*(f_c-f_b)); | |
}else{ | |
// Secant rule | |
s = b - f_b*(b-a)/(f_b-f_a); | |
} | |
f_s = f(s); | |
if(c>s) | |
{ | |
__swap(&s, &c); | |
} | |
if(f_c*f_s < 0) | |
{ | |
a = s; | |
b = c; | |
}else if(f_s*f_b < 0){ | |
a = c; | |
}else{ | |
b = s; | |
} | |
}while(f_s < tol || fabs(b-a)<tol); // Convergence conditions | |
root = s; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
There's another article by Steven Stage that points out a couple of problems with the Zhang algorithm. It also profiles its performance vs. Brent v. Regula Falsi. Check out: https://www.cscjournals.org/manuscript/Journals/IJEA/Volume4/Issue1/IJEA-33.pdf
I copied this and tweaked it slightly for my environment, fixed the templated arg issue, then modified per Stage. However, I didn't get proper convergence until I fixed a further problem with the above code, namely that after a and b are calculated f_a and/or f_b need to be recalculated. If you do this at the top of the do loop, there may be redundant calculation, whereas if you do it after they are modified, the recalculation might be superfluous.