Domando Equações Não Lineares: Inspeção e Descobrimento de Raízes
Contents
4. Domando Equações Não Lineares: Inspeção e Descobrimento de Raízes#
Objetivos de aprendizagem
- Identificar os tipos de não linearidades presentes em problemas de engenharia computacional.
- Utilizar análise gráfica e inspeção de parâmetros físicos para estimar palpites para raízes de equações não lineares.
- Implementar e testar algoritmos para processos iterativos usando condicionais e fluxo de controle.
"O coração sente que há três dimensões no espaço e que os números são infinitos; e a razão demonstra, em seguida, que não existem dois números quadrados dos quais um seja o dobro do outro. Os princípios são sentidos, as proposições são concluídas; e tudo isso com certeza, embora por caminhos diferentes." (Blaise Pascal, Pensées, 1670)
Resolver equações não lineares pela determinação de suas raízes é um dos problemas centrais em ciências computacionais e engenherias. Em termos simples, trata-se de encontrar os valores de uma variável que tornam verdadeira uma equação do tipo \(f(x) = 0\), onde \(f\) define uma não linear em \(x\). Diferente das equações lineares, cuja resolução segue regras algébricas diretas, as equações não lineares frequentemente não admitem soluções analíticas fechadas. Elas podem ter uma ou várias raízes, ou até mesmo nenhuma, dependendo do comportamento da função.
O interesse em resolver esse tipo de problema está presente em diversas áreas práticas, como a engenharia estrutural (quando se busca o ponto de equilíbrio de uma viga deformada), na termodinâmica (para encontrar a temperatura de equilíbrio), ou em algoritmos de inteligência artificial (ao ajustar parâmetros de redes neurais). Neste capítulo, relembraremos as principais funções descritoras de não linearidades e estudaremos recursos para análise gráfica de funções não lineares como precursores do conceito de aproximações sucessivas e de dois métodos clássicos para este fim: bisseção e Newton-Raphson.
Uma vez que cada método apresenta vantagens e limitações, que dependem da regularidade da função, do número de raízes esperadas, da proximidade de um chute inicial e da sensibilidade a erros de arredondamento, antes de aplicá-los, é essencial compreender o comportamento qualitativo da função por meio de abordagens iterativas, visuais e numéricas. Em particular, focaremos nos seguintes tópicos:
não linearidades;
plotagem básica de funções matemáticas com
matplotlib
;condicionais e fluxo de controle aplicados a processos iterativos;
critérios de decisão;

