3. Equações diferenciais

3.1. Forma geral das equações diferenciais

Uma equação diferencial ordinária —ou de forma abreviada, EDO— de ordem é uma relação entre uma função e as suas derivadas , , ..., . Por exemplo, uma possível equação diferencial ordinária de terceira ordem é:

(3.1)

Neste caso, é possível também escrever a equação mostrando explicitamente a expressão que define a derivada de terceira ordem:

(3.2)

É conveniente usar uma notação que permite ver a equação de forma mais geral: a função será denotada por , será a primeira derivada e a segunda derivada . A equação diferencial é então:

(3.3)

que define a derivada de em relação a (, , , ). Para resolver este problema é necessário resolver simultaneamente as equações que definem as derivadas de e . Ou seja, a equação original 3.1, de terceira ordem, foi transformada no sistema de 3 equações:


(3.4)

Esta forma de escrever a equação diferencial tem também a vantagem de poder ser escrita de forma mais compacta; define-se o vetor,

(3.5)

e o sistema de 3 equações diferenciais é equivalente a uma única equação diferencial vetorial:

(3.6)

onde a derivada do vetor é outro vetor obtido derivando cada uma das suas componentes,

(3.7)

e as três componentes da função vetorial são, neste caso,

(3.8)

Assim sendo, para resolver equações diferencias ordinárias como, por exemplo, a equação 3.1, basta saber resolver equações de primeira ordem com a forma geral

(3.9)

e generalizar o método ao caso em que e são vetores com várias componentes.

3.2. Equações diferenciais de primeira ordem

A forma geral das EDO de primeira ordem é

(3.10)

A função chama-se variável dependente e é a variável independente. Uma solução da EDO, num intervalo [, ], é qualquer função de que verifica a equação.

Existem em geral muitas soluções, por exemplo, a figura 3.1 mostra 7 possíveis soluções da equação . Em cada ponto de cada uma das curvas, o declive tem o mesmo valor obtido substituindo as coordenadas do ponto na expressão . As diferentes soluções não se cruzam nunca, porque em cada ponto existe apenas um valor e cada ponto pertence unicamente a uma das soluções da equação diferencial.

Soluções de uma equação diferencial de primeira ordem
Figura 3.1: Soluções da equação diferencial .

Se for dado um valor inicial para a função no ponto inicial , existe uma única solução , que é a curva tal que e com declive igual a em qualquer valor de .

Os métodos de resolução numérica consistem em calcular o valor da variável dependente numa sequência discreta de pontos {, , , …, }, usando alguma aproximação. O resultado obtido aplicando um determinado método será o conjunto de pontos {(, ), (, ), …, (, )}, que aproximam o gráfico da função . Nos métodos que usam incrementos constantes, o intervalo [, ] divide-se em subintervalos de comprimento , de forma que cada valor na sequência {} é igual ao valor anterior mais :

(3.11)

Por exemplo, a figura 3.2 mostra a solução da equação diferencial com e condição inicial = 1, no intervalo 0≤ ≤ 4. Os cinco pontos sobre a curva são a aproximação da função usando apenas 5 pontos com abcissas 0, 1, 2, 3 e 4.

Aproximação de uma curva com 5 pontos
Figura 3.2: Uma solução da equação , e aproximação com 5 pontos.

Cada ângulo na figura é a inclinação segmento entre os pontos (, ) e (, ). A tangente do ângulo (declive médio) é igual ao valor médio de no intervalo [, ]. A partir das coordenadas (, ) de um dos pontos, o declive médio permite calcular as coordenadas do ponto seguinte:

(3.12)

Usando estas relações de recorrência de forma iterativa, consegue-se obter as coordenadas de todos os pontos (, ), começando com os valores iniciais dados (, ).

Se os declives médios pudessem ser calculados de forma exata, os resultados obtidos seriam exatos. No entanto, para calcular o valor médio de num intervalo seria necessário conhecer a função , mas quando se usam métodos numéricos é porque não existem métodos para encontrar a expressão analítica dessa função. Os diferentes métodos numéricos que existem para resolver equações diferenciais correspondem a diferentes esquemas para estimar o valor médio aproximado da função em cada intervalo.

3.2.1. Método de Euler

No método de Euler admite-se que . Ou seja, o valor médio de em cada intervalo é aproximado pelo valor de no ponto inicial do intervalo. Realizando os cálculos no Maxima para o caso considerado acima, em que e com condição inicial = 1, pode usar-se uma lista para armazenar as coordenadas dos pontos; inicia-se a lista com o ponto inicial e define-se a função dada:

