1.3.3 Raízes de Equações — Raízes de Polinômios — O Método de Bairstow
1. Introdução
O Método de Bairstow consiste em dividir um polinômio em fatores isolados que permitam aproximar suas raízes.
Possui a grande vantagem de conseguir extrair todas as raízes de um polinômio arbitrário sem a necessidade de percorrer todo o espaço de busca,como fariam o métodos de Muller ou Newton Raphson, que sempre convergem à aproximação mais próxima.
Além disso, ainda possui o benefício de fornecer as raízes complexas à uma taxa de convergência quadrática utilizando-se apenas de aritmética real.
2. Desenvolvimento do Método
Seja a função um polinômio geral:
(eq1)
Se dividirmos o polinômio 1 pelo fator
(eq2)
Caso esta divisão não gere resto, então
(eq3)
e
(eq4)
onde
O Método de Bairstow segue um procedimento semelhante ao descrito acima, contudo, ao invés de dividir por um fator
, ele divide o polinômio pelo fator
. A introdução deste fator quadrático serve para permitir a determinação de raízes complexas, enquanto que o fator linear permitiria apenas aproximações reais.
Ao dividirmos o polinômio pelo fator de segunda ordem, obtemos:
(eq5)
isso gera uma expressão para o resto que é:
(eq6)
Aplicando o algoritmo de Briot-Ruffine com como divisor, obtemos a seguinte relação de recorrência:
(eq7)
(eq8)
(eq9)
onde
Com isso, o Método de Bairstow se resume a determinar os valores de e
que tornam o fator quadrático
exato. Ou seja, ele utiliza uma estratégia para encontrar os valores de
e
para qual
é zero. Quando
,
será uma raiz do polinômio.
Da equação do resto (Equação 6) é possível notar que quando é nulo,
e
são nulos. A técnica usada por Bairstow segue o desenvolvimento do Método de Newton, expandindo
e
em expressões que os aproximem de zero. Como
e
são funções de
e
, é possível expandir
por Série de Taylor para duas variáveis. Bairstow utiliza uma expansão de até a primeira ordem, gerando:
Como e
serão nulos quando
, então é possível reescrever as equações acimas como:
(eq12)
A partir das duas últimas equações, podemos obter os valores de e
. O problema agora está nas derivadas parciais de
em relação a
e
. Se elas forem determinadas o problema é reduzido à um sistema de duas equações lineares com apenas duas incógnitas:
e
.
Tomando as derivadas como constantes, renomeamos elas em termos de um vetor , tal que:
(eq14)
A partir das suposições acimas, o sistema de equações pode ser reescrito com suas derivadas parciais substituídas pela variável , tal que
(eq18)
O Método de Bairstow utiliza este sistema de equações lineares para ajustar variáveis complexas da mesma forma que as reais. As derivadas parciais, obtidas pela divisão sintética de , são:
(eq20)
portanto, generalizando para todos termos,
(eq22)
onde
Com este conjunto de equações é possível determinar e
, necessários para as aproximações de
e
, sendo o processo de cálculo do erro relativo de cada iteração é obtido pelas expressões:
(eq23)
e
(eq24)
Quando ambos erros estiverem abaixo do máximo tolerado, e
estarão aproximados de forma que as raízes
serão determinadas por
- Pela expressão abaixo, caso o polinômio que deseja-se encontrar a raiz seja quadrático:
(eq25)
- Pela expressão abaixo, caso o polinômio seja de grau um:
(eq26)
- Caso o polinômio seja de grau maior ou igual a três, o Método de Bairstow será aplicado ao quociente para o cálculo de novos valores
e
usando a função abaixo. Os valores de
e
calculados anteriormente servem como parâmetro inicial para a próxima iteração.
3. Implementação
Implementação em Python:
-
def bairstow(a, rr=1, ss=1, errto=0.001, imax=500):
-
"""
-
Calculate the roots of a function (using the Bairstow Method)
-
-
bairstow(a, polDegree, errto, rr, ss, imax)
-
-
* a[]: list with the coeficients
-
* rr: initial aprox. of "r" param
-
* ss: initial aprox. of "s" param
-
* errto: tolerated error
-
* imax: max iterCountation allowed (not inf-loop)
-
-
return: a list with all roots of polynom
-
-
Author: Pedro Garcia [sawp@sawp.com.br]
-
see: http://www.sawp.com.br
-
-
License: Creative Commons
-
http://creativecommons.org/licenses/by-nc-nd/2.5/br/
-
-
Jan 2010
-
"""
-
-
def filt(l):
-
r = l.real
-
i = l.imag
-
if abs(r) < errto:
-
r = 0.0
-
if abs(i) < errto:
-
i = 0.0
-
return complex(r, i)
-
-
r = rr
-
s = ss
-
n = len(a) – 1
-
-
ea1 = errto + 1
-
ea2 = errto + 1
-
iterCount = 0
-
roots = [ complex(0,0) for i in xrange(len(a)) ]
-
b = [ complex(0,0) for i in xrange(len(a)) ]
-
c = [ complex(0,0) for i in xrange(len(a)) ]
-
-
while n >= 3 or iterCount < imax:
-
iterCount = 0
-
-
while ea1 >= errto and ea2 >= errto or iterCount < imax:
-
iterCount += 1
-
b[n] = a[n]
-
b[n-1] = a[n-1] + r * b[n]
-
c[n] = b[n]
-
c[n-1] = b[n-1] + r * c[n]
-
-
for i in xrange(n-2, -1, -1):
-
b[i] = a[i] + r * b[i+1] + s * b[i+2]
-
c[i] = b[i] + r * c[i+1] + s * c[i+2]
-
-
det = c[2]*c[2] – c[3]*c[1]
-
-
if det != 0.0:
-
dr = (-b[1]*c[2] + b[0]*c[3])/det
-
ds = (-b[0]*c[2] + b[1]*c[1])/det
-
r = r + dr
-
s = s + ds
-
-
if r != 0.0:
-
ea1 = abs(dr/r)
-
-
if s != 0.0:
-
ea2 = abs(ds/s)
-
else:
-
r += errto
-
s += errto
-
iterCount = 0
-
-
(r1, i1, r2, i2) = quadratic_solve(r, s)
-
roots[n] = complex(r1, i1)
-
roots[n-1] = complex(r2, i2)
-
n = n – 2
-
-
for i in xrange(n):
-
a[i] = b[i+2]
-
-
if n == 2:
-
r = -a[1]/a[2]
-
s = -a[0]/a[2]
-
(r1, i1, r2, i2) = quadratic_solve(r, s)
-
roots[n] = complex(r1, i1)
-
roots[n-1] = complex(r2, i2)
-
else:
-
roots[n] = complex(-a[0]/a[1], 0.0)
-
print iterCount
-
roots[1:len(roots)].sort(key=lambda r: r.real)
-
return map(filt, roots[1:len(roots)])
Note que a função quadratic\_solve foi incluída para deixar explícita a resolução da fórmula quadrática. A implementação desta função é:
-
def quadratic_solve(r, s):
-
"""
-
Calculate the roots of a parabolic equation: a*x^2 + b*x + c = 0 (*)
-
-
quadratic_solve(r, s)
-
-
* r: b coeficient of (*)
-
* s: a*c, coeficients of (*)
-
-
return: a tuple with roots of (*)
-
-
Author: Pedro Garcia [sawp@sawp.com.br]
-
see: http:!www.sawp.com.br
-
-
License: Creative Commons
-
http://creativecommons.org/licenses/by-nc-nd/2.5/br/
-
-
Jan 2009
-
"""
-
delta = r*r + 4.0*s
-
-
if delta > 0:
-
r1 = (r + sqrt(delta))/2.0
-
r2 = (r - sqrt(delta))/2.0
-
i1 = 0.0
-
i2 = 0.0
-
else:
-
r1 = r/2.0
-
r2 = r1
-
i1 = sqrt(abs(delta))/2.0
-
i2 = -i1
-
-
return (r1, i1, r2, i2)
Observe que o programa altera os valores de e
com algum incremento aleatório e repete o procedimento da resolução das equações quando a determinante é nula, forçando o programa a convergir à uma aproximação melhor.
Um exemplo da utilização desta sub-rotina pode ser encontrado em: http://www.sawp.com.br/code/rootfind/bairstow.py
A utilização desta função não é tão simples, pois exige como parâmetro de entrada um vetor com os coeficientes do polinômio. Embora a utilização de listas para representar um polinômio seja algo comum para muitos programadores, certamente é uma abordagem menos intuitiva que passar uma função como referência.
Também há disponível uma outra implementação deste algoritmo em Fortran 90 no endereço http://www.sawp.com.br/code/rootfind/bairstow.f90
4. Copyright
Este documento é disponível sob a licença Creative Commons. As regras dos direitos de cópia deste conteúdo estão acessíveis em http://creativecommons.org/licenses/by-nc-nd/2.5/br/.
Você pode ler as eventuais respostas desta entrada
através do RSS 2.0 feed.
As respostas estão encerradas, mas você pode
trackback
a partir do seu próprio site.