4.1. Não linearidades#
Comportamentos não lineares estão na base de diversos fenômenos naturais e modelos físicos. Por exemplo, a resposta de um material a uma força intensa pode caracterizar uma relação não linear entre tensão e deformação. Em sistemas dinâmicos, o crescimento populacional pode ser modelado por uma função exponencial ou logística, ambas não lineares. O movimento do sangue pelas veias e artérias segue leis não lineares. É ousado, mas não descabido dizer que tudo (ou quase tudo) à nossa volta apresenta algum tipo de comportamento não linear.
As funções matemáticas não lineares mais comuns incluem exponenciais, logarítmicas, polinômios de grau maior que 1, trigonométricas, racionais (frações de polinômios), e funções compostas envolvendo essas categorias. Por exemplo, o comportamento de crescimento populacional pode ser descrito por uma função exponencial, enquanto o amortecimento de um sistema pode envolver uma função logarítmica. Equações envolvendo seno ou cosseno aparecem frequentemente na análise de vibrações ou circuitos elétricos. Cada uma dessas funções apresenta comportamentos locais complexos, como máximos e mínimos, inflexões e assimetrias, que não podem ser previstos apenas por inspeção direta da expressão algébrica.
Podemos separar equações não lineares unidimensionais da forma \(f(x) = 0\) em dois grupos básicos:
Equações polinomiais, em que \(f\) é um polinômio.
Equações transcendentais, em que \(f\) possui pelo menos uma função transcendental, i.e. que não pode ser escrita de forma algébrica (variáveis não podem ser postas em evidência).
4.1.1. Equações polinomiais#
Algumas formas univariadas de equações polinomiais são as seguintes:
Canônica: \(f(x) = a_nx^n + a_{n-1}x^{n-1} + \dots + a_1x + a_0 = \displaystyle\sum_{k=0}^n a_kx^k\)
Fatorada: \(f(x) = a_n(x - x_1)(x - x_2) \ldots (x - x_n) = a_n \displaystyle\prod_{k=1}^n (x-x_k)\)
Racional: \(f(x) = a_nx^{\frac{p_n}{q_n}} + a_{n-1} x^{ \frac{p_{n-1}} {q_{n-1}} } + \ldots + a_1 x^{\frac{p_1}{q_1}} + a_0 = \displaystyle\sum_{k=0}^n a_k x^{\frac{p_k}{q_k}}, \ \ \frac{p_k}{q_k} \in \mathbb{Q}\)
Exemplos são: \(x^3 - 2x + 1/2 = 0\); \((x+1)^5 - 40x = 0\); e \(3x^{4/3} - 0.32x^{1/2} - x + 2 = 0\).
4.1.2. Equações transcendentais#
Equações transcendentais são oriundas da combinação de uma ou mais das seguintes funções:
Logaritmo: \(\log\), \(\log_2\), \(\ln\)
Exponencial: \(\exp\)
Trigonométricas: \(\text{sen}\), \(\text{cos}\), \(\text{tan}\) etc.
Trigonométricas inversas: \(\text{asec},\text{acos}, \cosh\) etc.
Exemplos são: \(\log(x) - \cos(x) + \pi = 0\); \(\text{sen}(3x) + \tanh(0.5x) - 1 = 0\); e \(\sqrt{x} - \frac{3}{5}\exp(x) = 0\).
4.2. Análise gráfica#
Devido à complexidade das funções não lineares, a análise gráfica tornou-se uma ferramenta fundamental para compreender e investigar o comportamento delas. Visualizar o gráfico de uma função permite identificar regiões onde há cruzamento com o eixo das abscissas (ou seja, onde a função é igual a zero), bem como analisar a concavidade, a existência de múltiplas raízes e a sensibilidade a perturbações nos parâmetros. Essa intuição visual é essencial antes da aplicação de métodos numéricos. Faremos um ensaio básico de análise gráfica da função do salto do paraquedista já vista no Caderno 1, isto é:
Entretanto, vale tecer breves comentários sobre variáveis e parâmetros para fins de redução de dimensionalidade na análise.
4.2.1. Variáveis e parâmetros#
Como já dissemos, tratamos aqui da determinação de raízes de equações não-lineares em uma dimensão (1D). Isto significa que apenas uma variável é usada como dependente, embora a variável dependente possa depender de outras quantidades para ser computada. No caso da função \(v\) acima, o tempo \(t\) está sendo tomado como a única variável independente, ao passo que a aceleração da gravidade \(g\), a massa \(m\) e o coeficiente de arrasto \(c\) tornam-se parâmetros.
Aplicando-se uma notação matemática estendida, poderíamos escrever
onde destacamos a variável independente em vermelho e os parâmetros em azul, após o ponto-e-vírgula. Entretanto, qualquer um dos parâmetros poderia ser analisado sob o ponto de vista de variável, assumindo-se as outras quantidades como parâmetros. Para este mesmo exemplo, se quiséssemos estudar a dependência de \(v\) por variação de\(g\), teríamos de manter \(m\), \(c\) e \(t\) fixados, obtendo uma forma tal que
Se o mesmo raciocínio for utilizado para\(c\), a forma
indicaria que\(c\)é a variável independente, enquanto \(t\), \(g\) e \(m\) seriam parâmetros com um valor fixado e conhecido.
Assim, por indução, a busca por raízes de equações não-lineares que dependam de \(n\) variáveis em apenas uma dimensão deve considerar uma variável independente com valor mutável e \(n-1\) parâmetros com valores fixados. Usando o Cálculo a várias variáveis, se
é a função não-linear a ser estudada, a forma
significaria que a análise é unidimensional sobre a variável \(x_k\) nos parâmetros de \(x_1\) a \(x_n\), excluindo-se \(x_k\).
Para o nosso exemplo, \(n=4\), \(y = v\), a variável independente \(x_k\) é o coeficiente de arrasto \(c\) – note que \(k\) pode ser qualquer valor entre 1 e 4 –, \(f\) é a expressão matemática \(\dfrac{gm}{c}(1 - e^{-(c/m)t})\) e os demais símbolos \(x_j\), para \(j \neq k\), são os parâmetros.
A visualização de “cortes” paralelos aos eixos coordenados que resultam da fixação de uma variável (tornando-se parâmetro) e da liberação de outra para um caso em que \(f\) é bivariada está disponível abaixo. Ao interagir com a representação visual somos capazes de reconhecer que \(f(x_1,x_2)\) recai em uma função univariada se a variável \(x_1\) ou \(x_2\) for fixada – de modo exclusivo – , assim podendo ser estudada pelas ferramentas aprendidas neste curso.
plot(fig, show_link=False,filename='../figs/analise-grafica.html')
display(HTML('../figs/analise-grafica.html'))
4.2.2. Análise gráfica do coeficiente de arrasto#
Aplicaremos a redução de dimensionalidade proposta anteriormente fixando como parâmetros o tempo \(t\), a velocidade \(v\), a massa \(m\) e a aceleração da gravidade \(g\), e permitindo a livre variação do coeficiente de arrasto \(c\). A análise gráfica servirá para visualizar o comportamento desta variável e detectar, pelo menos aproximadamente, onde há possíveis raízes. Este processo é conhecido como localização.
Dica
A plotagem básica de funções matemáticas via matplotlib pode ser realizada pela simples chamada de plt.plot(x,y)
, quando plt
é o placeholder da biblioteca pyplot
. As variáveis de código x
e y
funcionam como domínio e contradomínio.
Vejamos como realizar a plotagem da função \(y = f(c; t,v,m,g)\). Observe que, sendo \(c\) a única variável e, além disso, a variável independente, podemos, sem prejuízo, escrever \(y = f(c)\), ressalvada a fixação dos demais parâmetros. Neste caso, a imagem desta função, \(y\), não necessariamente possui um sentido físico e não temos que nos preocupar em interpretar o que ela significa. O importante é identificar se há um ou mais valores de\(c\)que produzem uma imagem nula. Em caso positivo, teremos encontrado uma ou mais raízes de \(f\).
Em outras palavras, as perguntas que queremos responder são:
Existe algum coeficiente de arrasto que produz o cenário determinado pelos parâmetros?
Se houver, o cenário que se forma é realista? Ele respeita as leis da física?
Vamos fazer experimentos numéricos e começar com alguns valores iniciais para os parâmetros.
import numpy as np
import matplotlib.pyplot as plt
# Parâmetros fixados
t = 120.0
v = 80.0
m = 90.0
g = 10.
# Localização
a,b = 4,30
c = np.linspace(a,b,100)
f = g*m/c*(1 - np.exp(-c/m*t)) - v # f(c) = 0
plt.plot(c,f,'g-',c,c*0,'k:');
plt.xlabel('c');
plt.ylabel('f(c)');
plt.title('Variação do coeficiente de arrasto');

