RiddersSolver.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.math4.legacy.analysis.solvers;

  18. import org.apache.commons.math4.legacy.exception.NoBracketingException;
  19. import org.apache.commons.math4.legacy.exception.TooManyEvaluationsException;
  20. import org.apache.commons.math4.core.jdkmath.JdkMath;

  21. /**
  22.  * Implements the <a href="http://mathworld.wolfram.com/RiddersMethod.html">
  23.  * Ridders' Method</a> for root finding of real univariate functions. For
  24.  * reference, see C. Ridders, <i>A new algorithm for computing a single root
  25.  * of a real continuous function </i>, IEEE Transactions on Circuits and
  26.  * Systems, 26 (1979), 979 - 980.
  27.  * <p>
  28.  * The function should be continuous but not necessarily smooth.</p>
  29.  *
  30.  * @since 1.2
  31.  */
  32. public class RiddersSolver extends AbstractUnivariateSolver {
  33.     /** Default absolute accuracy. */
  34.     private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;

  35.     /**
  36.      * Construct a solver with default accuracy (1e-6).
  37.      */
  38.     public RiddersSolver() {
  39.         this(DEFAULT_ABSOLUTE_ACCURACY);
  40.     }
  41.     /**
  42.      * Construct a solver.
  43.      *
  44.      * @param absoluteAccuracy Absolute accuracy.
  45.      */
  46.     public RiddersSolver(double absoluteAccuracy) {
  47.         super(absoluteAccuracy);
  48.     }
  49.     /**
  50.      * Construct a solver.
  51.      *
  52.      * @param relativeAccuracy Relative accuracy.
  53.      * @param absoluteAccuracy Absolute accuracy.
  54.      */
  55.     public RiddersSolver(double relativeAccuracy,
  56.                          double absoluteAccuracy) {
  57.         super(relativeAccuracy, absoluteAccuracy);
  58.     }

  59.     /**
  60.      * {@inheritDoc}
  61.      */
  62.     @Override
  63.     protected double doSolve()
  64.         throws TooManyEvaluationsException,
  65.                NoBracketingException {
  66.         double min = getMin();
  67.         double max = getMax();
  68.         // [x1, x2] is the bracketing interval in each iteration
  69.         // x3 is the midpoint of [x1, x2]
  70.         // x is the new root approximation and an endpoint of the new interval
  71.         double x1 = min;
  72.         double y1 = computeObjectiveValue(x1);
  73.         double x2 = max;
  74.         double y2 = computeObjectiveValue(x2);

  75.         // check for zeros before verifying bracketing
  76.         if (y1 == 0) {
  77.             return min;
  78.         }
  79.         if (y2 == 0) {
  80.             return max;
  81.         }
  82.         verifyBracketing(min, max);

  83.         final double absoluteAccuracy = getAbsoluteAccuracy();
  84.         final double functionValueAccuracy = getFunctionValueAccuracy();
  85.         final double relativeAccuracy = getRelativeAccuracy();

  86.         double oldx = Double.POSITIVE_INFINITY;
  87.         while (true) {
  88.             // calculate the new root approximation
  89.             final double x3 = 0.5 * (x1 + x2);
  90.             final double y3 = computeObjectiveValue(x3);
  91.             if (JdkMath.abs(y3) <= functionValueAccuracy) {
  92.                 return x3;
  93.             }
  94.             final double delta = 1 - (y1 * y2) / (y3 * y3);  // delta > 1 due to bracketing
  95.             final double correction = (JdkMath.signum(y2) * JdkMath.signum(y3)) *
  96.                                       (x3 - x1) / JdkMath.sqrt(delta);
  97.             final double x = x3 - correction;                // correction != 0
  98.             final double y = computeObjectiveValue(x);

  99.             // check for convergence
  100.             final double tolerance = JdkMath.max(relativeAccuracy * JdkMath.abs(x), absoluteAccuracy);
  101.             if (JdkMath.abs(x - oldx) <= tolerance) {
  102.                 return x;
  103.             }
  104.             if (JdkMath.abs(y) <= functionValueAccuracy) {
  105.                 return x;
  106.             }

  107.             // prepare the new interval for next iteration
  108.             // Ridders' method guarantees x1 < x < x2
  109.             if (correction > 0.0) {             // x1 < x < x3
  110.                 if (JdkMath.signum(y1) + JdkMath.signum(y) == 0.0) {
  111.                     x2 = x;
  112.                     y2 = y;
  113.                 } else {
  114.                     x1 = x;
  115.                     x2 = x3;
  116.                     y1 = y;
  117.                     y2 = y3;
  118.                 }
  119.             } else {                            // x3 < x < x2
  120.                 if (JdkMath.signum(y2) + JdkMath.signum(y) == 0.0) {
  121.                     x1 = x;
  122.                     y1 = y;
  123.                 } else {
  124.                     x1 = x3;
  125.                     x2 = x;
  126.                     y1 = y3;
  127.                     y2 = y;
  128.                 }
  129.             }
  130.             oldx = x;
  131.         }
  132.     }
  133. }