(%i1) p: [ [0, 1] ]$
(%i2) f(x,y) := (x-1)*(x-3)-y$

Os quatro pontos seguintes na sequência são acrescentados à lista , usando um ciclo e as relações de recorrência 3.12 com = 1:

(%i3) for i thru 4 do (
    [xi,yi]: last(p),
    p: endcons ([xi + 1, yi + f(xi, yi)], p))$
(%i4) p;
(%o4)    [ [0, 1], [1, 3], [2, 0], [3, −1], [4, 0] ]

O gráfico na figura 3.3 mostra o resultado obtido (segmentos de reta e pontos), comparando-o com a solução exata (curva contínua). Observa-se que a solução obtida com o método de Euler não é uma aproximação muito boa. No entanto, reduzindo o valor dos incrementos em de = 1 para um valor menor, deverá ser possível obter uma aproximação melhor. É conveniente reduzir gradualmente o valor de e comparar as soluções com as obtidas com valores maiores de ; se a solução obtida não variar significativamente quando é reduzido, essa solução deverá ser uma boa aproximação da solução verdadeira.

Método de Euler
Figura 3.3: Método de Euler.

Exemplo 3.1

A carga armazenada num condensador ligado a uma pilha e a uma resistência é uma função que depende do tempo e verifica a seguinte equação diferencial

No instante inicial, = 0, a carga no condensador é nula. Usando o método de Euler, encontre uma sequência que aproxime no intervalo 0 ≤ ≤ 6.

Resolução. Neste caso, a função que define a derivada não depende da variável independente . Os valores iniciais e a função que define a derivada são:

(%i5) p: [ [0,0] ]$
(%i6) f(Q) := 1.25 - Q$

Começando com incrementos de tempo =0.1, são necessárias 60 iterações para terminar em = 6.

(%i7) for i thru 60 do (
    [t0,Q0]: last(p),
    p: endcons ([t0 + 0.1, Q0 + 0.1*f(Q0)], p))$

O último ponto na lista é:

(%i8) last(p);
(%o8)    [5.999999999999995, 1.247753737125107]

Convém repetir o processo, com um valor menor de , por exemplo 0.01 e comparar o novo resultado com o resultado anterior:

(%i9) q: [ [0,0] ]$
(%i10) for i thru 600 do (
    [t0,Q0]: last(q),
    q: endcons ([t0 + 0.01, Q0 + 0.01*f(Q0)], q))$
(%i11) last(q);
(%o11)    [5.999999999999917, 1.246993738385861]

A pesar de que o resultado final coincide em 4 algarismos significativos nos dois casos, é interessante comparar as duas sequências completas. As duas soluções obtidas com = 0.1 e = 0.01 estão armazenadas nas listas e e podem ser representadas num gráfico. A figura 3.4 mostra que, a pesar de os resultados obtidos para = 6 serem quase idênticos nos dois casos, há uma discrepância entre as duas soluções obtidas, mais visível no intervalo 0.5 < < 3.5. No entanto, como essa discrepância não é muito grande, espera-se que a solução com = 0.01 já esteja bastante próxima da solução verdadeira. O gráfico foi feito com o seguinte comando

(%i12) plot2d ([[discrete, p], [discrete, q]], [xlabel, "t"], [ylabel, "Q"], [legend, "h=0.1", "h=0.01"]);
Carga num condensador em função do tempo
Figura 3.4: Soluções obtidas pelo método de Euler.

3.2.2. Método de Euler melhorado

A maior discrepância entre a sequência de pontos que aproximam a solução exata na figura 3.2 e a sequência obtida usando o método de Euler, apresentada na figura 3.3, é no segundo ponto, (, ). O valor médio do declive no primeiro intervalo, que era aproximadamente igual a zero, no método de Euler foi substituído por 2, que é o declive no ponto inicial (, ). A figura 3.2 mostra que, a pesar de que os dois primeiros valores da função, e , são iguais, o declive muda de um valor positivo em = 0 para um valor negativo em = 1. No método de Euler usou-se o declive em como valor médio o valor do declive nesse intervalo. Se o valor médio do declive fosse aproximado pela média entre os dois declives em e , o resultado seria muito melhor.

O método de Euler melhorado consiste em admitir que o declive médio é igual à média entre os declives no ponto inicial (, ) e no ponto final (, ); ou seja,

(3.13)

