| home | send comment | send link | add bookmark |

Solving polynomial equations with the Lin-Bairstow method

by Stephen R. Schmitt


Polynomials

A polynomial is an expression of some terms that are distinct powers in one ore more variables. The Lin-Bairstow method solves for the roots of a polynomial in one variable, x. The highest power in x is the degree (or order) of the polynomial. A polynomial of 2nd degree is a quadratic.

A polynomial of degree n, P(x), has a factor of (x - r) if and only if P(r) = 0. Then r is said to be a root of the polynomial. Such a polynomial has a factorization of the form:

P(x) = (x - r)Q(x) = 0
where Q(x) is a polynomial of degree n - 1. If P(x) has real coefficients and if x is a complex root of P(x), then the complex conjugate of x is also a root of P(x). A polynomial can have all real roots, or a mix of real roots and pairs of complex conjugate roots.

Method

The Lin-Bairstow method is a procedure for finding the quadratic factors for the complex conjugate roots of a polynomial P(x) with real coefficients. It uses a strategy of searching for quadratic factors of the form:
x2 - 2·a·x + a2 + b2
which may have pairs of complex conjugate roots:
x = a ± ib
The advantage of this is that complex arithmetic is avoided. Polynomial deflation is used to simplify the problem.

Zeno source code

Zeno 1.2 is an interpreter for the Zeno programming language. It is an easy to learn and is suitable for educational purposes.


const TOLERANCE : real := 0.000000000001
const DIM : int := 11           % at least poly order + 2

type rvector : array[DIM] of real

type complex : record
               re : real
               im : real
               end record

type cvector : array[DIM] of complex


program

    var coeff : rvector
    var roots : cvector
    var r : complex
    var num_roots, poly_order, i : int

    % X^4 - 16 = 0
    poly_order := 4
    coeff[1] := -16
    coeff[2] :=   0
    coeff[3] :=   0
    coeff[4] :=   0
    coeff[5] :=   1
    put "\nthe polynomial:"
    print_polynomial( coeff, poly_order )

    if Lin_Bairstow( coeff, roots, poly_order ) then
        put "has roots:"
        for i := 1...poly_order do
            r.re := roots[i].re
            r.im := roots[i].im
            put cstr( r )
        end for        
    else
        put "could not solve"
    end if
    
    % X^5 - 4·X^4 + 1·X^3 + 4·X^2 + 10·X - 12 = 0
    poly_order := 5
    coeff[1] := -12
    coeff[2] :=  10
    coeff[3] :=   4
    coeff[4] :=   1
    coeff[5] :=  -4
    coeff[6] :=   1
    put "\nthe polynomial:"
    print_polynomial( coeff, poly_order )

    if Lin_Bairstow( coeff, roots, poly_order ) then
        put "has roots:"
        for i := 1...poly_order do
            r.re := roots[i].re
            r.im := roots[i].im
            put cstr( r )
        end for        
    else
        put "could not solve"
    end if
    
end program