4.2.3. Refinamento#
Observa-se que existe uma raiz para \(f(c)\) próxima a \(c = 11\). Todavia, é difícil precisar seu valor apenas por observação visual. A fim de buscar melhores aproximações, podemos refinar o gráfico realizando uma espécie de zoom até obter um intervalo adequado para aplicação algorítmica.
Nos gráficos abaixo, produzimos uma espécie de refinamento apenas através de plotagens. Até aí, não há nada numérico acontecendo. Porém, isso nos ajuda a selecionar ainda melhor uma condição inicial para o processo iterativo subsequente.
# Refinamento
a,b = 10,12
c = np.linspace(a,b,100)
f = g*m/c*(1 - np.exp(-c/m*t)) - v
plt.plot(c,f,'g-');
plt.plot(c,0*c,'k:');

# Refinamento
a,b = 11.24,11.27
c = np.linspace(a,b,100)
f = g*m/c*(1 - np.exp(-c/m*t)) - v
plt.plot(c,f,'g-')
plt.plot(c,0*c,'k:');

# Refinamento
a,b = 11.245,11.255
c = np.linspace(a,b,100)
f = g*m/c*(1 - np.exp(-c/m*t)) - v
plt.plot(c,f,'g-');
plt.plot(c,0*c,'k:');

