package umontreal.iro.lecuyer.util;
import umontreal.iro.lecuyer.functions.MathFunction;


/**
 * This class provides methods to solve non-linear equations.
 * 
 */
public class RootFinder {
   private RootFinder() {}


   /**
    * Computes a root x of the function in f using the
    *     Brent-Dekker method. The interval [a, b] must contain the root x.
    *     The calculations are done with an approximate relative precision
    *     tol.  Returns x such that f (x) = 0.
    *  
    * @param a left endpoint of initial interval
    * 
    *    @param b right endpoint of initial interval
    * 
    *    @param f the function which is evaluated
    * 
    *    @param tol accuracy goal
    * 
    *    @return the root x
    * 
    */
   public static double brentDekker (double a, double b,
                                     MathFunction f, double tol) {
      final double EPS = 0.5E-15;
      final int MAXITER = 120;    // Maximum number of iterations
      double c, d, e;
      double fa, fb, fc;

      // Special case I = [b, a]
      if (b < a) {
         double ctemp = a;
         a = b;
         b = ctemp;
      }
      // Initialization
      fa = f.evaluate (a);
      fb = f.evaluate (b);
      c = a;
      fc = fa;
      d = e = b - a;
      tol += EPS + Num.DBL_EPSILON; // in case tol is too small

      if (Math.abs (fc) < Math.abs (fb)) {
         a = b;
         b = c;
         c = a;
         fa = fb;
         fb = fc;
         fc = fa;
      }

      for (int i = 0; i < MAXITER; i++) {
         double s, p, q, r;
         double tol1 = tol + 4.0 * Num.DBL_EPSILON * Math.abs (b);
         double xm = 0.5 * (c - b);

         if ((Math.abs (fb) == 0.0) || (Math.abs (xm) <= tol1))
            return b;

         if ((Math.abs (e) >= tol1) && (Math.abs (fa) > Math.abs (fb))) {
            if (a != c) {
               // Inverse quadratic interpolation
               q = fa / fc;
               r = fb / fc;
               s = fb / fa;
               p = s * (2.0 * xm * q * (q - r) - (b - a) * (r - 1.0));
               q = (q - 1.0) * (r - 1.0) * (s - 1.0);
            } else {
               // Linear interpolation
               s = fb / fa;
               p = 2.0 * xm * s;
               q = 1.0 - s;
            }

            // Adjust signs
            if (p > 0.0)
               q = -q;
            p = Math.abs (p);

            // Is interpolation acceptable ?
            if (((2.0 * p) >= (3.0 * xm * q - Math.abs (tol1 * q)))
                  || (p >= Math.abs (0.5 * e * q))) {
               d = xm;
               e = d;
            } else {
               e = d;
               d = p / q;
            }
         } else {
            // Bisection necessary
            d = xm;
            e = d;
         }

         a = b;
         fa = fb;
         if (Math.abs (d) > tol1)
            b += d;
         else if (xm < 0.0)
            b -= tol1;
         else
            b += tol1;
         fb = f.evaluate (b);
         if ((fb * (fc / Math.abs (fc))) > 0.0) {
            c = a;
            fc = fa;
            d = e = b - a;
         } else {
            a = b;
            b = c;
            c = a;
            fa = fb;
            fb = fc;
            fc = fa;
         }
      }

      return b;
   }


   /**
    * Computes a root x of the function in f using the
    *     bisection method. The interval [a, b] must contain the root x.
    *     The calculations are done with an approximate relative precision
    *     tol.  Returns x such that f (x) = 0.
    *  
    * @param a left endpoint of initial interval
    * 
    *    @param b right endpoint of initial interval
    * 
    *    @param f the function which is evaluated
    * 
    *    @param tol accuracy goal
    * 
    *    @return the root x
    * 
    */
   public static double bisection (double a, double b,
                                   MathFunction f, double tol) {
      // Case I = [b, a]
      if (b < a) {
         double ctemp = a;
         a = b;
         b = ctemp;
      }
      double xa = a;
      double xb = b;
      double yb = f.evaluate (b);
      double ya = f.evaluate (a);
      double x = 0, y = 0;
      final int MAXITER = 1200;   // Maximum number of iterations
      final boolean DEBUG = false;
      final double myMIN_NORMAL = 2.25e-308;

      if (DEBUG)
         System.out.println
         ("\niter              xa                   xb              f(x)");

      boolean fini = false;
      int i = 0;
      while (!fini) {
         x = (xa + xb) / 2.0;
         y = f.evaluate (x);
         if ((Math.abs (y) <= myMIN_NORMAL) ||
             (Math.abs (xb - xa) <= tol * Math.abs (x)) ||
             (Math.abs (xb - xa) <= 0)) {
            return x;
         }
         if (y * ya < 0.0)
            xb = x;
         else
            xa = x;
         ++i;
         if (DEBUG)
            System.out.printf("%3d    %18.12g     %18.12g    %14.4g%n",
                              i, xa, xb, y); 
         if (i > MAXITER) {
            System.out.println ("***** bisection:  SEARCH DOES NOT CONVERGE");
            fini = true;
         }
      }
      return x;
   }

}

 

Ads help maintain this website.