I made a program that contains a root-finding algorithm for polynomials as a function and contains 3 test polynomials. The algorithm is Brent's method and is based entirely off the pseudocode from Wikipedia. Is there anything I should change to make the code faster/easier to understand/etc?
The code does run and prints correct results for all test cases I've tried.
I'm mostly interested in writing optimized code with the priority being faster runtime, less memory usage, and fewer redundant function calls respectively, but I'm also interested in picking up better coding practices.
/*******************************************************************************
*
* Grant Williams
*
* Version 1.0.0
* August 27, 2015
*
* Test Program for Brent's Method Function.
*
* Brent's method makes use of the bisection method, the secant method, and inverse quadratic interpolation in one algorithm.
*
* To Compile Please use icc -std=c++11 if using intel or g++ -std=c++11 if using GCC.
*
********************************************************************************/
#include <iostream>
#include <cmath>
#include <algorithm>
#include <functional>
#include <ctime>
//#include "brent_fun.h"
void brents_fun(std::function<double (double)> f, double lower_bound, double upper_bound, double TOL, double MAX_ITER);
int main()
{
//clock stuff
std::clock_t start;
double duration;
start = std::clock();
//stop clock stuff
double a; // lower bound
double b; // upper bound
double TOL = 0.0001; // tolerance
double MAX_ITER = 1000; // maximum number of iterations
int function; // set polynomial to find roots of & boundary conditions for that polynomial
std::cout << std::endl;
for (function = 1; function <= 3; function++)
{
if (function == 1)
{
a = -1.5;
b = 0;
auto f = [](double x){ return (x+1) * (x+2) * (x+3); };
brents_fun(f,a,b,TOL,MAX_ITER);
}
else if (function == 2)
{
a = -10;
b = 10;
auto f = [](double x){ return (x*x*x -4*x - 9); };
brents_fun(f,a,b,TOL,MAX_ITER);
}
else if (function == 3)
{
a = -4;
b = 3;
auto f = [](double x){ return (x+3)*(x-1)*(x-1); };
brents_fun(f,a,b,TOL,MAX_ITER);
}
}
//clock stuff again
duration = (std::clock() - start) / (double) CLOCKS_PER_SEC;
std::cout << "Elapsed time: " << duration << " seconds" << std::endl;
//finish clock stuff
std::cout << std::endl;
return 0;
}
void brents_fun(std::function<double (double)> f, double lower_bound, double upper_bound, double TOL, double MAX_ITER)
{
double a = lower_bound;
double b = upper_bound;
double fa = f(a); // calculated now to save function calls
double fb = f(b); // calculated now to save function calls
double fs = 0; // initialize
if (!(fa * fb < 0))
{
std::cout << "Signs of f(lower_bound) and f(upper_bound) must be opposites" << std::endl; // throws exception if root isn't bracketed
return;
}
if (std::abs(fa) < std::abs(b)) // if magnitude of f(lower_bound) is less than magnitude of f(upper_bound)
{
std::swap(a,b);
std::swap(fa,fb);
}
double c = a; // c now equals the largest magnitude of the lower and upper bounds
double fc = fa; // precompute function evalutation for point c by assigning it the same value as fa
bool mflag = true; // boolean flag used to evaluate if statement later on
double s = 0; // Our Root that will be returned
double d = 0; // Only used if mflag is unset (mflag == false)
for (unsigned int iter = 1; iter < MAX_ITER; ++iter)
{
// stop if converged on root or error is less than tolerance
if (std::abs(b-a) < TOL)
{
std::cout << "After " << iter << " iterations the root is: " << s << std::endl;
return;
} // end if
if (fa != fc && fb != fc)
{
// use inverse quadratic interopolation
s = ( a * fb * fc / ((fa - fb) * (fa - fc)) )
+ ( b * fa * fc / ((fb - fa) * (fb - fc)) )
+ ( c * fa * fb / ((fc - fa) * (fc - fb)) );
}
else
{
// secant method
s = b - fb * (b - a) / (fb - fa);
}
/*
Crazy condition statement!:
-------------------------------------------------------
(condition 1) s is not between (3a+b)/4 and b or
(condition 2) (mflag is true and |s−b| ≥ |b−c|/2) or
(condition 3) (mflag is false and |s−b| ≥ |c−d|/2) or
(condition 4) (mflag is set and |b−c| < |TOL|) or
(condition 5) (mflag is false and |c−d| < |TOL|)
*/
if ( ( (s < (3 * a + b) * 0.25) || (s > b) ) ||
( mflag && (std::abs(s-b) >= (std::abs(b-c) * 0.5)) ) ||
( !mflag && (std::abs(s-b) >= (std::abs(c-d) * 0.5)) ) ||
( mflag && (std::abs(b-c) < TOL) ) ||
( !mflag && (std::abs(c-d) < TOL)) )
{
// bisection method
s = (a+b)*0.5;
mflag = true;
}
else
{
mflag = false;
}
fs = f(s); // calculate fs
d = c; // first time d is being used (wasnt used on first iteration because mflag was set)
c = b; // set c equal to upper bound
fc = fb; // set f(c) = f(b)
if ( fa * fs < 0) // fa and fs have opposite signs
{
b = s;
fb = fs; // set f(b) = f(s)
}
else
{
a = s;
fa = fs; // set f(a) = f(s)
}
if (std::abs(fa) < std::abs(fb)) // if magnitude of fa is less than magnitude of fb
{
std::swap(a,b); // swap a and b
std::swap(fa,fb); // make sure f(a) and f(b) are correct after swap
}
} // end for
std::cout<< "The solution does not converge or iterations are not sufficient" << std::endl;
} // end brent_fun