O problema com esta equação é que para poder-se calcular seria necessário saber o valor de , mas esse valor é desconhecido enquanto a solução da equação não for conhecida. É possível fazer uma estimativa inicial do valor que poderá ter e a seguir melhorar essa estimativa. Uma primeira estimativa muito rudimentar consiste em dizer que é igual a .

Com essa abordagem, a resolução da mesma equação considerada na secção anterior, com valor inicial = 1 e com 5 pontos no intervalo 0 ≤ ≤ 4, pode ser feita assim: começa-se por iniciar a lista de pontos e definir a função que calcula o declive

(%i13) p: [ [0,1] ]$
(%i14) f(x,y) := (x-1)*(x-3)-y$

Foi necessário definir novamente a função , já que a definição usada na secção anterior foi logo substituída pela função do exemplo 3.1. Basta repetir as 4 iterações mas usando agora a expressão 3.13 nas relações de recorrência 3.12

(%i15) for i thru 4 do (
    [xi,yi]: last(p),
    p: endcons ([xi+1, yi + (f (xi, yi) + f (xi+1, yi))/2], p))$
(%i16) p;
(%o16)  [ [0, 1], [1, ], [2, ], [3, ], [4, ] ]

A figura 3.5 mostra a sequência de pontos obtida, juntamente com a solução exata (curva contínua na figura). O resultado é muito melhor que o resultado obtido com o método de Euler.

Método de Euler melhorado
Figura 3.5: Solução exata e solução obtida com o método usado em (%i15).

O resultado obtido com (%i15) pode ser melhorado se a estimativa inicial = for melhorada com o resultado obtido a partir dessa primeira estimativa. Se designarmos à primeira estimativa do valor de , então = , como foi usado em (%i15). A segunda estimativa é: . Esse valor estará mais próximo do valor real de do que a estimativa inicial ; assim sendo, espera-se que a sequência se aproxime do valor real de . Calculam-se alguns termos dessa sequência, até ser menor que uma precisão desejada. Nesse momento usa-se = como valor inicial para o seguinte intervalo e o processo repete-se iterativamente em todo o intervalo de .

No caso do mesmo problema resolvido em (%i15), com = 1 e usando precisão de 0.0001 para , o método pode ser implementado assim:

(%i17) f(x,y) := float ((x-1)*(x-3)-y)$
(%i18) p: [ [0,1] ]$
(%i19) for i thru 4 do (
    [xi,yi]: last(p),
    y1: yi,
    y2: yi + (f(xi,yi)+f(xi+1,y1))/2,
    while abs(y1-y2) > 0.0001 do (
        y1: y2, y2: yi + (f(xi,yi)+f(xi+1,y1))/2),
    p: endcons ([xi+1, y2], p))$
(%i20) p;
(%o20)  [ [0, 1], [1, 1.33331298828125], [2, 0.1111229788511992], [3, −0.2962674737252655], [4, 0.9012259028472571] ]

Definiu-se novamente a função , acrescentando o comando float, para evitar obter fracções com numeradores e denominadores muito grandes; nos exemplos anteriores foram feitos menos cálculos e, por isso, não produziram frações com números grandes. O resultado (ver figura 3.6) mostra que a solução obtida está mais próxima da verdadeira solução do que as aproximações apresentadas nas figuras 3.3 e 3.5.

Metodo de Euler melhorado
Figura 3.6: Solução exata e solução obtida com o método de Euler melhorado.

A pesar de se ter usado uma precisão de 0.0001, não se pode garantir que a solução obtida esteja dentro dessa precisão em relação á solução real, já que a equação 3.13 é apenas uma aproximação e não o valor médio real da derivada. Para melhorar essa aproximação, em vez de se obter a média do declive em dois pontos será necessário fazer uma média em vários pontos do intervalo em questão, com diferentes pesos que podem ser definidos de forma a minimizar o erro. Existem muitos métodos diferentes que usam esse esquema; o mais popular é o que será descrito na seguinte secção.

3.2.3. Método de Runge-Kutta de quarta ordem

Neste método o valor médio do declive obtém-se a partir da média dos declives em 4 pontos diferentes, com pesos diferentes (figura 3.7).

Metodo de Runge-Kutta de quarta ordem
Figura 3.7: Declives usados no método de Runge-Kutta de quarta ordem.

Começa-se por calcular o declive no ponto inicial do intervalo, tal como no método de Euler:

(3.14)