Apenas com esses refinamentos, já é possível determinar a raiz única com um erro absoluto de 0.01 unidades, visto que ela está entre 11.245 e 11.255. Além disso, note como, neste intervalo, a curvatura da função não é mais identificável. Isso mostra que, nesse intervalo, \(f\) é praticamente “linear”. Porém, nos casos reais, geralmente requeremos precisão mais elevada. Então, para melhorarmos a precisão de nossa raiz, precisamos avançar para uma segunda etapa que apoia-se em processos iterativos para criar o que é conhecido como aproximações sucessivas.
4.2.4. Receitas prontas para visuais#
Na análise gráfica de funções não lineares, alguns elementos úteis para as plotagens são:
o gráfico da função \(f(x)\) sob análise;
o gráfico da função \(g(x) = 0\) ou o gradeado do plano Cartesiano;
as legendas dos eixos;
o intervalo de estimativas iniciais;
Um bloco de código baseado em matplotlib
replicável que contempla tudo isso é fornecido a seguir:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-1,2) # domínio de análise
f = np.exp(-x**2)*np.cos(3*x) # função-alvo
fig, ax = plt.subplots(figsize=(6,4), constrained_layout=True) # figura
#ax.grid(axis='both') # gradeado
ax.plot(x,f,'g',label='$f(x)$') # plotagem
ax.axhline(y=0, ls=':', color='k', label='$y=0$') # g(x) = 0
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_title('Exemplo de plotagem para análise gráfica')
# 1o. intervalo de busca
ax.plot(-0.7,0,'|',c='k',ms=15)
ax.plot(-0.4,0,'|',c='k',ms=15)
# 2o. intervalo de busca
ax.plot(0.4,0,'|',c='k',ms=15)
ax.plot(0.7,0,'|',c='k',ms=15)
# 3o. intervalo de busca
ax.plot(1.4,0,'|',c='k',ms=15)
ax.plot(1.7,0,'|',c='k',ms=15)
ax.legend();

