Cálculo com Python e SymPy – limite

cálculo limite sympy

O conceito de limite é o mais importante dentro do estudo de cálculo. Diversos outros conceitos e ideias se baseiam na definição de limite.

Neste artigo, veremos como podemos achar o limite de funções usando o SymPy e como este pacote pode ser um auxílio didático no estudo de cálculo.

Antes de entrar na parte de limite em si, vamos ver como o pacote lida com a representação de infinito, já que é recorrente termos limite tendendo ao infinito ou infinito como resposta de exercícios de cálculo.

Como já vimos desde o primeiro artigo dessa série sobre o SymPy, este é um sistema algébrico computacional simbólico. Logo, precisamos saber qual a representação interna do conceito de infinito:

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

from sympy import oo

type(oo)
sympy.core.numbers.Infinity

Sim, são duas letras “o” minúsculas 🙂

Podemos fazer algumas operações simples para verificar se os resultados apresentados são os que esperamos com base em nosso conhecimento matemático. Vejamos:

oo + 1
1000 < oo
1 / oo

Como vemos nos três últimos exemplos, os resultados são coerentes com os esperados matematicamente. Assim, de forma muito simples, temos a representação de um conceito que não é trivial.

Vejamos, agora, a parte de limite.

A função limit

A definição de limite é um tanto abstrata, ao menos num primeiro olhar:

A função f aproximar-se de um limite l próximo a a significa: para cada \epsilon > 0 há algum \delta > 0 tal que, para todo x se 0 < |x-a| < \delta, então |f(x) - l| < \epsilon.

É… um belo emaranhado de símbolos. E que é o ponto de partida de diversos conceitos de cálculo. Mas, para este artigo e muitas discussões, podemos partir de uma definição mais simplificada e intuitiva de limite:

A função f aproxima-se de um limite l próximo a a se pudermos fazer f(x) tão próximo quanto desejado de l tomando x suficientemente próximo de, mas não igual a, a.

Ainda pode soar abstrato, mas veremos diversos exemplos que devem facilitar a compreensão.

Comecemos com um dos limites mais conhecidos, uma das diversas formas de representar o número de Euler:

e \equiv \lim_{x \to \infty} \left( 1 + \frac{1}{x} \right)^x

Vamos verificar se o SymPy retorna e para o limite em questão:

from sympy import limit, Symbol

x = Symbol('x')

limit( (1 + 1 / x)**x, x, oo)

Como vemos, foi obtido a representação do número de Euler como resultado. Para entender melhor, vejamos o gráfico a seguir:

from sympy.plotting import plot
from sympy import E
import matplotlib.pyplot as plt


def move_sympy_plot_to_axes(sympy_plot, plt_ax):
    """Moves a SymPy plot to a Matplotlib axes

    Adapted from: https://stackoverflow.com/a/46813804/8706250

    Parameters
    ----------
    sympy_plot : SymPy plot
    plt_ax : Matplotlib axes
    """

    backend = sympy_plot.backend(sympy_plot)
    backend.ax = plt_ax
    backend._process_series(backend.parent._series, plt_ax, backend.parent)
    backend.ax.spines['right'].set_color('none')
    backend.ax.spines['bottom'].set_position('zero')
    backend.ax.spines['top'].set_color('none')
    plt.close(backend.fig)


fig, ax = plt.subplots(figsize=(8, 6))

p1 = plot((1 + 1/x)**x, (x, 0, 50), show=False,
          label=r'$\left( 1 + \frac{1}{x} \right)^x$')
p2 = plot(E, (x, 0, 50), show=False, label='Número de Euler')

move_sympy_plot_to_axes(p1, ax)
move_sympy_plot_to_axes(p2, ax)

ax.get_lines()[1].set(color='red', linestyle='--')
ax.set_ylim(0, 3)
ax.legend(loc='lower right')

plt.show()

Veja no gráfico como a curva azul se aproxima da linha tracejada vermelha, que representa o número de Euler, à medida que x se torna cada vez maior. Ou seja, no limite em que x tende ao infinito, a função em questão se aproxima do valor do número de Euler.