%
% The Lin-Bairstow method improves on the deflation method to solve 
% for the complex roots of the following polynomial:
%
%     y = coeff(1) + coeff(2) X + coeff(3) X^2 +...+ coeff(n+1) X^n
%
% parameters:
%
%   coeff      must be an array with at least order+1 elements.
%   roots      output array of roots
%   order      order of polynomial
%
function Lin_Bairstow( var coeff : rvector, 
                       var roots : cvector, 
                       order     : int ) : boolean

    const SMALL : real := 0.00000001
    var a, b, c, d : rvector
    var alfa1, alfa2 : real
    var beta1, beta2 : real
    var delta1, delta2, delta3 : real
    var i, j, k : int
    var count : int
    var n : int
    var n1 : int
    var n2 : int

    n  := order
    n1 := n + 1
    n2 := n + 2
  
    % is the coefficient of the highest term zero?
    if abs( coeff[1] ) < SMALL then
        return false
    end if

    for i := 0...n do
        a[n1-i+1] := coeff[i+1]
    end for
    
    % is highest coeff not close to 1?
    if abs( a[2] - 1 ) > SMALL then
        % adjust coefficients because a(1) != 1
        for i := 3...n2 do
            a[i] := a[i] / a[2]
        end for
        a[2] := 1
    end if

    % initialize root counter
    count := 1

    %
    % start the main Lin-Bairstow iteration loop
    % initialize the counter and guesses for the
    % coefficients of quadratic factor:
    %
    % p(x) = x^2 + alfa1 * x + beta1
    %
    repeat
        % these are just guesses
        alfa1 := 2
        beta1 := 1
        repeat
            b[1] := 0
            d[1] := 0
            b[2] := 1
            d[2] := 1
            j := 2
            k := 1
            for i := 3...n2 do
                b[i] := a[i] - alfa1 * b[j] - beta1 * b[k]
                d[i] := b[i] - alfa1 * d[j] - beta1 * d[k]
                j := j + 1
                k := k + 1
            end for
            j := n
            k := n - 1
            delta1 := d[j]^2 - ( d[n+1] - b[n+1] ) * d[k]
            if delta1 = 0 then
                return false    % cannot divide by zero
            end if
            alfa2 := ( b[n+1] * d[j] - b[n1+1] * d[k] ) / delta1
            beta2 := ( b[n1+1] * d[j] - ( d[n+1] - b[n+1] ) * b[n+1] ) / delta1
            alfa1 := alfa1 + alfa2
            beta1 := beta1 + beta2
        until ( abs( alfa2 ) < TOLERANCE ) and ( abs( beta2 ) < TOLERANCE )      
        delta1 := alfa1^2 - 4 * beta1
        if delta1 < 0 then      % imaginary roots
            delta2 := sqrt( abs( delta1 ) ) / 2
            delta3 := -alfa1 / 2
            roots[count].re :=  delta3
            roots[count].im := +delta2
            roots[count+1].re :=  delta3
            roots[count+1].im := -delta2
        else                    % roots are real
            delta2 := sqrt( delta1 )
            roots[count].re := ( delta2 - alfa1 ) / 2
            roots[count].im := 0
            roots[count+1].re := ( delta2 + alfa1 ) / ( -2 )
            roots[count+1].im := 0
        end if     
        % update root counter
        count := count + 2
        % reduce polynomial order
        n  := n  - 2
        n1 := n1 - 2
        n2 := n2 - 2
        % for n >= 2 calculate coefficients of the new polynomial
        if n >= 2 then
            for i := 2...n2 do
                a[i] := b[i]
            end for
        end if
    until n < 2

    % obtain last single real root
    if n = 1 then
        roots[count].re := -b[3]
        roots[count].im := 0
    end if

    return true
    
end function

% display polynomial in readable format
procedure print_polynomial( var coeff : rvector, order : int )

    var i : int
    var dot : char := chr( 183 )

    for decreasing i := order+1...1 do
        continue when coeff[i] = 0.0 
        if i = order+1 then
            if coeff[i] ~= 1 then
                put coeff[i], dot...
            end if
            put "X^", i-1...
        elsif( i > 2 ) then
            if coeff[i] < 0 then
                put " - "...
            else
                put " + "...
            end if
            put abs( coeff[i] ), dot, "X^", i-1...
        elsif i = 2 then
            if coeff[i] < 0 then
                put " - "...
            else
                put " + "...
            end if
            put abs( coeff[i] ), dot, "X"...
        elsif i = 1 then
            if coeff[i] < 0 then
                put " - "...
            else
                put " + "...
            end if
            put abs( coeff[i] )...
        end if
    end for
    put " = 0"

end procedure

% returns the absolute value of the argument
function abs( x : real ) : real

    if x < 0.0 then
        x := -x
    end if
    return x

end function

% returns a complex number as a string
function cstr( var c : complex ) : string

    var s : string
    var re, im : real

    re := c.re
    im := c.im

    if im >= 0.0 then
        s := frealstr(re,14,9 ) & " +" & frealstr(im,12,9 ) & "j"
    else
        im := -im
        s := frealstr(re,14,9 ) & " -" & frealstr(im,12,9 ) & "j"
    end if
    return s

end function

Sample output

the polynomial:
X^4 - 16 = 0
has roots:
   2.000000000 + 0.000000000j
  -2.000000000 + 0.000000000j
   0.000000000 + 2.000000000j
   0.000000000 - 2.000000000j

the polynomial:
X^5 - 4·X^4 + 1·X^3 + 4·X^2 + 10·X - 12 = 0
has roots:
  -1.000000000 + 1.000000000j
  -1.000000000 - 1.000000000j
   2.000000000 + 0.000000000j
   1.000000000 + 0.000000000j
   3.000000000 + 0.000000000j


Copyright © 2005, Stephen R. Schmitt