4.3. Processo iterativo#
Como as soluções analíticas são raras em problemas não lineares mais realistas, recorre-se frequentemente a processos iterativos que convergem progressivamente para uma solução aceitável. Essas aproximações baseiam-se em hipóteses locais, como a linearização em torno de um ponto, e dependem criticamente da escolha de bons palpites iniciais e de critérios de parada adequados. Processos iterativos em métodos numéricos assumem o caráter de aproximações sucessivas, pelo fato de buscarem aproximações melhores para uma quantidade a partir de uma estimativa inicial predefinida.
Levando em conta que estamos trabalhando com funções em uma dimensão, um processo iterativo a uma variável pode ser descrito por:
Acima, \(\phi\) é a função de iteração, ao passo que \(x_0\) é a estimativa inicial (initial guess). Nesta notação, \(x^{(k)}\) é a \(k\)-ésima iterada de uma sequência numérica discreta.
4.3.1. Fluxo de controle#
A primeira estrutura de controle fundamental para computação numérica de processos iterativos é o laço for
. Vamos ver como usá-lo para computar algumas funções iterativas, inclusive plotar os resultados.
\(\phi_1 = \dfrac{ (x^{(k-1)})^{1/2}}{\pi} - x^{(k-1)}, \ \ x^{(0)} = 1, \ \ k < N\)
# No. de elementos
N = 35
# Valor inicial
x = [1]
print(f'x(0) = {x[0]}')
# Função de iteração
for k in range(1,N):
phi = x[k-1]**1/2/np.pi - x[k-1]
x.append(phi)
print(f'x({k}) = {phi:.4f}')
# Plotagem
plt.figure(figsize=(8,3))
plt.plot(x,'go',label=r'$\dfrac{ (x^{(k)})^{1/2}}{\pi} - x^{(k)}$')
plt.xticks(range(N))
plt.xlabel('$x^{(k)}$',fontsize=12)
plt.ylabel(r'$\phi_1^{(k)}$',fontsize=12)
plt.legend(loc='upper right', fontsize=12);
x(0) = 1
x(1) = -0.8408
x(2) = 0.7070
x(3) = -0.5945
x(4) = 0.4999
x(5) = -0.4203
x(6) = 0.3534
x(7) = -0.2972
x(8) = 0.2499
x(9) = -0.2101
x(10) = 0.1767
x(11) = -0.1486
x(12) = 0.1249
x(13) = -0.1050
x(14) = 0.0883
x(15) = -0.0743
x(16) = 0.0624
x(17) = -0.0525
x(18) = 0.0441
x(19) = -0.0371
x(20) = 0.0312
x(21) = -0.0262
x(22) = 0.0221
x(23) = -0.0186
x(24) = 0.0156
x(25) = -0.0131
x(26) = 0.0110
x(27) = -0.0093
x(28) = 0.0078
x(29) = -0.0066
x(30) = 0.0055
x(31) = -0.0046
x(32) = 0.0039
x(33) = -0.0033
x(34) = 0.0028

\(\phi_2 = \dfrac{k}{x^{(k-1)}}, \ \ x^{(0)} = 1/5, \ \ k < 10\)
# No. de elementos
N = 30
# Função de iteração
y = [1/5]
print(f'y(0) = {y[0]}')
for k in range(1,N):
phi = k/(y[k-1])
y.append(phi)
print(f'y({k}) = {phi:.4f}')
# Plotagem
plt.figure(figsize=(8,3))
plt.plot(y,'go',label=r'$\dfrac{k}{x^{(k-1)}}$')
plt.xticks(range(N))
plt.xlabel('$x^{(k)}$',fontsize=12)
plt.ylabel(r'$\phi_2^{(k)}$',fontsize=12)
plt.legend(loc='center right', fontsize=12);
y(0) = 0.2
y(1) = 5.0000
y(2) = 0.4000
y(3) = 7.5000
y(4) = 0.5333
y(5) = 9.3750
y(6) = 0.6400
y(7) = 10.9375
y(8) = 0.7314
y(9) = 12.3047
y(10) = 0.8127
y(11) = 13.5352
y(12) = 0.8866
y(13) = 14.6631
y(14) = 0.9548
y(15) = 15.7104
y(16) = 1.0184
y(17) = 16.6924
y(18) = 1.0783
y(19) = 17.6197
y(20) = 1.1351
y(21) = 18.5007
y(22) = 1.1891
y(23) = 19.3416
y(24) = 1.2408
y(25) = 20.1475
y(26) = 1.2905
y(27) = 20.9224
y(28) = 1.3383
y(29) = 21.6697