Limites laterais

Vejamos o gráfico da seguinte função f:

f(x) = \frac{1}{x}
from sympy.plotting import plot

x = Symbol('x')
f = 1 / x

plot(f, (x, -10, 10), ylim=(-10, 10));

Como vemos, a forma como nos aproximamos de x=0 afeta o limite. Este é um caso de limites laterais. Se nos aproximarmos de zero pela direita, usualmente representado pelo símbolo +, teremos um limite. Aproximando-se pela esquerda, representado pelo símbolo , teremos outro valor para o limite. No SymPy, podemos indicar o sentido da aproximação pelo parâmetro dir (de direction) da função limit:

limit(f, x, 0, dir='+')
limit(f, x, 0, dir='-')

Só é necessário indicar tal parâmetro em casos ambíguos. Nos limites tendendo ao infinito (negativo e positivo), não é necessário como vemos no gráfico:

limit(f, x, -oo), limit(f, x, oo)

Duas funções oscilantes

Vejamos mais alguns exemplos que costumam aparecer em livros de cálculo. Começando pela função g a seguir:

g(x) = \frac{\sin(x)}{x}
from sympy import sin, exp

g = sin(x) / x

plot(g, (x, -30, 30));

Vejamos dois limites para a função:

limit(g, x, 0)
limit(g, x, oo)

O limite quando x tende a zero ter valor 1 é bem evidente pelo gráfico. O limite quando x tende ao infinito fica mais evidente quando aumentamos um pouco o intervalo do eixo x, motivo pelo qual especifiquei no código o intervalo de -30 a 30. Veja como a função oscila entre valores positivos e negativos, mas a amplitude da oscilação fica cada vez menor à medida que os valores de x se tornam mais positivos ou mais negativos. Assim, no limite de x tendendo ao infinito, seja este positivo ou negativo, a função se aproxima de zero.

A função h definida a seguir possui análise similar:

h(x) = \frac{\sin(x)^2}{x}
h = sin(x)**2 / x

plot(h, (x, -30, 30));
limit(h, x, 0)
limit(h, x, oo)

O limite quando x tende a zero é evidente no gráfico. O limite quando x tende ao infinito tem análise similar à função g anterior. A diferença é que a amplitude diminui durante a oscilação tendendo ao valor zero, mas não muda o sinal da função.

Qual função domina?

Vejamos a função j definida a seguir:

j(x) = \frac{\exp(x)}{x^{100}}

Comecemos com o gráfico:

# apenas para desligar o aviso de erro numérico
import warnings
warnings.filterwarnings('ignore', category=RuntimeWarning)

j = exp(x) / (x**100)

plot(j);

Veja como a escala vertical do gráfico se estende até valores da ordem de 10^{138}. Isto dificulta a análise e a compreensão dos valores de limite que queremos avaliar. Vamos fazer alguns gráficos com escalas mais adequadas. Comecemos restringindo os limites de x e y:

plot(j, (x, -2, 2), ylim=(0, 10));

Agora temos uma noção um pouco melhor do comportamento do gráfico. Vemos que nas proximidades de x = \pm 1 a função cresce muito rapidamente. Vamos verificar o resultado dos quatro seguintes limites, comparando com o gráfico apresentado:

limit(j, x, 0)
limit(j, x, 1)
limit(j, x, -oo)
limit(j, x, oo)

Os três primeiros limites são evidentes pelo gráfico e pela expressão da função, concorda? Nas proximidades de zero, a função tende ao infinito; nas proximidades de 1 a função tende a e (substitua 1 na função caso tenha dificuldade de ver pelo gráfico); e, quando x tende ao infinito negativo, a função tende a zero, coerente com o gráfico mostrando a curva próxima ao eixo x. O que parece estranho é o último limite, quando x tende ao infinito positivo. Afinal, o gráfico mostra a curva rente ao eixo x, levando a crer que o resultado também deveria ser zero.

Para entender, comecemos fazendo o gráfico das funções do numerador e do denominador:

h_numerator = exp(x)
plot_h_numerator = plot(h_numerator, (x, -2, 2),
                        ylim=(0, 100), show=False, label='Numerador')
h_denominator = x**100
plot_h_denominator = plot(h_denominator, (x, -2, 2),
                          ylim=(0, 100), show=False, label='Denominador')

fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(8, 6))

move_sympy_plot_to_axes(plot_h_numerator, ax1)
move_sympy_plot_to_axes(plot_h_denominator, ax2)

ax1.set_title('Numerador - $\exp(x)$ \n')
ax2.set_title('Denominador - $x^{100}$ \n')

plt.show()

No intervalo apresentado, a função do denominador cresce muito rapidamente nas proximidades de x \pm 1. Vemos, também, que a função exponencial do numerador é crescente. Ora, se a função do numerador é crescente, então pode ocorrer de em algum momento ter valor maior que o denominador. Mas, antes de verificar isto numericamente, vamos lembrar de algumas discussões usuais no estudo de funções matemáticas.

Quando avaliamos funções resultantes de somas, diferenças, produtos ou quocientes de outras funções de diferentes tipos, ou diferentes potências de um mesmo tipo de função, é usual falarmos que uma função “domina” a outra. Isto significa que quando x tende ao infinito negativo ou ao infinito positivo, o gráfico da função será semelhante ao da função dominante. Assim, temos as seguintes regras práticas usualmente discutidas e detalhadas em cursos de cálculo:

  • funções exponenciais dominam funções polinomiais
  • funções polinomiais dominam funções logarítmicas
  • entre funções exponenciais, bases maiores dominam bases menores
  • entre funções polinomiais, ordens maiores dominam ordens menores

Portanto, no caso em questão, a função exponencial do numerador dominará a função polinomial do denominador. O que significa que faz sentido a função j em questão tender ao infinito quando x tende ao infinito, visto que a função exponencial é crescente. Mas o gráfico de j, ao menos na janela apresentada não mostra isto ocorrendo, parece tender a zero…

Bom, em algum momento o numerador deve se igualar ao denominador e, então, passar a ter valor maior que este denominador. Para descobrir o ponto (ou pontos) onde ocorre tal igualdade, basta resolvermos a seguinte equação:

\exp(x) - x^{100} = 0

Podemos utilizar a função solve discutida em outro artigo:

from sympy import solve

solution = solve(exp(x) - x**100, x, numerical=False)
[s.n() for s in solution]

Vemos que há três valores de x que satisfazem a equação. Observe que o último é particularmente alto, bem superior ao intervalo apresentado nos gráficos anteriores. Vamos fazer um gráfico sobrepondo as funções do numerador e do denominador em um intervalo de valores de x que rodeiam o valor encontrado:

fig, ax = plt.subplots(figsize=(8, 6))

h_numerator = exp(x)
plot_h_numerator = plot(h_numerator, (x, 600, 650), show=False)
h_denominator = x**100
plot_h_denominator = plot(h_denominator, (x, 600, 650), show=False)

p1 = plot_h_numerator
p2 = plot_h_denominator

move_sympy_plot_to_axes(p1, ax)
move_sympy_plot_to_axes(p2, ax)

ax.legend(loc='upper left')

plt.show()

Agora, sim, percebemos que a função exponencial realmente domina a partir de x aproximadamente 647,3. Assim, podemos refazer o gráfico da função j com uma escala mais adequada para visualizar tal fato:

plot(j, (x, 640, 650), axis_center=(640, 0));

Vemos que, efetivamente, a função passa a aumentar de valor e, no limite de x tendendo ao infinito, tende ao infinito.

Podemos, também, verificar por substituição de alguns valores de exemplo essas mudanças de comportamento na função:

examples = (-1000, -2, -1, -0.9, -0.1, 0.1, 0.9, 1,
            1.01, 1.1, 2, 100, 500, 600, 650, 1000)

row_length = 35


print(f'x {" " * (row_length//2)} | {" " * (row_length//2)} j(x)')
print('-' * 45)

for number in examples:
    value = (exp(number) / number**100).n()
    spaces = str(row_length - len(str(number)))
    print(f'{number:<{spaces}}{value:.3e}')
