Computação com polinômios¶
Como exemplo, vamos implementar a forma aninhada de um polinômio de grau 3 (também conhecida como forma de Hörner)
import sympy as sy
import numpy as np
import matplotlib.pyplot as plt
sy.init_printing()
# escreve a forma aninhada de um polinomio de grau n
# grau do polinomio
n = 3
# variavel independente
x = sy.Symbol('x');
# coeficientes do polinomio
a = [ sy.Symbol('a'+ str(i)) \
for i in range(0,n+1) ]
# forma aninhada simbolica
p, dp = 0, 0
for j in range(n,-1,-1):
dp = dp*x + p
p = a[j] + p*x
# determinacao de derivada de modo simbolico
dp2 = sy.diff(p,x)Imprimindo o polinômio simbólico
pImprimindo a derivada simbólica do polinômio implementada pelo usuário
dpImprimindo a derivada simbólica do polinômio pela função residente diff
dp2Verificando igualdade
dp == dp2TrueCalculando raízes de polinômios¶
# define valores dos coeficientes aj para o polinômio
# na ordem a0 + a1x + a2x**2 + ...
v = [-1, 2.2,3.5,4]
# escreve o polinômio
pn = p.subs(dict(zip(a,v)))
print(pn)x*(x*(4*x + 3.5) + 2.2) - 1
Calcula todas as raízes do polinômio pn
rc = sy.roots(pn,x,multiple=True)
rcCalcula apenas as raízes reais de pn
rr = sy.roots(pn,x,multiple=True,filter='R')
rrAvaliando polinômios¶
Podemos avaliar polinômios usando a função polyval do Numpy. Entretanto, como ela recebe coeficientes do maior para o menor grau, para mantermos a consistência com nosso polinômio anterior, devemos converter a lista v para um objeto array e fazer uma inversão (flip).
vi = np.flip(np.asarray(v),axis=0)
viarray([ 4. , 3.5, 2.2, -1. ])Agora, vamos avaliar o polinômio em
xi = np.pi
np.polyval(vi,xi)Note que se avaliássemos o polinômio em um ponto arbitrário, a forma impressa é idêntica àquela que obtivemos anteriormente.
np.polyval(vi,x)Agora vamos plotar o polinômio. Antes, vamos converter nosso polinômio para uma função a fim de a avaliarmos em um intervalo. Vamos escolher o intervalo
# converte para função
f = sy.lambdify(x,pn)
# intervalo
xf = np.linspace(-1,1,num=20,endpoint=True)
# plotagem do polinômio com destaque para a raiz real
plt.axhline(y=f(rr[0]),c='r',ls='--')
plt.axvline(x=rr[0],c='r',ls='--')
plt.plot(xf,f(xf))
plt.plot(rr[0],f(rr[0]),'ro')
plt.xlabel('$x$')
plt.ylabel('$P(x)$');
Implementação: Método de Müller¶
# \TODO caso complexo (verificar aritmética)
def metodo_muller(f,x0,dx,EPS,N):
"""
Busca aproximação para raiz da função f
pelo método de Muller.
ENTRADA:
f: função; ex. f = lambda x: x^3 + 2*x
x0: estimativa inicial
h: incremento (produz valores vizinhos)
EPS: erro
N: iterações
SAÍDA:
x: aproximação de raiz de f
"""
if N < 3:
raise("N deve ser maior do que 3")
# escolhendo os dois pontos adicionais
# na vizinhança de x0 para ter as 3
# estimativas iniciais
x1 = x0 - dx
x2 = x0 + dx
h0 = x1 - x0
h1 = x2 - x1
d0 = (f(x1) - f(x0))/h0
d1 = (f(x2) - f(x1))/h1
d = (d1 - d0)/(h1 + h0)
i = 3
while i <= N:
b = d1 + h1*d
# discriminante
D = (b**2 - 4*f(x2)*d)**0.5
# Verificando o denominador:
# Esta condição irá definir o maior denominador
# haja vista que b + sgn(b)D.
# (critério de sgn(b))
if abs(b - D) < abs(b + D):
E = b + D
else:
E = b - D
h = -2*f(x2)/E
x = x2 + h
if abs(h) < EPS:
return x
# atualização
x0 = x1
x1 = x2
x2 = x
h0 = x1 - x0
h1 = x2 - x1
d0 = (f(x1) - f(x0))/h0
d1 = (f(x2) - f(x1))/h1
d = (d1 - d0)/(h1 + h0)
i += 1Exemplos¶
Exemplo. Determinando raízes para o polinômio com estimativas iniciais e , e . Notemos que o segundo argumento da função desempenha o papel de e o terceiro argumento opera como um “raio” de comprimento que fará com que e . Isto decorre de como o a função foi programada. Veja o código anterior.
f = lambda x: 4*x**3 + 3.5*x**2 + 2.2*x - 1
x0 = metodo_muller(f,1.0,0.5,1e-5,100)
X = np.linspace(0.2,1.7,100)
plt.plot(X,f(X))
plt.plot(X,0*f(X))
plt.axvline(x=x0.real,c='r',ls='--');
Exemplo. Determinando raízes para o polinômio com estimativas iniciais e , e .
f2 = lambda x: x**4 - 3*x**3 + x**2 + x + 1
metodo_muller(f2,-0.5,0.5,1e-5,100)(-0.3390928377617365-0.4466300999972928j)Com essas estimativas a raiz é um número complexo. Escolhamos agora estimativas diferentes:
Caso 1: e
Caso 2: e
# caso 1
c1 = metodo_muller(f2,1.5,0.5,1e-5,100)
print(c1)1.3893906833348133
# caso 2
c2 = metodo_muller(f2,2.0,0.5,1e-5,100)
print(c2)2.2887949921884836
Por que há resultados diferentes? Vamos verificar o gráfico deste polinômio no domínio .
from matplotlib.pyplot import plot,legend
from numpy import linspace, where, logical_and
x = linspace(-1,2.8,50)
plot(x,f2(x))
plot(x,0*f2(x),':')
xi = where( logical_and(x >= -0.5,x <= 0.5) )
xi = x[xi]
plot(xi,0*xi,'-g',label='intervalo onde a raiz é complexa')
plot(c1,0,'or',c2,0,'or',label='raiz real')
legend();

Na primeira escolha de estimativas iniciais, obtivemos uma raiz complexa porque no intervalo , o polinômio não intersecta o eixo . Nos outros dois casos, temos as duas raízes reais do polinômio.