a seguir, realiza-se um deslocamento na direção desse declive, avançando-se uma distância no eixo das abcissas, até um ponto 2 (ver figura 3.7). Nesse ponto 2, calcula-se um segundo valor do declive

(3.15)

Esse novo valor do declive é usado novamente, para realizar outro deslocamento a partir do ponto inicial, avançando na direção do eixo das abcissas, até um outro ponto 3, onde é calculado um terceiro valor do declive

(3.16)

seguindo a direção desse declive , realiza-se um terceiro deslocamento, a partir do ponto inicial, desta vez avançando uma distância no sentido do eixo das abcissas, para chegar até um ponto 4, onde se calcula um quarto valor do declive

(3.17)

Pode mostrar-se que para minimizar o erro cometido, o valor médio do declive deve ser aproximado pela seguinte combinação linear dos quatro declives calculados:

(3.18)

no exemplo da figura 3.7, esse valor médio da derivada desloca o ponto inicial até o ponto 4, que está bastante próximo da solução exata da equação.

Para aplicar este método à mesma equação considerada nas secções anteriores, com valor inicial = 1 e com 5 pontos no intervalo 0 ≤ ≤ 4, inicia-se a lista de pontos (a função que define o declive em cada ponto é a função que já está definida)

(%i21) p: [ [0,1] ]$
(%i22) h: 1$

Neste caso foi dado explicitamente o valor do comprimento dos intervalos, para que as expressões seguintes sejam mais claras.

A seguir realizam-se as 4 iterações necessárias para chegar até o ponto = 4. Em cada iteração calculam-se os quatro declives , , e e a média com os pesos dados na equação 3.18.

(%i23) for i thru 4 do (
    [xi,yi]: last(p),
    d1: f(xi, yi),
    d2: f(xi+h/2, yi+(h/2)*d1),
    d3: f(xi+h/2, yi+(h/2)*d2),
    d4: f(xi+h, yi+h*d3),
    p: endcons ([xi+h, yi + h*(d1+2*d2+2*d3+d4)/6], p))$
(%i24) p;
(%o24) [ [0, 1], [1, 1.020833333333333], [2, −0.09635416666666652], [3, −0.3902994791666666], [4, 0.8744710286458335] ]

A figura 3.8 mostra a sequência de pontos obtida, juntamente com a solução exata (curva contínua na figura). O resultado é bastante bom, a pesar do valor tão elevado que foi usado para os incrementos da variável . No caso da equação diferencial resolvida neste exemplo, existem métodos analíticos que permitem encontrar a expressão para a curva contínua que foi apresentada na figura; nos casos em que não é possível encontrar a solução analítica, o método de Runge-Kutta de quarta ordem é uma boa opção para encontrar uma boa aproximação numérica.

Exemplo do uso do métdodo de Runge-Kutta
Figura 3.8: Solução exata e solução obtida com o método de Runge-Kutta de quarta ordem.

Já existe uma função rk predefinida no Maxima que executa o método de Runge-Kutta de quarta ordem. No exemplo acima, bastava indicar a expressão de , o nome que identifica a variável dependente, o seu valor inicial e o intervalo em que se quer obter a solução, incluindo o valor dos incrementos . Assim sendo, a mesma lista obtida com os comandos (%i17) até (%i21) podia ser obtida com um único comando:

(%i25) rk ((x-1)*(x-3)-y, y, 1, [x,0,4,1]);
(%o25) [ [0.0, 1.0], [1.0, 1.020833333333333], [2.0, −0.09635416666666652], [3.0, −0.3902994791666666], [4.0, 0.8744710286458335] ]

3.3. Sistemas de equações diferenciais

Os métodos descritos nas secções anteriores podem ser generalizados facilmente a um sistema de várias equações, quando sejam conhecidas condições iniciais para todas as variáveis. Tal como se explica no inicio do capítulo, basta admitir que é um vetor e que é uma função vetorial.

Exemplo 3.2

Usando o método de Runge-Kutta de quarta ordem, encontre a solução do seguinte sistema de equações diferenciais:

no intervalo, 0 ≤ ≤ 4, com condições iniciais = −1.25 e = 0.75, em = 0.

Resolução. A variável independente é e a variável dependente é o vetor , que no Maxima representa-se por uma lista com duas partes, [, ]. A expressão que define as derivadas das variáveis dependentes também será uma lista com duas partes, [, ], mas é mais conveniente (para poder generalizar ao caso de variáveis) representá-la em função das componentes da lista :