x                   |                   j(x)
---------------------------------------------
-1000                         5.076e-735
-2                               1.068e-31
-1                               3.679e-1
-0.9                           1.531e+4
-0.1                           9.048e+99
0.1                             1.105e+100
0.9                             9.260e+4
1                                 2.718e+0
1.01                           1.015e+0
1.1                             2.180e-4
2                                 5.829e-30
100                             2.688e-157
500                             1.779e-53
600                             5.775e-18
650                             1.000e+1
1000                           1.970e+134

Perceba como há realmente um afastamento de valores próximos de zero para valores grandes à medida que x aumenta. Interessante, certo?

Teorema do Confronto

Para finalizarmos este artigo, veremos o chamado Teorema do Confronto, também conhecido como Teorema do Sanduíche. Comecemos pela definição formal:

Seja I um intervalo tendo o ponto a como limite. E sejam g, f e h funções definidas em I exceto possivelmente em a. Suponha que para cada x em I não igual a a tenhamos
g(x) \le f(x) \le h(x)
e suponha também que
\lim_{x \to a} g(x) = \lim_{x \to a} h(x) = L
Então \lim_{x \to a} f(x) = L

OK, bem abstrato. Vamos ver um exemplo, partindo da seguinte função:

k(x) = x^2 \sin \left( \frac{1}{x} \right)

Comecemos pelo gráfico da função:

k = x**2 * sin(1/x)

plot(k, (x, -0.25, 0.25));

Graficamente, verificamos que o limite de tal função quando x tende a zero deve ser zero. Vejamos o resultado pelo SymPy:

limit(k, x, 0)

Resultado esperado. Mas, e daí?

Caso você queira resolver o exercício manualmente, provavelmente começaria pela seguinte propriedade de limite:

\lim_{x \to a} (f(x) \cdot g(x)) = \lim_{x \to a} f(x) \cdot \lim_{x \to a} g(x)

No entanto, \lim\limits_{x \to 0} \sin \left( \frac{1}{x} \right) não existe. Logo, outra forma de resolver deve ser adotada.

Da própria definição da função seno, temos: -1 \le \sin \left( \frac{1}{x} \right) \le 1. Segue, portanto, que -x^2 \le x^2 \sin \left( \frac{1}{x} \right) \le x^2. Como \lim\limits_{x \to 0} -x^2 = \lim\limits_{x \to 0} x^2 = 0, do teorema do confronto segue que \lim\limits_{x \to 0} x^2 \sin \left( \frac{1}{x} \right) = 0.

Podemos utilizar o SymPy para verificar visualmente o significado do que acabamos de ver:

fig, ax = plt.subplots(figsize=(8, 6))

p1 = plot(k, (x, -0.25, 0.25), show=False,
          label=r'$x^2 \sin \left( \frac{1}{x} \right)$')
p2 = plot(-x**2, (x, -0.25, 0.25), show=False, label='$-x^2$')
p3 = plot(x**2, (x, -0.25, 0.25), show=False, label='$x^2$')

move_sympy_plot_to_axes(p1, ax)
move_sympy_plot_to_axes(p2, ax)
move_sympy_plot_to_axes(p3, ax)

ax.legend(loc='upper left')

plt.show()

Vemos como o gráfico de nossa função de interesse fica entre os gráficos das demais funções. Daí o nome “sanduíche” 🙂

Conclusão

Deu uma boa revisão de cálculo, não? Vimos vários exemplos usuais em aulas de cálculo e como o uso do SymPy permite enriquecer mais a discussão com a facilidade de gerar gráficos e de obter resultados nem sempre evidentes rapidamente, levando a aprofundadas discussões.

Acompanhe o projeto Ciência Programada nas redes sociais para saber quando são publicados novos artigos, em breve escreverei mais sobre cálculo 🙂

A lista completa de artigos sobre SymPy pode ser vista na tag SymPy aqui do site.

Até a próxima

Compartilhe:

1 comentário em “Cálculo com Python e SymPy – limite”

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Rolar para cima