4.4. Aplicações#
4.4.1. Determinação de raízes por força bruta#
No computador, sabemos que uma função matemática \(f(x)\) pode ser representada de duas formas principais:
através de uma função programada (em Python, por exemplo) que retorna o valor da função para um dado argumento
uma coleção de pontos \((x,f(x))\) na forma de uma tabela.
A segunda forma é bem mais útil para análise gráfica. Esta forma é também adequada para resolver problemas de determinação de raízes e de otimização com simplicidade. No primeiro caso, basta pesquisar todos os pontos e procurar onde a função cruza o eixo \(x\), como fizemos anteriormente. No segundo caso, buscamos um ponto de mínimo ou máximo local, ou global.
Abordagens que seguem esse caminho podem chegar a examinar uma grande quantidade de pontos. Por essa razão, são chamados de métodos de força bruta, isto é, não seguem uma técnica elaborada.
4.4.1.1. Algoritmo numérico#
Em geral, queremos resolver o problema \(f(x) = 0\) especialmente quando \(f\) é não-linear. Para isso, desejamos encontrar os \(x\) onde \(f\) cruza o eixo. Um algoritmo em força bruta deverá percorrer todos os pontos sobre a curva e verificar se um ponto está abaixo do eixo e seu sucessor imediato está acima, ou vice-versa. Se isto ocorrer, então deve haver uma raiz neste intervalo.
Algoritmo. Dado um conjunto de \(n+1\) pontos \((x_i,y_i)\), \(y_i = f(x_i), \, i = 0,\ldots,n\), onde \(x_0 < \ldots < x_n\). Verificamos se \(y_i < 0\) e se \(y_{i+1} > 0\). Uma expressão compacta para esta checagem é o teste \(y_i \, y_{i+1} < 0\). Se o produto for negativo, então a raiz de \(f\) está no intervalo \([x_i,x_{i+1}]\). Assumindo uma variação linear entre os pontos, temos a aproximação
Logo, \(f(x) = 0\) implica que a raiz é
Exemplo. Encontre a raiz da função \(f(x) = \exp(-x^2)\cos(3x)\) usando o algoritmo de força bruta.
Vamos plotar esta função apenas para visualizar seu comportamento.
from numpy import exp, cos
f = lambda x: exp(-x**2)*cos(3*x)
x = np.linspace(0,4,200)
plt.plot(x,f(x),'g');
plt.plot(x,0*f(x),'k:');

Nesta plotagem, vemos claramente que a função possui duas raizes: uma próxima de \(x = 0.5\) e outra em \(x = \pi/6\).
Implementemos o algoritmo.
def forca_bruta(f,a,b,n):
from numpy import linspace
x = linspace(a,b,n)
y = f(x)
raizes = []
for i in range(n-1):
if y[i]*y[i+1] < 0:
raiz = x[i] - (x[i+1] - x[i])/(y[i+1] - y[i])*y[i]
raizes.append(raiz)
if len(raizes) == 0:
print('Nenhuma raiz foi encontrada')
return raizes
Agora aplicamos o algoritmo na mesma função.
a,b,n = 0,4,1000
raizes = forca_bruta(f,a,b,n)
print(raizes)
[np.float64(0.5236017411236913), np.float64(1.5708070694852787), np.float64(2.6180048381439596), np.float64(3.665219264613299)]
Temos, na verdade, 4 raízes! Plotemos o gráfico ampliado no intervalo [2.5,3.8].
x2 = np.linspace(2.5,3.8,100)
plt.plot(x2,f(x2),'g',x2,0*f(x2),'k:');

Conseguimos enxergar mais uma raiz. Agora, plotemos um pouco mais ampliado entre [3.6,3.7].
x3 = np.linspace(3.6,3.7,100)
plt.plot(x3,f(x3),'g',x3,0*f(x3),'k:');