(%i26) f(t,r) := [4-r[1]^2-4*r[2]^2, r[2]^2-r[1]^2+1]$

foi dada também a variável independente como argumento da função, a pesar de que neste caso não depende de , para que os comandos usados a seguir possam ser reutilizados em casos mais gerais.

Com as condições iniciais = 0 e = (−1.25, 0.75) cria-se a lista onde serão guardados os resultados das iterações

(%i27) p: [ [0,-1.25,0.75] ]$

Observe-se que a lista inicial [0,−1.25,0.75] e todas as outras listas que serão acrescentadas, incluem a variável independente e o vetor dependente . Para extrair apenas a variável independente usa-se o comando first, que extrai o primeiro elemento de uma lista e para extrair o vector usa-se o comando rest, que produz uma nova lista excluindo o primeiro elemento da lista original; ou seja, rest([0, −1.25, 0.75]) produz [−1.25, 0.75].

Usando incrementos = 0.02 para a variável independente , será necessário realizar 200 iterações para terminar em = 4.

(%i28) h: 0.02$
(%i29) for i thru 200 do (
    ti: first (last(p)),
    ri: rest (last(p)),
    d1: f (ti, ri),
    d2: f (ti+h/2, ri+(h/2)*d1),
    d3: f (ti+h/2, ri+(h/2)*d2),
    d4: f (ti+h, ri+h*d3),
    p: endcons (cons (ti+h, ri+h*(d1+2*d2+2*d3+d4)/6), p))$

O comando cons é semelhante a endcons, mas insere um elemento no início de uma lista, em vez de no fim. Neste caso, cons foi usado para inserir o valor de na lista [, ], produzindo a lista [, , ], que foi logo acrescentada no fim da lista dos resultados.

O último ponto na solução obtida é:

(%i30) last (p);
(%o30) [4.000000000000003, 1.232365393486131, −0.7493152366008236]

O mesmo resultado podia ter sido obtido com o comando rk

(%i31) p: rk ([4-x^2-4*y^2,y^2-x^2+1],[x,y],[-1.25,0.75],[t,0,4,h])$

Para imprimir os resultados é conveniente usar printf. Esse comando aceita um formato do Lisp que diz como devem ser imprimidos os dados. Neste exemplo pode usar se o formato "∼{∼{∼f∼^, }∼%∼}" para imprimir os 3 elementos , e de cada iteração numa linha separada, separados por uma vírgula e um espaço. Os dois símbolos ∼{ e ∼} usam-se para delimitar um formato que deve ser aplicado recursivamente a todos os elementos de uma lista, neste caso a lista . Dentro desses símbolos, o formato ∼{∼f∼^, ∼}∼% aplicará recursivamente o formato ∼f ^, a cada elemento das sublistas de , neste caso a , e , seguido de um de fim de linha, representado pelo símbolo ∼%. O símbolo ∼f é usado para escrever números de vírgula flutuante e o símbolo ∼^ indica que o que vem a seguir só será escrito se não se tiver chegado ao último elemento da lista; ou seja, será escrita uma vírgula e um espaço após e , mas não após .

Para imprimir unicamente os 9 resultados para = 0, 0.5,… ,4, extraem-se unicamente esses elementos da lista , usando o comando makelist:

(%i32) printf (true,"~{~{~f~^, ~}~%~}", makelist(p[25*i+1],i,0,8))$
0.0, -1.25, 0.75
0.5000000000000001, −1.1773384444565893, 0.8294212227394446
1.0000000000000004, −1.5811924578377141, 0.818421880110717
1.5000000000000009, −1.5173593339612323, 0.03778051398450264
2.0000000000000013, −0.0005587219405601973, 0.06781851774672522
2.5000000000000018, 1.4685416405394016, 0.148009097100235
3.000000000000002, 1.6981292199163442, −0.7368848754678627
3.5000000000000027, 1.188041502209304, −0.8582807863663763
4.000000000000003, 1.232365393486131, −0.7493152366008236

Finalmente, pode ser conveniente gravar esses resultados num ficheiro. Por exemplo, os programas de folha de cálculo conseguem ler dados como estão apresentados acima, dentro de ficheiros do tipo CSV (Comma Separated Values). Para gravar o que é apresentado no ecrã num ficheiro, usa-se o comando with_stdout

(%i33) with_stdout ("resultados.csv",
    printf(true,"~{~{~f~^, ~}~%~}", makelist(p[25*i+1],i,0,8)))$

Os resultados ficam gravados no ficheiro resultados.csv