3 XCSoar Glide Computer - http://www.xcsoar.org/
4 Copyright (C) 2000-2013 The XCSoar Project
5 A detailed list of copyright holders can be found in the file "AUTHORS".
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include "ZeroFinder.hpp"
29 static constexpr fixed
epsilon(fixed::internal(), 2);
30 static const fixed sqrt_epsilon
= sqrt(epsilon
);
32 /** machine tolerance */
33 static constexpr fixed
epsilon(std::numeric_limits
<fixed
>::epsilon());
34 /** sqrt of machine tolerance */
35 static const fixed sqrt_epsilon
= sqrt(epsilon
);
37 static const fixed
r((3. - sqrt(5.0)) / 2); /* Gold section ratio */
39 #define fixed_threequaters fixed(0.75)
42 ZeroFinder::tolerance_actual_min(const fixed x
) const
44 return sqrt_epsilon
* fabs(x
) + tolerance
* fixed_third
;
48 ZeroFinder::tolerance_actual_zero(const fixed x
) const
50 return Double(epsilon
* fabs(x
)) + Half(tolerance
);
53 //#define INSTRUMENT_ZERO
54 #ifdef INSTRUMENT_ZERO
55 unsigned long zero_skipped
= 0;
56 unsigned long zero_total
= 0;
60 ZeroFinder::solution_within_tolerance(const fixed x
,
64 // are we away from the edges? if so, check improved solution
65 const fixed x_minus
= x
-tol_act
;
68 const fixed x_plus
= x
+tol_act
;
72 const fixed fx
= f(x
);
77 // existing solution is good
83 - When you can, use find_zero search, since it narrows in at rate of 1.6 per step compared
84 to 1.3 for the find_min.
88 ************************************************************************
90 * function ZEROIN - obtain a function zero within the given range
93 * fixed zeroin(ax,bx,f,tol)
94 * fixed ax; Root will be seeked for within
95 * fixed bx; a range [ax,bx]
96 * fixed (*f)(fixed x); Name of the function whose zero
98 * fixed tol; Acceptable tolerance for the root
100 * May be specified as 0.0 to cause
101 * the program to find the root as
102 * accurate as possible
105 * Zeroin returns an estimate for the root with accuracy
106 * 4*epsilon*abs(x) + tol
109 * G.Forsythe, M.Malcolm, C.Moler, Computer methods for mathematical
110 * computations. M., Mir, 1980, p.180 of the Russian edition
112 * The function makes use of the bissection procedure combined with
113 * the linear or quadric inverse interpolation.
114 * At every step program operates on three abscissae - a, b, and c.
115 * b - the last and the best approximation to the root
116 * a - the last but one approximation
117 * c - the last but one or even earlier approximation than a that
118 * 1) |f(b)| <= |f(c)|
119 * 2) f(b) and f(c) have opposite signs, i.e. b and c confine
121 * At every step Zeroin selects one of the two new approximations, the
122 * former being obtained by the bissection procedure and the latter
123 * resulting in the interpolation (if a,b, and c are all different
124 * the quadric interpolation is utilized, otherwise the linear one).
125 * If the latter (i.e. obtained by the interpolation) point is
126 * reasonable (i.e. lies within the current interval [b,c] not being
127 * too close to the boundaries) it is accepted. The bissection result
128 * is used in the other case. Therefore, the range of uncertainty is
129 * ensured to be reduced at least by the factor 1.6
131 ************************************************************************
134 fixed
ZeroFinder::find_zero(const fixed xstart
) {
135 #ifdef INSTRUMENT_ZERO
138 if ((xmin
<=xstart
) || (xstart
<=xmax
) ||
139 (f(xstart
)> sqrt_epsilon
))
140 return find_zero_actual(xstart
);
141 #ifdef INSTRUMENT_ZERO
148 ZeroFinder::find_zero_actual(const fixed xstart
)
150 fixed a
, b
, c
; // Abscissae, descr. see above
155 bool b_best
= true; // b is best and last called
163 // Main iteration loop
165 // Distance from the last but one to the last approximation
166 fixed prev_step
= b
- a
;
168 if (fabs(fc
) < fabs(fb
)) {
169 // Swap data for b to be the best approximation
184 const fixed tol_act
= tolerance_actual_zero(b
);
186 // Step at this iteration
187 fixed new_step
= Half(c
- b
);
189 if (fabs(new_step
) <= tol_act
|| fabs(fb
) < sqrt_epsilon
) {
194 // Acceptable approx. is found
198 // Decide if the interpolation can be tried
200 // If prev_step was large enough and was in true direction,
201 // interpolation may be tried
202 if (fabs(prev_step
) >= tol_act
&& fabs(fa
) > fabs(fb
)) {
203 // Interpolation step is calculated in the form p/q;
204 // division operations is delayed until the last moment
208 const fixed cb
= c
- b
;
209 // If we have only two distinct points
210 // -> linear interpolation can only be applied
212 const fixed t1
= fb
/ fa
;
216 // Quadric inverse interpolation
218 const fixed t1
= fb
/ fc
;
219 const fixed t2
= fb
/ fa
;
220 p
= t2
* (cb
* q
* (q
- t1
) - (b
- a
) * (t1
- fixed(1)));
221 q
= (q
- fixed(1)) * (t1
- fixed(1)) * (t2
- fixed(1));
224 // p was calculated with the opposite sign;
225 // make p positive and assign possible minus to q
231 // If b+p/q falls in [b,c] and isn't too large it is accepted
232 // If p/q is too large then the bissection procedure can
233 // reduce [b,c] range to more extent
234 if (p
< (fixed_threequaters
* cb
* q
- Half(fabs(tol_act
* q
)))
235 && p
< fabs(Half(prev_step
* q
)))
239 // Adjust the step to be not less than tolerance
240 limit_tolerance(new_step
, tol_act
);
242 // Save the previous approx.
246 // Do step to a new approxim.
250 // Adjust c for it to have a sign opposite to that of b
251 if ((positive(fb
) && positive(fc
)) || (negative(fb
) && negative(fc
))) {
263 ************************************************************************
265 * function FMINBR - one-dimensional search for a function minimum
266 * over the given range
269 * fixed fminbr(a,b,f,tolerance)
270 * fixed a; Minimum will be seeked for over
271 * fixed b; a range [a,b], a being < b.
272 * fixed (*f)(fixed x); Name of the function whose minimum
274 * fixed tolerance; Acceptable tolerance for the minimum
275 * location. It have to be positive
276 * (e.g. may be specified as epsilon)
279 * Fminbr returns an estimate for the minimum location with accuracy
280 * 3*sqrt_epsilon*abs(x) + tolerance.
281 * The function always obtains a local minimum which coincides with
282 * the global one only if a function under investigation being
284 * If a function being examined possesses no local minimum within
285 * the given range, Fminbr returns 'a' (if f(a) < f(b)), otherwise
286 * it returns the right range boundary value b.
289 * G.Forsythe, M.Malcolm, C.Moler, Computer methods for mathematical
290 * computations. M., Mir, 1980, p.202 of the Russian edition
292 * The function makes use of the "gold section" procedure combined with
293 * the parabolic interpolation.
294 * At every step program operates three abscissae - x,v, and w.
295 * x - the last and the best approximation to the minimum location,
296 * i.e. f(x) <= f(a) or/and f(x) <= f(b)
297 * (if the function f has a local minimum in (a,b), then the both
298 * conditions are fulfiled after one or two steps).
299 * v,w are previous approximations to the minimum location. They may
300 * coincide with a, b, or x (although the algorithm tries to make all
301 * u, v, and w distinct). Points x, v, and w are used to construct
302 * interpolating parabola whose minimum will be treated as a new
303 * approximation to the minimum location if the former falls within
304 * [a,b] and reduces the range enveloping minimum more efficient than
305 * the gold section procedure.
306 * When f(x) has a second derivative positive at the minimum location
307 * (not coinciding with a or b) the procedure converges superlinearly
308 * at a rate order about 1.324
310 ************************************************************************
313 fixed
ZeroFinder::find_min(const fixed xstart
) {
314 #ifdef INSTRUMENT_ZERO
317 if (!solution_within_tolerance(xstart
, tolerance_actual_min(xstart
)))
318 return find_min_actual(xstart
);
319 #ifdef INSTRUMENT_ZERO
326 ZeroFinder::find_min_actual(const fixed xstart
)
328 fixed x
, v
, w
; // Abscissae, descr. see above
336 assert(positive(tolerance
) && b
> a
);
338 /* First step - always gold section*/
339 x
= w
= v
= a
+ r
* (b
- a
);
342 // Main iteration loop
344 // Range over which the minimum is seeked for
345 const fixed range
= b
- a
;
346 const fixed middle_range
= Half(a
+ b
);
349 const fixed tol_act
= tolerance_actual_min(x
);
350 const fixed double_tol_act
= Double(tol_act
);
352 if (fabs(x
-middle_range
) + Half(range
) <= double_tol_act
) {
357 // Acceptable approx. is found
361 // Step at this iteration
362 // Obtain the gold section step
363 fixed new_step
= r
* (x
< middle_range
? b
- x
: a
- x
);
365 // Decide if the interpolation can be tried
367 // If x and w are distinct
368 // interpolation may be tried
369 if (fabs(x
- w
) >= tol_act
) {
370 // Interpolation step is calculated as p/q;
371 // division operation is delayed until last moment
375 const fixed t
= (x
- w
) * (fx
- fv
);
376 q
= (x
- v
) * (fx
- fw
);
377 p
= (x
- v
) * q
- (x
- w
) * t
;
380 // q was calculated with the opposite sign;
381 // make q positive and assign possible minus to p
387 // If x+p/q falls in [a,b] not too close to a and b,
388 // and isn't too large it is accepted
389 // If p/q is too large then the gold section procedure can
390 // reduce [a,b] range to more extent
391 if (fabs(p
) < fabs(new_step
* q
) && p
> q
* (a
- x
+ double_tol_act
)
392 && p
< q
* (b
- x
- double_tol_act
))
396 // Adjust the step to be not less than tolerance
397 limit_tolerance(new_step
, tol_act
);
399 // Obtain the next approximation to min and reduce the enveloping range
401 // Tentative point for the min
402 const fixed t
= x
+ new_step
;
403 const fixed ft
= f(t
);
404 // t is a better approximation
406 // Reduce the range so that t would fall within it
409 // Assign the best approx to x
419 // x remains the better approx
421 // Reduce the range enclosing x
424 if ((ft
<= fw
) || (w
== x
)) {
429 } else if ((ft
<= fv
) || (v
== x
) || (v
== w
)) {