123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- // David Eberly, Geometric Tools, Redmond WA 98052
- // Copyright (c) 1998-2020
- // Distributed under the Boost Software License, Version 1.0.
- // https://www.boost.org/LICENSE_1_0.txt
- // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
- // Version: 4.0.2019.08.13
- #pragma once
- #include <cmath>
- #include <functional>
- // This is an implementation of Brent's Method for computing a root of a
- // function on an interval [t0,t1] for which F(t0)*F(t1) < 0. The method
- // uses inverse quadratic interpolation to generate a root estimate but
- // falls back to inverse linear interpolation (secant method) if
- // necessary. Moreover, based on previous iterates, the method will fall
- // back to bisection when it appears the interpolated estimate is not of
- // sufficient quality.
- //
- // maxIterations:
- // The maximum number of iterations used to locate a root. This
- // should be positive.
- // negFTolerance, posFTolerance:
- // The root estimate t is accepted when the function value F(t)
- // satisfies negFTolerance <= F(t) <= posFTolerance. The values
- // must satisfy: negFTolerance <= 0, posFTolerance >= 0.
- // stepTTolerance:
- // Brent's Method requires additional tests before an interpolated
- // t-value is accepted as the next root estimate. One of these
- // tests compares the difference of consecutive iterates and
- // requires it to be larger than a user-specified t-tolerance (to
- // ensure progress is made). This parameter is that tolerance
- // and should be nonnegative.
- // convTTolerance:
- // The root search is allowed to terminate when the current
- // subinterval [tsub0,tsub1] is sufficiently small, say,
- // |tsub1 - tsub0| <= tolerance. This parameter is that tolerance
- // and should be nonnegative.
- namespace WwiseGTE
- {
- template <typename Real>
- class RootsBrentsMethod
- {
- public:
- // It is necessary that F(t0)*F(t1) <= 0, in which case the function
- // returns 'true' and the 'root' is valid; otherwise, the function
- // returns 'false' and 'root' is invalid (do not use it). When
- // F(t0)*F(t1) > 0, the interval may very well contain a root but we
- // cannot know that. The function also returns 'false' if t0 >= t1.
- static bool Find(std::function<Real(Real)> const& F, Real t0, Real t1,
- unsigned int maxIterations, Real negFTolerance, Real posFTolerance,
- Real stepTTolerance, Real convTTolerance, Real& root)
- {
- // Parameter validation.
- if (t1 <= t0
- || maxIterations == 0
- || negFTolerance > (Real)0
- || posFTolerance < (Real)0
- || stepTTolerance < (Real)0
- || convTTolerance < (Real)0)
- {
- // The input is invalid.
- return false;
- }
- Real f0 = F(t0);
- if (negFTolerance <= f0 && f0 <= posFTolerance)
- {
- // This endpoint is an approximate root that satisfies the
- // function tolerance.
- root = t0;
- return true;
- }
- Real f1 = F(t1);
- if (negFTolerance <= f1 && f1 <= posFTolerance)
- {
- // This endpoint is an approximate root that satisfies the
- // function tolerance.
- root = t1;
- return true;
- }
- if (f0 * f1 > (Real)0)
- {
- // The input interval must bound a root.
- return false;
- }
- if (std::fabs(f0) < std::fabs(f1))
- {
- // Swap t0 and t1 so that |F(t1)| <= |F(t0)|. The number t1
- // is considered to be the best estimate of the root.
- std::swap(t0, t1);
- std::swap(f0, f1);
- }
- // Initialize values for the root search.
- Real t2 = t0, t3 = t0, f2 = f0;
- bool prevBisected = true;
- // The root search.
- for (unsigned int i = 0; i < maxIterations; ++i)
- {
- Real fDiff01 = f0 - f1, fDiff02 = f0 - f2, fDiff12 = f1 - f2;
- Real invFDiff01 = ((Real)1) / fDiff01;
- Real s;
- if (fDiff02 != (Real)0 && fDiff12 != (Real)0)
- {
- // Use inverse quadratic interpolation.
- Real infFDiff02 = ((Real)1) / fDiff02;
- Real invFDiff12 = ((Real)1) / fDiff12;
- s =
- t0 * f1 * f2 * invFDiff01 * infFDiff02 -
- t1 * f0 * f2 * invFDiff01 * invFDiff12 +
- t2 * f0 * f1 * infFDiff02 * invFDiff12;
- }
- else
- {
- // Use inverse linear interpolation (secant method).
- s = (t1 * f0 - t0 * f1) * invFDiff01;
- }
- // Compute values need in the accept-or-reject tests.
- Real tDiffSAvr = s - ((Real)0.75) * t0 - ((Real)0.25) * t1;
- Real tDiffS1 = s - t1;
- Real absTDiffS1 = std::fabs(tDiffS1);
- Real absTDiff12 = std::fabs(t1 - t2);
- Real absTDiff23 = std::fabs(t2 - t3);
- bool currBisected = false;
- if (tDiffSAvr * tDiffS1 > (Real)0)
- {
- // The value s is not between 0.75*t0 + 0.25*t1 and t1.
- // NOTE: The algorithm sometimes has t0 < t1 but sometimes
- // t1 < t0, so the between-ness test does not use simple
- // comparisons.
- currBisected = true;
- }
- else if (prevBisected)
- {
- // The first of Brent's tests to determine whether to
- // accept the interpolated s-value.
- currBisected =
- (absTDiffS1 >= ((Real)0.5) * absTDiff12) ||
- (absTDiff12 <= stepTTolerance);
- }
- else
- {
- // The second of Brent's tests to determine whether to
- // accept the interpolated s-value.
- currBisected =
- (absTDiffS1 >= ((Real)0.5) * absTDiff23) ||
- (absTDiff23 <= stepTTolerance);
- }
- if (currBisected)
- {
- // One of the additional tests failed, so reject the
- // interpolated s-value and use bisection instead.
- s = ((Real)0.5) * (t0 + t1);
- if (s == t0 || s == t1)
- {
- // The numbers t0 and t1 are consecutive
- // floating-point numbers.
- root = s;
- return true;
- }
- prevBisected = true;
- }
- else
- {
- prevBisected = false;
- }
- // Evaluate the function at the new estimate and test for
- // convergence.
- Real fs = F(s);
- if (negFTolerance <= fs && fs <= posFTolerance)
- {
- root = s;
- return true;
- }
- // Update the subinterval to include the new estimate as an
- // endpoint.
- t3 = t2;
- t2 = t1;
- f2 = f1;
- if (f0 * fs < (Real)0)
- {
- t1 = s;
- f1 = fs;
- }
- else
- {
- t0 = s;
- f0 = fs;
- }
- // Allow the algorithm to terminate when the subinterval is
- // sufficiently small.
- if (std::fabs(t1 - t0) <= convTTolerance)
- {
- root = t1;
- return true;
- }
- // A loop invariant is that t1 is the root estimate,
- // F(t0)*F(t1) < 0 and |F(t1)| <= |F(t0)|.
- if (std::fabs(f0) < std::fabs(f1))
- {
- std::swap(t0, t1);
- std::swap(f0, f1);
- }
- }
- // Failed to converge in the specified number of iterations.
- return false;
- }
- };
- }
|