Alex's Anthology of Algorithms Common Code for Contests in Concise C++
Optimization / Numerical Methods

5.4.1 Root Finding (Bracketing)

5-Optimization/5.4.1_Root_Finding_(Bracketing).cpp

View on GitHub

Finds an $x$ in an interval $[a, b]$ for a continuous function $f$ such that $f(x) = 0$. By the intermediate value theorem, a root must exist in $[a, b]$ if the signs of $f(a)$ and $f(b)$ differ. Each bisection step evaluates the midpoint of the interval and keeps the half whose endpoints still differ in sign, so a root always remains bracketed. The answer is found with an absolute error of roughly $1 / 2^n$, where $n$ is the number of iterations. Although it is possible to control the error by looping while $b - a$ is greater than an arbitrary epsilon, it is simpler to let the loop run for a desired number of iterations until floating point arithmetic breaks down. 100 iterations is usually sufficient, since the search space will be reduced to $2^{-100}$ (roughly $10^{-30}$) times its original size.

  • bisection_root(f, a, b) returns a root in an interval [a, b] for a continuous function $f$ where $\operatorname{sgn}(f(a)) \neq \operatorname{sgn}(f(b))$, using the bisection method.
  • falsi_illinois_root(f, a, b) returns a root in an interval [a, b] for a continuous function $f$ where $\operatorname{sgn}(f(a)) \neq \operatorname{sgn}(f(b))$, using the Illinois algorithm variant of the false position (a.k.a. regula falsi) method.

Implementation

#include <stdexcept>

template<class Fn>
double bisection_root(Fn f, double a, double b, const int ITERATIONS = 100) {
  if (a > b || f(a) * f(b) > 0) {
    throw std::runtime_error("Must give [a, b] where sgn(f(a)) != sgn(f(b)).");
  }
  double m;
  for (int i = 0; i < ITERATIONS; i++) {
    m = a + (b - a) / 2;
    if (f(a) * f(m) >= 0) {
      a = m;
    } else {
      b = m;
    }
  }
  return m;
}

template<class Fn>
double falsi_illinois_root(Fn f, double a, double b, const int ITERATIONS = 100) {
  if (a > b || f(a) * f(b) > 0) {
    throw std::runtime_error("Must give [a, b] where sgn(f(a)) != sgn(f(b)).");
  }
  double m, fm, fa = f(a), fb = f(b);
  int side = 0;
  for (int i = 0; i < ITERATIONS; i++) {
    m = (fa * b - fb * a) / (fa - fb);
    fm = f(m);
    if (fb * fm > 0) {
      b = m;
      fb = fm;
      if (side < 0) {
        fa /= 2;
      }
      side = -1;
    } else if (fa * fm > 0) {
      a = m;
      fa = fm;
      if (side > 0) {
        fb /= 2;
      }
      side = 1;
    } else {
      break;
    }
  }
  return m;
}

Example Usage

#include <cassert>
#include <cmath>

double f(double x) {
  return x * x - 4 * sin(x);
}

int main() {
  assert(fabs(f(bisection_root(f, 1, 3))) < 1e-10);
  assert(fabs(f(falsi_illinois_root(f, 1, 3))) < 1e-10);
  return 0;
}