Escolar Documentos
Profissional Documentos
Cultura Documentos
Renato Vicente
3 de março de 2011
Resumo
Os códigos neste resumo foram extraı́dos e adaptados de [1].
1 Problema
Encontre as soluções reais de f (x) = 0, onde a função f é dada.
#coding: utf-8
## modulo buscaraiz
’’’ x1,x2 = buscaraiz(f,a,b,dx).
Busca a menor raiz de f(x) no intervalo (x1,x2)
no intervalo (a,b) em incrementos dx.
Returna x1 = x2 = None caso nenhuma raiz seja
encontrada.
’’’
def buscaraiz(f,a,b,dx):
1
x1 = a; f1 = f(a)
x2 = a + dx; f2 = f(x2) #intervalo inicial
while f1*f2 > 0.: #procura intervalo com troca de sinal
if x1 >= b: return None,None
x1 = x2; f1 = f2
x2 = x1 + dx; f2 = f(x2) #novo intervalo
else:
return x1,x2 # se houver troca de sinal retorna
>>> hold(True)
>>> plot(x,y)
>>> show()
>>> plot(x,y)
>>> grid(True)
3 Dicotomia
Localizado um intervalo contendo uma raiz, um dos métodos para aproximar
o valor dessa raiz é a dicotomia (ou bisecção). Na dicotomia os intervalos
com extremos mudando de sinal são sucessivamente reduzidos pela metade.
Em cada passo o valor da raiz é aproximada pela mediana x3 = (x1 + x2 )/2
do intervalo atual x2 − x1 . O procedimento é repetido até que o tamanho do
intevalo atinja um tamanho pré-especificado | x2 −x1 |≤ , que fixa a precisão
no conhecimento da raiz em ±. O número de dicotomias necessárias para
atingirmos a precisão requerida pode ser calculado observando que após n
2
passos a amplitude do intervalo original ∆x é reduzida a ∆x/2n . Dessa
maneira temos:
log(| ∆x | /)
n= (1)
log 2
Na implementação que se segue precisamos também de um módulo para
tratamento de mensagens de erro:
#coding: utf-8
## module erros
’’’ err(string).
Imprime ’string’ e termina um programa.
’’’
import sys
def err(string):
print string
raw_input(’Press return to exit’)
sys.exit()
A implementação a seguir apenas aceita intervalos monotônicos:
#coding: utf-8
## module dicotomia
’’’ raiz = dicomia(f,x1,x2,chave=0,tol=1.0e-9).
Encontra a raiz de f(x) = 0 por dicotomia.
A raiz tem que estar no intervalo (x1,x2).
Escolhendo chave = 1 returna raiz = None se
f(x) aumentar como resultado da dicotomia.
’’’
from math import log,ceil
import erros
def dicotomia(f,x1,x2,chave=0,epsilon=1.0e-9):
# Os valores padr~
ao s~
ao chave desligada e epsilon 1e-09
# Se o intevalo n~
ao atende a condiç~
ao suficiente
# para exist^
encia de raiz retorna mensagem de erro
3
if f1*f2 > 0.: erros.err(’Pode n~
ao haver raiz no intervalo’)
# Se a funç~
ao n~
ao for monot^
onica no intervalo
# retorna ’None’
if (chave == 1) and (abs(f3) > abs(f1)) \
and (abs(f3) > abs(f2)):
return None
4
>>> raiz = dicotomia(f,x1,x2)
>>> raiz
4 Método Iterativo
Se encontrarmos uam função φ tal que uma sequência gerada pelo mapa
xk+1 = φ(xk ) convirja para um x∗ que seja solução de f (x) = 0, teremos um
método iterativo para achar raizes de equações. Uma condição necessária é
que x∗ seja um ponto fixo de φ. Ou seja, que φ(x∗ ) = x∗ . Essa condição,
no entanto, não é suficiente, pois para que haja convergência do método é
necessário adicionalmente que o ponto fixo encontrado seja estável.
Funções φ candidatas naturais têm a forma φ(x) = x + α(x)f (x). A esta-
bilidade do ponto fixo de φ pode ser analisada observando o comportamento
do mapa ao redor do ponto fixo x∗ :
xk+1 = φ(xk ) = φ(x∗ + k ) = x∗ + k+1 (2)
Aqui indica uma pequena perturbação. Prosseguimos utilizando uma ex-
pansão de Taylor em torno do ponto fixo:
dφ ∗
x∗ + k+1 ≈ x∗ + (x )k (3)
dx
A partir disso notamos que a condição para que uma perturbação seja re-
duzida pelo mapa, restabelecendo o ponto fixo é
dφ ∗
| (x ) |< 1 (4)
dx
Infelizmente não sabemos x∗ , mas temos um critério para escolha da função
α(x). Comecemos pelo caso mais simples com α constante:
−1 < 1 + αf 0 (x∗ ) < 1, (5)
ou seja,
2
0<α<− , (6)
f 0 (x∗ )
Estamos livres para escolher qualquer α nesse intervalo. Em particular,
podemos fazer φ(x∗ ) = 0 e assim terı́amos
1
α=− , (7)
f 0 (x∗ )
Se conhecessemos f 0 (x∗ ) simplesmente usarı́amos o mapa
f (x)
φ(x) = x − . (8)
f 0 (x∗ )
5
5 Método Newton-Raphson
O método Newton-Raphson baseia-se na construção de um mapa como na
seção anterior. Uma forma rápida de introduzirmos o método é imaginando
que temos um chute inicial a para a raiz. Adicionalmente, supomos que o
chute está próximo da raiz que desejamos obter x∗ . Neste caso terı́amos a
seguinte aproximação:
Como não estamos necessariamente tão próximos da raiz o que temos é uma
sequência de “chutes” definida pelo mapa:
f (xk )
xk+1 = xk − (11)
f 0 (xk )
2 00
f (xk ) = −k f 0 (xk ) − f (xk ) (13)
2
Substituindo (13) em (12) obtemos:
2k f 00 (xk )
k+1 = − (14)
2f 0 (xk )
Se a razão f 00 (xk )/f 0 (xk ) não oscilar muito em torno da raiz, podemos
dizer que o erro é proporcional ao erro anterior ao quadrado. Se o erro é
pequeno inicialmente ele ficará rapidamente muito menor. A convergência
é quadrática se o erro inicial não for muito grande. O método converge
rapidamente quando o chute inicial é bastante próximo da raiz, mas tem
convergência global ruim. Uma implementação segura combinando Newton-
Raphson e dicotomia segue abaixo:
6
#coding: utf-8
## module newtonRaphson
’’’ raiz,niter = newtonRaphson(f,df,a,b,tol=1.0e-9).
Encontra uma raiz de f(x) = 0 combinando o método Newton-Raphson
e o método de dicotomia. A raiz tem que estar localizada em (a,b).
É necessário fornecer f(x) e sua derivada df(x).
’’’
def newtonRaphson(f,df,a,b,tol=1.0e-9):
import erros
# se a condiç~
ao suficiente n~
ao for verificada para
if fa*fb > 0.: erros.err(’Pode n~ao haver raiz no intervalo.’)
#inicia iteraç~
oes na mediana do intevalo
x = .5*(a + b)
# no máximo 30 iteraç~
oes
for i in range(30):
fx = f(x)
# Passo de Newton-Raphson
dfx = df(x)
7
# fora dos limites iniciais
try: dx = -fx/dfx
except ZeroDivisionError: dx = b - a
x = x + dx
# Se o resultado da aplicaç~
ao de N-R estiver fora
# do intevalo atual use dicotomia
if (b - x)*(x - a) < 0.:
dx = .5*(b-a)
x = a + dx
# Verifique converg^
encia
if abs(dx) < tol*max(abs(b),1.): return x,i
No shell iterativo:
6 Método de Brent
O método Newton-Raphson requer o uso de derivadas da função cujo zero
queremos encontrar. Uma alternativa interessante na ausência de derivadas
que converge mais rapidamente do que o método de dicotomia é o método de
Brent. Este método consiste da utilização de uma combinação da dicotomia
8
com interpolações quadráticas inversas para gerar candidatos a raiz. O pro-
cedimento inicia-se tomando um intervalo (x1 , x2 ) com a condição suficiente
para existência de raiz f (x1 )f (x2 ) < 0. Elege-se a mediana x3 = (x1 + x2 )/2
do intervalo como candidato inicial a raiz. Conferindo a condição suficiente
escolhe-se um dos subintervalos como candidato à conter uma raiz. Assim se
f (x1 )f (x3 ) < 0 o intervalo escolhido é o da esquerda, caso contrário, o da di-
reita. Utiliza-se um interpolante de Lagrange de três pontos (será visto mais
a frente no curso) para gerar uma aproximação da curva. A raiz da aprox-
imação dentro do intervalo candidato será o novo candidato a raiz tomando
o papel de x3 , o antigo candidato passa a ser um dos extremos do novo in-
tervalo. O procedimento de escolha de subintervalo e interpolação é repetido
até que a correção de um candidato ao próximo seja menor que uma precisão
pré-estabelecida.
A interpolação reversa consiste em construir uma curva quadrática que
passe pelos três pontos (x1 , f (x1 )), (x2 , f (x2 )) e (x3 , f (x3 )). Isso pode con-
struido escrevendo-se:
(f − f2 )(f − f3 ) (f − f1 )(f − f3 ) (f − f1 )(f − f2 )
x(f ) = x1 + x2 + x3
(f1 − f2 )(f1 − f3 ) (f2 − f1 )(f2 − f3 ) (f3 − f1 )(f3 − f1 )
(15)
A raiz dessa curva aproximada estará situada em:
f2 f3 x1 (f2 − f3 ) + f3 f1 x2 (f3 − f1 ) + f1 f2 x3 (f1 − f2 )
x=− (16)
(f1 − f2 )(f2 − f3 )(f3 − f1 )
A correção ao candidato à raiz a cada passo de interpolação será:
x3 (f1 − f2 )(f2 − f3 + f1 ) + f2 x1 (f2 − f3 ) + f1 x2 (f3 − f1 )
∆x = x − x3 = f3
(f2 − f1 )(f2 − f3 )(f3 − f1 )
(17)
Quando essa correção for | ∆x |< para uma precisão pré-definida paramos
o algoritmo. Uma implementação simplificada do método de Brent segue:
#coding: utf-8
## module brent
’’’raiz,numiter = brent(f,a,b,tol=1.0e-9).
Encontra a raiz de f(x) = 0 combinando interpolaç~ ao
quadrática com bisecç~
ao (Método de Brent simplificado)
A raiz tem que estar no intervalo especificado (a,b).
É necessário fornecer a funç~
ao f(x).
’’’
import erros
9
def brent(f,a,b,tol=1.0e-9):
#verifica condiç~
ao suficiente
if f1*f2 > 0.0: erros.err(’Pode n~
ao haver raiz no intervalo’)
# Faz a interpolaç~
ao quadrática inversa
# para encontrar o novo candidato
denom = (f2 - f1)*(f3 - f1)*(f2 - f3)
numer = x3*(f1 - f2)*(f2 - f3 + f1)\
+ f2*x1*(f2 - f3) + f1*x2*(f3 - f1)
# Se houver divis~
ao por zero
# tira x do intervalo (a,b)
try: dx = f3*numer/denom
except ZeroDivisionError: dx = b - a
10
x = x3 + dx
# Se a iterpolaç~
ao sair do intervalo
# usar dicotomia
if (b - x)*(x - a) < 0.0:
dx = 0.5*(b - a)
x = a + dx
No shell iterativo:
>>> from pylab import *
>>> from math import *
>>> f = lambda x:x*abs(cos(x)) - 1.0
>>> x=arange(0.,4.,0.01)
>>> y=f(x)
>>>plot(x,y)
>>>show()
>>>plot(x,y)
>>>grid(True)
>>>from brent import *
>>>raiz,nint=brent(f,0.,4.)
>>>raiz
>>>nint
Compare o número de iterações necessárias usando o método de Brent
com aquele necessário utilizando dicotomia.
Referências
[1] J. Kiusalaas, Numerical Methods in Engineering with Python, Cambridge
University Press, 2005.
11