Ir para o conteúdo principal

Polinômios com SymPy - Cálculos com Python

·1439 palavras·7 minutos·
Programação Sympy Python Matemática
Autor
Francisco Bustamante
Um químico trabalhando com Ciência de Dados e Programação em Python.
Tabela de conteúdos
SymPy - Este artigo faz parte de uma série de artigos.
Parte 5: Esse Artigo

Certamente um dos assuntos mais abstratos nas aulas de matemática: polinômios. Afinal, que atire a primeira variável quem nunca ficou confuso em uma divisão de polinômios, é x que não acaba mais. Neste artigo, veremos como a biblioteca SymPy, para a linguagem Python, nos ajuda a lidar com operações envolvendo polinômios.

Criando polinômios manualmente
#

Comecemos importando a biblioteca que usaremos durante o artigo:

import sympy

# configuração para outputs melhores no artigo, pode ser ignorado
sympy.init_printing(use_latex='png', scale=1.0, order='grlex',
                    forecolor='Black', backcolor='White',)

Para começar, vamos utilizar os conhecimentos mais básicos que adquirimos no primeiro artigo da série do SymPy. Vamos criar um símbolo para nossa variável e uma expressão que representará um polinômio:

x = sympy.Symbol('x')

expr = (x - 1) * (x - 2) * (x - 3)

expr

\(\displaystyle \left(x - 3\right) \left(x - 2\right) \left(x - 1\right)\)

Veja que criamos nossa expressão polinomial representando um polinômio na forma fatorada. Para obter a forma expandida, ou seja, a forma onde todas as multiplicações distributivas são feitas, utilizamos o método expand:

expr.expand()

\(\displaystyle x^{3} - 6 x^{2} + 11 x - 6\)

No caso de inicialmente ter um polinômio expandido, pode-se fatorá-lo com o método factor:

_.factor()

\(\displaystyle \left(x - 3\right) \left(x - 2\right) \left(x - 1\right)\)

A forma fatorada já deixa evidente que as raízes do polinômio são 1, 2 e 3. Mas podemos obter as raízes programaticamente utilizando o solve do SymPy:

roots = sympy.solve(expr, x)

roots

\(\displaystyle \left[ 1, \ 2, \ 3\right]\)

Checando igualdades
#

Vamos criar duas novas expressões polinomiais:

P = (x - 5) * (x + 5)
Q = x**2 - 25

Veja que P é simplesmente a forma fatorada de Q. Logo, são equivalentes e se verificarmos a igualdade entre eles o resultado será True, correto?

P == Q
False

Ué…?! Bom, já escrevemos sobre essa questão da igualdade no SymPy. A biblioteca analisa igualdade de forma e não equivalência matemática. Logo, como as formas dos dois polinômios são distintas, a comparação retorna False.

Da mesma maneira, sabemos que matematicamente a diferença entre os polinômios é zero afinal são equivalentes. Mas o retorno da seguinte comparação também é False:

P - Q == 0
False

Por que? Porque o SymPy não é proativo nas simplificações, como já vimos em outros artigos. Vejamos a representação da diferença:

P - Q

\(\displaystyle - x^{2} + \left(x - 5\right) \left(x + 5\right) + 25\)

Veja que efetivamente o SymPy armazena a expressão, sem simplificá-la. Se solicitarmos a simplificação com o método simplify, vemos que efetivamente a diferença equivale ao valor zero:

sympy.simplify(P - Q) == 0
True

E podemos ver que efetivamente um polinômio é a versão fatorada do outro com as seguintes comparações:

P.simplify() == Q
True
P == Q.factor()
True

Usando o construtor de polinômios
#

Até o momento, vimos poucas novidades quando comparado ao artigo sobre expressões com SymPy. Mas vamos mudar isso. Vejamos o tipo do objeto Q:

Q

\(\displaystyle x^{2} - 25\)

type(Q)
sympy.core.add.Add

Veja que, internamente, é um objeto da classe Add do SymPy. Ou seja, para o SymPy tal objeto é simplesmente a resultante da adição de dois outros objetos SymPy, o que está correto. Quem está dando o significado de polinômio para o objeto somos nós. Vamos entender essa última frase.

