RootsBrentsMethod.h 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <cmath>
  9. #include <functional>
  10. // This is an implementation of Brent's Method for computing a root of a
  11. // function on an interval [t0,t1] for which F(t0)*F(t1) < 0. The method
  12. // uses inverse quadratic interpolation to generate a root estimate but
  13. // falls back to inverse linear interpolation (secant method) if
  14. // necessary. Moreover, based on previous iterates, the method will fall
  15. // back to bisection when it appears the interpolated estimate is not of
  16. // sufficient quality.
  17. //
  18. // maxIterations:
  19. // The maximum number of iterations used to locate a root. This
  20. // should be positive.
  21. // negFTolerance, posFTolerance:
  22. // The root estimate t is accepted when the function value F(t)
  23. // satisfies negFTolerance <= F(t) <= posFTolerance. The values
  24. // must satisfy: negFTolerance <= 0, posFTolerance >= 0.
  25. // stepTTolerance:
  26. // Brent's Method requires additional tests before an interpolated
  27. // t-value is accepted as the next root estimate. One of these
  28. // tests compares the difference of consecutive iterates and
  29. // requires it to be larger than a user-specified t-tolerance (to
  30. // ensure progress is made). This parameter is that tolerance
  31. // and should be nonnegative.
  32. // convTTolerance:
  33. // The root search is allowed to terminate when the current
  34. // subinterval [tsub0,tsub1] is sufficiently small, say,
  35. // |tsub1 - tsub0| <= tolerance. This parameter is that tolerance
  36. // and should be nonnegative.
  37. namespace WwiseGTE
  38. {
  39. template <typename Real>
  40. class RootsBrentsMethod
  41. {
  42. public:
  43. // It is necessary that F(t0)*F(t1) <= 0, in which case the function
  44. // returns 'true' and the 'root' is valid; otherwise, the function
  45. // returns 'false' and 'root' is invalid (do not use it). When
  46. // F(t0)*F(t1) > 0, the interval may very well contain a root but we
  47. // cannot know that. The function also returns 'false' if t0 >= t1.
  48. static bool Find(std::function<Real(Real)> const& F, Real t0, Real t1,
  49. unsigned int maxIterations, Real negFTolerance, Real posFTolerance,
  50. Real stepTTolerance, Real convTTolerance, Real& root)
  51. {
  52. // Parameter validation.
  53. if (t1 <= t0
  54. || maxIterations == 0
  55. || negFTolerance > (Real)0
  56. || posFTolerance < (Real)0
  57. || stepTTolerance < (Real)0
  58. || convTTolerance < (Real)0)
  59. {
  60. // The input is invalid.
  61. return false;
  62. }
  63. Real f0 = F(t0);
  64. if (negFTolerance <= f0 && f0 <= posFTolerance)
  65. {
  66. // This endpoint is an approximate root that satisfies the
  67. // function tolerance.
  68. root = t0;
  69. return true;
  70. }
  71. Real f1 = F(t1);
  72. if (negFTolerance <= f1 && f1 <= posFTolerance)
  73. {
  74. // This endpoint is an approximate root that satisfies the
  75. // function tolerance.
  76. root = t1;
  77. return true;
  78. }
  79. if (f0 * f1 > (Real)0)
  80. {
  81. // The input interval must bound a root.
  82. return false;
  83. }
  84. if (std::fabs(f0) < std::fabs(f1))
  85. {
  86. // Swap t0 and t1 so that |F(t1)| <= |F(t0)|. The number t1
  87. // is considered to be the best estimate of the root.
  88. std::swap(t0, t1);
  89. std::swap(f0, f1);
  90. }
  91. // Initialize values for the root search.
  92. Real t2 = t0, t3 = t0, f2 = f0;
  93. bool prevBisected = true;
  94. // The root search.
  95. for (unsigned int i = 0; i < maxIterations; ++i)
  96. {
  97. Real fDiff01 = f0 - f1, fDiff02 = f0 - f2, fDiff12 = f1 - f2;
  98. Real invFDiff01 = ((Real)1) / fDiff01;
  99. Real s;
  100. if (fDiff02 != (Real)0 && fDiff12 != (Real)0)
  101. {
  102. // Use inverse quadratic interpolation.
  103. Real infFDiff02 = ((Real)1) / fDiff02;
  104. Real invFDiff12 = ((Real)1) / fDiff12;
  105. s =
  106. t0 * f1 * f2 * invFDiff01 * infFDiff02 -
  107. t1 * f0 * f2 * invFDiff01 * invFDiff12 +
  108. t2 * f0 * f1 * infFDiff02 * invFDiff12;
  109. }
  110. else
  111. {
  112. // Use inverse linear interpolation (secant method).
  113. s = (t1 * f0 - t0 * f1) * invFDiff01;
  114. }
  115. // Compute values need in the accept-or-reject tests.
  116. Real tDiffSAvr = s - ((Real)0.75) * t0 - ((Real)0.25) * t1;
  117. Real tDiffS1 = s - t1;
  118. Real absTDiffS1 = std::fabs(tDiffS1);
  119. Real absTDiff12 = std::fabs(t1 - t2);
  120. Real absTDiff23 = std::fabs(t2 - t3);
  121. bool currBisected = false;
  122. if (tDiffSAvr * tDiffS1 > (Real)0)
  123. {
  124. // The value s is not between 0.75*t0 + 0.25*t1 and t1.
  125. // NOTE: The algorithm sometimes has t0 < t1 but sometimes
  126. // t1 < t0, so the between-ness test does not use simple
  127. // comparisons.
  128. currBisected = true;
  129. }
  130. else if (prevBisected)
  131. {
  132. // The first of Brent's tests to determine whether to
  133. // accept the interpolated s-value.
  134. currBisected =
  135. (absTDiffS1 >= ((Real)0.5) * absTDiff12) ||
  136. (absTDiff12 <= stepTTolerance);
  137. }
  138. else
  139. {
  140. // The second of Brent's tests to determine whether to
  141. // accept the interpolated s-value.
  142. currBisected =
  143. (absTDiffS1 >= ((Real)0.5) * absTDiff23) ||
  144. (absTDiff23 <= stepTTolerance);
  145. }
  146. if (currBisected)
  147. {
  148. // One of the additional tests failed, so reject the
  149. // interpolated s-value and use bisection instead.
  150. s = ((Real)0.5) * (t0 + t1);
  151. if (s == t0 || s == t1)
  152. {
  153. // The numbers t0 and t1 are consecutive
  154. // floating-point numbers.
  155. root = s;
  156. return true;
  157. }
  158. prevBisected = true;
  159. }
  160. else
  161. {
  162. prevBisected = false;
  163. }
  164. // Evaluate the function at the new estimate and test for
  165. // convergence.
  166. Real fs = F(s);
  167. if (negFTolerance <= fs && fs <= posFTolerance)
  168. {
  169. root = s;
  170. return true;
  171. }
  172. // Update the subinterval to include the new estimate as an
  173. // endpoint.
  174. t3 = t2;
  175. t2 = t1;
  176. f2 = f1;
  177. if (f0 * fs < (Real)0)
  178. {
  179. t1 = s;
  180. f1 = fs;
  181. }
  182. else
  183. {
  184. t0 = s;
  185. f0 = fs;
  186. }
  187. // Allow the algorithm to terminate when the subinterval is
  188. // sufficiently small.
  189. if (std::fabs(t1 - t0) <= convTTolerance)
  190. {
  191. root = t1;
  192. return true;
  193. }
  194. // A loop invariant is that t1 is the root estimate,
  195. // F(t0)*F(t1) < 0 and |F(t1)| <= |F(t0)|.
  196. if (std::fabs(f0) < std::fabs(f1))
  197. {
  198. std::swap(t0, t1);
  199. std::swap(f0, f1);
  200. }
  201. }
  202. // Failed to converge in the specified number of iterations.
  203. return false;
  204. }
  205. };
  206. }