Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Raízes de polinômios com o método de Müller

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

p
Loading...

Imprimindo a derivada simbólica do polinômio implementada pelo usuário

dp
Loading...

Imprimindo a derivada simbólica do polinômio pela função residente diff

dp2
Loading...

Verificando igualdade

dp == dp2
True

Calculando 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)
rc
Loading...

Calcula apenas as raízes reais de pn

rr = sy.roots(pn,x,multiple=True,filter='R')
rr
Loading...

Avaliando 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)
vi
array([ 4. , 3.5, 2.2, -1. ])

Agora, vamos avaliar o polinômio em x=πx=\pi

xi = np.pi
np.polyval(vi,xi)
Loading...

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)
Loading...

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 1x1-1 \le x \le 1

# 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)$');
<Figure size 432x288 with 1 Axes>

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 += 1

Exemplos

Exemplo. Determinando raízes para o polinômio P(x)=4x3+3.5x2+2.2x1P(x) = 4x^3 + 3.5x^2 + 2.2x - 1 com estimativas iniciais x0=0.5x_0 = 0.5 x1=1.0x_1 = 1.0 e x2=1.5x_2 = 1.5, ϵ=105\epsilon = 10^{-5} e N=100N = 100. Notemos que o segundo argumento da função desempenha o papel de x1x_1 e o terceiro argumento opera como um “raio” de comprimento dx=0.5dx = 0.5 que fará com que x0=x1dxx_0 = x_1 - dx e x2=x1+dxx_2 = x_1 + dx. 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='--');
<Figure size 432x288 with 1 Axes>

Exemplo. Determinando raízes para o polinômio P(x)=x43x3+x2+x+1P(x) = x^4 - 3x^3 + x^2 + x + 1 com estimativas iniciais x0=0.5x_0 = -0.5 x1=0.0x_1 = 0.0 e x2=0.5x_2 = 0.5, ϵ=105\epsilon = 10^{-5} e N=100N = 100.

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: x0=0.5x_0 = 0.5 x1=1.0x_1 = 1.0 e x2=1.5x_2 = 1.5

  • Caso 2: x0=1.5x_0 = 1.5 x1=2.0x_1 = 2.0 e x2=2.5x_2 = 2.5

# 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 [1,2.8][-1,2.8].

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();
<Figure size 432x288 with 1 Axes>

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