A grande vantagem de utilizar uma linguagem de alto nível como o Python é que podemos escrever código que seja mais próximo à nossa compreensão utilizando abstrações. Quem cuida de traduzir tais abstrações para a máquina é o interpretador da linguagem. Até o momento utilizamos meras expressões como representação de polinômios, mas o ideal seria efetivamente que o SymPy reconhecesse tais expressões como polinômios e não como adições. E isso é possível e veremos as vantagens dessa abordagem.

Expressões possuem o método as_poly:

Q.as_poly()

\(\displaystyle \operatorname{Poly}{\left( x^{2} - 25, x, domain=\mathbb{Z} \right)}\)

Veja o retorno! Agora o SymPy reconhece como um tipo Poly, reconhece que a variável é x e que o domínio é dos números inteiros (pode ser alterado, é apenas o padrão quando não passamos explicitamente o domínio). Vamos associar esse retorno à uma variável e confirmar o tipo:

Q_poly = Q.as_poly()

type(Q_poly)
sympy.polys.polytools.Poly

E qual a vantagem? Agora podemos solicitar o que desejamos com métodos mais próximos da semântica que utilizamos em matemática. Por exemplo, podemos solicitar os coeficientes do polinômio:

Q_poly.coeffs()

\(\displaystyle \left[ 1, \ -25\right]\)

O retorno é do maior grau em x para o menor. Porém, perceba que o retorno pode levar a enganos. Afinal, sabemos que há o termo de grau 1 em x, só o coeficiente dele que é zero. O coeffs retorna apenas coeficientes não nulos. Para evitar enganos, recomendo sempre utilizar o método all_coeffs:

Q_poly.all_coeffs()

\(\displaystyle \left[ 1, \ 0, \ -25\right]\)

Agora sim, todos os coeficientes.

Outro conceito que sempre aparece no estudo de polinômios: grau do polinômio. Agora que temos uma abstração mais próxima de nossa linguagem, podemos efetivamente solicitar o grau com o método degree:

Q_poly.degree()

\(\displaystyle 2\)

Podemos solicitar a lista de fatores do polinômio:

Q_poly.factor_list()

\(\displaystyle \left( 1, \ \left[ \left( \operatorname{Poly}{\left( x - 5, x, domain=\mathbb{Z} \right)}, \ 1\right), \ \left( \operatorname{Poly}{\left( x + 5, x, domain=\mathbb{Z} \right)}, \ 1\right)\right]\right)\)

Solicitar as raízes:

Q_poly.all_roots()

\(\displaystyle \left[ -5, \ 5\right]\)

Derivadas e integrais
#

Podemos, inclusive, fazer operações mais elaboradas. Saindo um pouco de matemática de ensino médio, é muito comum precisarmos derivar ou integrar um polinômio. Agora, tais operações se tornam triviais. Vejamos a primeira derivada de nosso polinômio:

Q_poly.diff()

\(\displaystyle \operatorname{Poly}{\left( 2 x, x, domain=\mathbb{Z} \right)}\)

Podemos obter o valor da derivada em um determinado valor de x utilizando o método subs já visto em outros artigos:

Q_poly.diff().subs({x: 2})

\(\displaystyle 4\)

Ou, mais simples, com o método eval e passando o valor de x:

Q_poly.diff().eval(2)

\(\displaystyle 4\)

Para derivadas de mais alta ordem, basta passar uma tupla com a variável e a ordem desejada:

Q_poly.diff((x, 2))

\(\displaystyle \operatorname{Poly}{\left( 2, x, domain=\mathbb{Z} \right)}\)

Q_poly.diff((x, 3))

\(\displaystyle \operatorname{Poly}{\left( 0, x, domain=\mathbb{Z} \right)}\)

Para obter a integral, usamos o método integrate:

Q_poly.integrate()

\(\displaystyle \operatorname{Poly}{\left( \frac{1}{3} x^{3} - 25 x, x, domain=\mathbb{Q} \right)}\)

Da mesma forma, podemos obter o valor da integral em determinado valor de x com o método subs ou com o método eval:

Q_poly.integrate().subs({x: 1})

\(\displaystyle - \frac{74}{3}\)

Q_poly.integrate().eval(1)

\(\displaystyle - \frac{74}{3}\)

A classe Poly e raízes complexas
#

Já vimos que há a classe Poly no SymPy. Podemos passar uma expressão diretamente para a classe ao invés de usar o método as_poly:

expr = x**3 + 2*x + 3

