Calculates the zeros of a polynomial using Bernoulli's algorithm.

View versions (1)

Interface

#include <codecogs/maths/rootfinding/bernoulli.h>

using namespace Maths::Rootfinding;

Bernoulli's method exploits the connection between a linear difference equation and the zeros of its characteristic polynomial in order to find the zeros of a polynomial without knowing crude first approximations. Given the polynomial

p(z) = a_0 z^k + a_1 z^{k - 1} + \ldots + a_k \qquad k \geq 1 \qquad a_0 a_k \neq 0
(1)

the difference equation which has p as its characteristic polynomial is given as

a_0 x_n + a_1 x_{n - 1} + \ldots + a_k x_{n - k} = 0
(2)

Given any starting values x_0, x_1, \ldots , x_{k - 1}, the corresponding solution of the previous equation can be found numerically using the recurrence relation

x_n = - \frac {1} {a_0} (a_1 x_{n - 1} + a_2 x_{n - 2} + \ldots + a_k x_{n - k}) \qquad n \geq k
(3)

If we suppose that the zeros z_1, z_2, \ldots , z_k of p all have multiplicity 1 (i.e. distinct zeros) then the solution can be expressed in the form

x_n = c_1 z_1^n + c_2 z_2^n + \ldots + c_k z_k^n
(4)

The c_j coefficients can be computed if the zeros are known. But since these are not known, the coefficients are unknown. However, the quotients

q_n = \frac {x_{n + 1}} {x_n}
(5)

using the previous equation, are analytically represented by

q_n = \frac {c_1 z_1^{n + 1} + c_2 z_2^{n + 1} + \ldots + c_k z_k^{n + 1}}
                {c_1 z_1^n + c_2 z_2^n + \ldots c_k z_k^n}
(6)

If a polynomial p of degree K has zeros z_1, z_2, \ldots , z_k, not necessarily distinct, then the zero z_j is called dominant if its modulus is strictly greater than the moduli of the other zeros, i.e. |z_j| > |z_i| for all i \neq j. Now suppose the above polynomial p has a single dominant zero, and let this be z_1 for our case (this zero will be real if the coefficients of p are real). Also, assume that the starting values of x_0, x_1, \ldots , x_{k - 1} of the solution {x_n} are chosen so that c_1 \neq 0. It can be shown that this condition is always satisfied if the starting values are chosen so that

x_{-k + 1} = x_{-k + 2} = \ldots = x_{-1} = 0 \qquad x_0 = 1
(7)

Under these assumptions, we may write the quotients as

q_n = z_1 \frac {1 + \frac{c_2}{c_1} \left( \frac {z_2} {z_1} \right) ^ {n + 1} + \ldots +
    \frac {c_k} {c_1} \left( \frac {z_k} {z_1} \right) ^ {n + 1} }
    { 1 + \frac {c_2} {c_1} \left( \frac {z_2} {z_1} \right) ^ n + \ldots +
      \frac {c_k} {c_1} \left( \frac {z_k} {z_1} \right) ^ n }
(8)

Furthermore,

\lim_{n \rightarrow \infty} \left( \frac {z_j} {z_1} \right) ^ n = 0 \qquad j = 2, 3, \ldots , k
(9)

and it follows that

\lim_{n \rightarrow \infty} q_n = z_1
(10)

Having obtained this zero, the polynomial is deflated and the procedure is repeated. Thus this method can only furnishone or two zeros of a given polynomial at a time, and these zeros are those of largest or smallest magnitude. So, if a zero of intermediate modulus is desired, it is necessary to compute all larger (or all smaller) zeros and then remove them from the polynomial by deflation. Also if z_2 has nearly the same magnitude as z_1 the convergence process is very slow. However, if the zero of largest or smallest magnitude is the zero that is desired and is distinct, Bernoulli's method can be useful.

This algorithm finds the roots of the polynomial given as an array of coefficients and returns them wrapped inside a C++ vector object. The method described above has been used.

References

  • Jean-Pierre Moreau's Home Page, http://perso.wanadoo.fr/jean-pierre.moreau/
  • Claude Nowakowski, "Methodes de calcul numerique", PSI Editions, France, 1981
  • Wankere R. Mekwi, "Iterative Methods for Roots of Polynomials", Exeter College, University of Oxford

Example 1

#include <codecogs/maths/rootfinding/bernoulli.h>

#include <iostream>
#include <iomanip>

int main()  
{
    double A[5] = { 1, 2, -13, -14, 24 };

    std::vector<double> poly(A, A + 5), roots = Maths::RootFinding::bernoulli(poly);

    std::cout << "The roots of the polynomial are : " << std::endl << std::endl;
    int i;
    for (i = 0; i < roots.size(); i++)
    {
      std::cout << std::setw(5) << "x_" << i << " = ";
      std::cout << std::setprecision(12) << roots[i] << std::endl;
    }
    return 0;
}

Output:

The roots of the polynomial are :

   x_0 = -3.99998329472
   x_1 = 3.00000133652
   x_2 = -2.00001264664
   x_3 = 0.999994604828

Parameters

polynomial
the coefficients of the polynomial given as a C++ vector object
GPL Licence — free for non commercial use. See Licence details.