If you need some diagnostic output to see what is going on, you can
@@ -208,13 +220,15 @@
Raphson Method
- Given an initial guess x0 the subsequent values are computed using:
+ Given an initial guess x0 the subsequent values are
+ computed using:
- Out of bounds steps revert to bisection of the current bounds.
+ Out of bounds steps revert to bisection
+ of the current bounds.
Under ideal conditions, the number of correct digits doubles with each iteration.
@@ -235,7 +249,8 @@
wrong direction) causes the method to revert to a Newton-Raphson step.
- Out of bounds steps revert to bisection of the current bounds.
+ Out-of-bounds steps revert to bisection
+ of the current bounds.
Under ideal conditions, the number of correct digits trebles with each iteration.
@@ -246,7 +261,8 @@
Method
- Given an initial guess x0 the subsequent values are computed using:
+ Given an initial guess x0 the subsequent values are
+ computed using:
@@ -258,7 +274,8 @@
by more than 10%.
- Out of bounds steps revert to bisection of the current bounds.
+ Out of bounds steps revert to bisection
+ of the current bounds.
Under ideal conditions, the number of correct digits trebles with each iteration.
@@ -275,90 +292,140 @@
- To begin with lets solve the problem using Newton-Raphson iterations, we'll
- begin by defining a function object (functor) that returns the evaluation
- of the function to solve, along with its first derivative f'(x):
+ To begin with let us solve the problem using Newton-Raphson iterations. We
+ will begin by defining a function object (functor) that returns the evaluation
+ of the function to solve, along with its first derivative f'(x):
template <class T>
-struct cbrt_functor
-{
- cbrt_functor(T const& target) : a(target)
- {
- }
- boost::math::tuple<T, T> operator()(T const& z)
- {
- return boost::math::make_tuple(
- z*z*z - a,
- 3 * z*z);
- }
+struct cbrt_functor_1stderiv
+{
+
+ cbrt_functor_1stderiv(T const& target) : value(target)
+ {
+ }
+
+ std::pair<T, T> operator()(T const& z)
+ {
+ T fx = z*z*z - value;
+ T d1x = 3 * z*z;
+ return std::make_pair(fx, d1x);
+ }
private:
- T a;
-};
+ T value;
+};
Implementing the cube root is fairly trivial now, the hardest part is finding
- a good approximation to begin with: in this case we'll just divide the exponent
- by three:
+ a good approximation to begin with: in this case, we'll just divide the exponent
+ by three. (There are better but more complex 'guess' algorithms used in 'real-life'.)
template <class T>
-T cbrt(T z)
-{
- using namespace std;
- using namespace boost::math::tools;
+T cbrt_1deriv(T x)
+{
+ using namespace std;
+ using namespace boost::math::tools;
- int exp;
- frexp(z, &exp);
- T min = ldexp(0.5, exp/3);
- T max = ldexp(2.0, exp/3);
- T guess = ldexp(1.0, exp/3);
- int digits = std::numeric_limits<T>::digits;
- return newton_raphson_iterate(detail::cbrt_functor<T>(z), guess, min, max, digits);
-}
+ int exponent;
+ frexp(x, &exponent);
+ T guess = ldexp(1., exponent/3);
+
+ T min = ldexp(0.5, exponent/3);
+ T max = ldexp(2., exponent/3);// Maximum possible value is twice our guess.
+
+ int digits = std::numeric_limits<T>::digits;
+
+ boost::uintmax_t maxit = 20;
+
+ T result = newton_raphson_iterate(cbrt_functor_1stderiv<T>(x), guess, min, max, digits, maxit);
+
+ cout << "Iterations " << maxit << endl;
+ return result;
+}
- Using the test data in libs/math/test/cbrt_test.cpp this
+ Using the test data in ../../test/cbrt_data.ipp,
+ the test ../../test/test_cbrt.cpp
found the cube root exact to the last digit in every case, and in no more
- than 6 iterations at double precision. However, you will note that a high
- precision was used in this example, exactly what was warned against earlier
- on in these docs! In this particular case it is possible to compute f(x)
- exactly and without undue cancellation error, so a high limit is not too
- much of an issue. However, reducing the limit to std::numeric_limits<T>::digits * 2 / 3
+ than 6 iterations at double precision.
+
+
+ However, you will note that a high (maximum) precision was used in this example,
+ exactly what was warned against earlier on in these docs! In this particular
+ case it is possible to compute f(x) exactly and without
+ undue cancellation error, so a high limit is not too much of an issue. However,
+ reducing the limit to std::numeric_limits<T>::digits * 2 / 3
gave full precision in all but one of the test cases (and that one was out
by just one bit). The maximum number of iterations remained 6, but in most
cases was reduced by one.
+
+ So we might decide to trade the accuracy and limit iterations for speed.
+
+int get_digits = (digits * 2) /3;
+boost::uintmax_t maxit = 7;
+
Note also that the above code omits a probably optimization by computing
- z², and reusing it, omits error handling, and does not handle negative values
- of z correctly. (These are left as an exercise for the reader!)
+ z², and reusing it, omits error handling, and does not
+ handle negative values of z correctly. (As is traditional, these are left
+ as an exercise for the reader!)
- The boost::math::cbrt function also includes these and other
- improvements.
+ The boost::math::cbrt function cbrt
+ also includes these and other improvements.
- Now let's adapt the functor slightly to return the second derivative as well:
+ Now let's adapt the functor slightly to return the second derivative f''(x)
+ as well:
template <class T>
-struct cbrt_functor
-{
- cbrt_functor(T const& target) : a(target){}
- boost::math::tuple<T, T, T> operator()(T const& z)
- {
- return boost::math::make_tuple(
- z*z*z - a,
- 3 * z*z,
- 6 * z);
- }
+struct cbrt_functor_2deriv
+{
+ cbrt_functor_2deriv(T const& to_find_root_of) : value(to_find_root_of)
+ {
+ }
+
+
+ boost::math::tuple<T, T, T> operator()(T const& x)
+ {
+ using boost::math::make_tuple;
+ T fx = x*x*x - value;
+ T dx = 3 * x*x;
+ T d2x = 6 * x;
+ return make_tuple(fx, dx, d2x);
+ }
private:
- T a;
-};
+ T value;
+};
And then adapt the cbrt function
to use Halley iterations:
template <class T>
+T cbrt_2deriv(T x)
+{
+
+ using namespace boost::math;
+ int exponent;
+ frexp(x, &exponent);
+ T guess = ldexp(1., exponent/3);
+ T min = ldexp(0.5, exponent/3);
+ T max = ldexp(2., exponent/3);// Maximum possible value is twice our guess.
+
+ int digits = std::numeric_limits<T>::digits /2 ;
+
+
+
+
+ boost::uintmax_t maxit = 10;
+ T result = halley_iterate(cbrt_functor_2deriv<T>(x), guess, min, max, digits, maxit);
+
+ cout << "Iterations " << maxit << endl;
+ return result;
+}
+
+template <class T>
T cbrt(T z)
{
using namespace std;
@@ -382,21 +449,27 @@
Just to complete the picture, we could have called schroeder_iterate
in the last example: and in fact it makes no difference to the accuracy or
number of iterations in this particular case. However, the relative performance
- of these two methods may vary depending upon the nature of f(x), and the
- accuracy to which the initial guess can be computed. There appear to be no
- generalisations that can be made except "try them and see".
+ of these two methods may vary depending upon the nature of f(x),
+ and the accuracy to which the initial guess can be computed. There appear
+ to be no generalisations that can be made except "try them and see".
- Finally, had we called cbrt
- with NTL::RR set to
- 1000 bit precision, then full precision can be obtained with just 7 iterations.
- To put that in perspective, an increase in precision by a factor of 20, has
+ Finally, had we called cbrt
+ with Boost.Multiprecision
+ or NTL::RR set to 1000
+ bit precision, then full precision still can be obtained with just 7 iterations.
+ To put that in perspective, an increase in precision by a factor of 20 has
less than doubled the number of iterations. That just goes to emphasise that
most of the iterations are used up getting the first few digits correct:
after that these methods can churn out further digits with remarkable efficiency.
- Or to put it another way: nothing beats a really good initial guess!
+ Or to put it another way: nothing beats a really good
+ initial guess!
+
+
+ See root_finding_example.cpp
+ for full source code.