A = sympy.Poly(expr)
A

\(\displaystyle \operatorname{Poly}{\left( x^{3} + 2 x + 3, x, domain=\mathbb{Z} \right)}\)

type(A)
sympy.polys.polytools.Poly

Perceba que nosso polinômio A é de terceiro grau. Vejamos todas as suas raízes:

A.all_roots()

\(\displaystyle \left[ -1, \ \frac{1}{2} - \frac{\sqrt{11} i}{2}, \ \frac{1}{2} + \frac{\sqrt{11} i}{2}\right]\)

Temos raízes complexas agora. Vejamos o resultado do atributo is_real de cada raiz:

for root in A.all_roots():
    print(root.is_real)
True
False
False

Faz sentido, a primeira raiz é real, as demais, complexas.

Isso explica a existência de um método all_roots. Afinal, se há um método que se preocupa em retornar todas as raízes, é porque deve haver algum método que retorna apenas algumas, né? A depender da aplicação em questão, podemos estar interessados apenas nas raízes reais. Daí podemos usar o método real_roots:

A.real_roots()

\(\displaystyle \left[ -1\right]\)

Divisão de polinômios
#

Vamos verificar dois objetos Poly que criamos no decorrer do artigo:

A

\(\displaystyle \operatorname{Poly}{\left( x^{3} + 2 x + 3, x, domain=\mathbb{Z} \right)}\)

Q_poly

\(\displaystyle \operatorname{Poly}{\left( x^{2} - 25, x, domain=\mathbb{Z} \right)}\)

Para facilitar a compreensão vamos associar uma variável B ao Q_poly:

B = Q_poly
B

\(\displaystyle \operatorname{Poly}{\left( x^{2} - 25, x, domain=\mathbb{Z} \right)}\)

Na chamada do artigo, falamos sobre divisão de polinômios. É chegado o momento, vejamos como fazer tal operação com o SymPy.

É bem simples, chamamos o método div passando o dividendo e o divisor:

sympy.div(A, B)

\(\displaystyle \left( \operatorname{Poly}{\left( x, x, domain=\mathbb{Z} \right)}, \ \operatorname{Poly}{\left( 27 x + 3, x, domain=\mathbb{Z} \right)}\right)\)

O retorno é uma tupla com dois polinômios, o quociente e o resto. Podemos obter cada um separadamente fazendo unpack da tupla resultante:

quotient, remainder = sympy.div(A, B)

quotient

\(\displaystyle \operatorname{Poly}{\left( x, x, domain=\mathbb{Z} \right)}\)

remainder

\(\displaystyle \operatorname{Poly}{\left( 27 x + 3, x, domain=\mathbb{Z} \right)}\)

Como é uma divisão, esperamos que divisor X quociente + resto = dividendo. Vejamos:

B * quotient + remainder

\(\displaystyle \operatorname{Poly}{\left( x^{3} + 2 x + 3, x, domain=\mathbb{Z} \right)}\)

A == B * quotient + remainder
True

Simples.

Conclusão e mais artigos sobre SymPy
#

Neste artigo vimos o básico de como lidar com polinômios no SymPy e espero que tenha sido possível perceber o poder dessa biblioteca.

Até a próxima.

SymPy - Este artigo faz parte de uma série de artigos.
Parte 5: Esse Artigo

Relacionados

Resolvendo equações com SymPy
·1447 palavras·7 minutos
Programação Sympy Python Matemática
Grande parte do estudo de matemática em níveis mais fundamentais é dedicada a resoluções de equações e sistemas de equações. Neste artigo veremos como utilizar o SymPy para essas tarefas de forma rápida e intuitiva.
Introdução ao SymPy - Cálculos com Python
·2490 palavras·12 minutos
Programação Sympy Python Matemática
Neste artigo veremos uma introdução ao SymPy, um dos pacotes matemáticos mais poderosos para a linguagem Python
Gráficos com SymPy e Matplotlib
·3489 palavras·17 minutos
Programação Sympy Python Matemática Matplotlib
Neste artigo veremos como fazer gráficos com SymPy e Matplotlib. Usualmente quando se fala de SymPy focamos na parte relacionada à resolução de problemas de cálculos matemáticos. Mas muitos esquecem que a biblioteca possui alguns recursos básicos para construção de gráficos, que veremos neste artigo.