Dessa forma, podemos identificar que, de fato existe uma quarta raiz.
Este exemplo mostrou uma aplicação do método de força bruta para determinação de raízes. Para finalizar, podemos embelezar o gráfico.
r = np.array(raizes) # vetoriza a lista
plt.plot(x,0*f(x),'k:',x,f(x),'g-',r,np.zeros(4),'og',)
plt.xlabel('$x$',fontsize=14)
plt.ylabel('$f(x)$',fontsize=14)
plt.title(r'Raízes de $e^{-x^2}\cos(3x)$');

4.4.2. Programa simulador de saltos#
A seguinte classe é um protótipo de código com maior carga de abstração para simulações genéricas do modelo de velocidade terminal do paraquedista. O propósito é criar plotagens diretas do comportamento de uma variável a partir de parâmetros pré-fixados pelo usuário. Por sua vez, ele pode também ser caracterizado como um modelo computacional para estudo e análise do problema do salto.
import numpy as np, matplotlib.pyplot as plt
class Parachute:
'''Cria objetos para realizar simulações com o modelo 1D de velocidade terminal
com base em opções do usuário.
'''
# Variáveis de classe
base_param = None
unit = None
# Construtor
def __init__(self, lim_inf: float, lim_sup: float, num: int, **kwargs):
self.dict = kwargs
self.a = lim_inf
self.b = lim_sup
self.num = num
# Função definidora do modelo matemático
def f(self):
params = self.dict.keys()
# Variável: gravidade
if 'g' not in params:
self.dict['g'] = np.linspace(self.a, self.b, self.num, dtype=np.float32)
x = self.dict['g']
self.base_param = 'g'
self.unit = '$m/s^2$'
# Variável: massa
elif 'm' not in params:
self.dict['m'] = np.linspace(self.a, self.b, self.num, dtype=np.float32)
x = self.dict['m']
self.base_param = 'm'
self.unit = '$kg$'
# Variável: coef. de arrasto
elif 'c' not in params:
self.dict['c'] = np.linspace(self.a, self.b, self.num, dtype=np.float32)
x = self.dict['c']
self.base_param = 'c'
self.unit = '$kg/s$'
# Variável: tempo
elif 't' not in params:
self.dict['t'] = np.linspace(self.a, self.b, self.num, dtype=np.float32)
x = self.dict['t']
self.base_param = 't'
self.unit = '$s$'
# Variável: velocidade (padrão)
else:
self.dict['v'] = 0.0
self.dict['t'] = np.linspace(self.a, self.b, self.num, dtype=np.float32)
x = self.dict['t']
self.base_param = 't'
self.unit = '$s$'
# Modelo para velocidade terminal
y = self.dict['g']*self.dict['m']/self.dict['c']*(1 - np.exp( - self.dict['c']/self.dict['m']*self.dict['t'])) - self.dict['v']
return x, y
# Função de suporte para plotagem
def plot(self, label: str):
x,y = self.f()
h = plt.plot(x,y,label=label)
plt.xlabel(f'{self.base_param} [{self.unit}]', fontsize=10)
plt.ylabel(f'y = f({self.base_param})', fontsize=10)
plt.grid(True)
return h
A partir daí, podemos criar diversos testes e analisar comportamentos.
Simulações para intervalo de massa em 4 durações de tempo diferentes:
Essa faixa de massa corresponde a de adultos?
curves = []
for ti in np.linspace(10,30,4,dtype='float32'):
line, = Parachute(30, 40, num=30, c=10, g=9.85, v=32, t=ti).plot(label=f't={ti:.2f} s')
curves.append(line)
plt.legend(handles=curves,bbox_to_anchor=(1,1));

Simulações para coeficiente de arrasto em 4 acelerações gravitacionais diferentes:
Qual é a característica das atmosferas para eses casos?
curves = []
for gi in np.linspace(9.8,10.4,4,dtype='float32'):
line, = Parachute(13, 22, num=30, m=70, g=gi, t=60, v=40).plot(label=f'g={gi:.2f} $m/s^2$')
curves.append(line)
plt.legend(handles=curves,bbox_to_anchor=(1.,1));
