INTRODUÇÃO AO CÁLCULO NUMÉRICO LABORATÓRIO – AULA 02 EQUAÇÕES NÃO LINEARES 1. INTRODUÇÃO Nesta aula prática estudaremos alguns problemas que são expressos, matematicamente, como equações não lineares, ou seja, iremos determinar a raiz dessas equações, em computador. A ferramenta computacional que empregaremos é o Scilab, com o qual nos familiarizamos na Aula 01 de Laboratório. Na resolução das equações não lineares, empregaremos os métodos que aprendemos nas aulas teórica: bissecção, falsa posição, ponto fixo, newton-raphson e secante. Cada um desses métodos foi implementado como uma função do Scilab, armazenada em arquivos de extensão .sci. A esta altura, você já deve ter copiado os arquivos ‘bisecção.sci’, ‘falsaposicao.sci’, ‘pontofixo.sci’, ‘newtonraphson.sci’ e ‘secante.sci’ para alguma pasta em seu computador. Essa pasta será o diretório de trabalho para tudo o que for realizado nesta aula. Antes de começarmos a explorar os métodos numéricos propriamente ditos, vamos conhecer dois recursos do ambiente Scilab, funções e scritps. Ambos podem ser encarados como formas de programação do ambiente permitindo organizar nossas atividades e alcançar um desempenho significativamente melhor.
15
Embed
INTRODUÇÃO AO CÁLCULO NUMÉRICO LABORATÓRIO – … · aulas teórica: bissecção, falsa posição, ponto fixo, newton-raphson e secante. Cada um desses métodos foi implementado
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
INTRODUÇÃO AO CÁLCULO NUMÉRICO
LABORATÓRIO – AULA 02
EQUAÇÕES NÃO LINEARES
1. INTRODUÇÃO
Nesta aula prática estudaremos alguns problemas que são expressos, matematicamente, como
equações não lineares, ou seja, iremos determinar a raiz dessas equações, em computador. A
ferramenta computacional que empregaremos é o Scilab, com o qual nos familiarizamos na
Aula 01 de Laboratório.
Na resolução das equações não lineares, empregaremos os métodos que aprendemos nas
aulas teórica: bissecção, falsa posição, ponto fixo, newton-raphson e secante. Cada um desses
métodos foi implementado como uma função do Scilab, armazenada em arquivos de extensão
.sci. A esta altura, você já deve ter copiado os arquivos ‘bisecção.sci’, ‘falsaposicao.sci’,
‘pontofixo.sci’, ‘newtonraphson.sci’ e ‘secante.sci’ para alguma pasta em seu computador.
Essa pasta será o diretório de trabalho para tudo o que for realizado nesta aula.
Antes de começarmos a explorar os métodos numéricos propriamente ditos, vamos conhecer
dois recursos do ambiente Scilab, funções e scritps. Ambos podem ser encarados como formas
de programação do ambiente permitindo organizar nossas atividades e alcançar um
desempenho significativamente melhor.
2. FUNÇÕES E SCRIPTS NO SCILAB
2.1. FUNÇÕES
O conceito de função do Scilab é similar ao de outras linguagens de programação
como, por exemplo, a linguagem C.
Considere o método da bissecção; na aula teórica o algoritmo foi apresentado e
discutido através do pseudocódigo correspondente e vamos supor que sabemos
traduzir esse algoritmo para a linguagem do Scilab. Então, numa situação prática,
executaríamos o Scilab e, na janela Console, definiríamos a função matemática
cuja raiz queremos determinar , digitaríamos todos os comandos correspondentes
ao método da bissecção e teríamos a solução.
Mas e se quiséssemos resolver novamente o mesmo problema com diferentes
estimativas iniciais ou, ainda, resolver uma equação não linear diferente? Teríamos
que digitar de novo todos os comandos correspondentes ao algoritmo da
bissecção mas, sem dúvida, isso é um enorme desperdício de tempo.
É aqui que entra o conceito de função; todas as instruções (ou comandos)
relacionados entre si e utilizados para executar uma tarefa bem definida são
agrupados em um único conjunto que pode ser utilizado sempre que necessário,
sem que precisemos digitar novamente todas as instruções.
Como dissemos, o conceito de função do Scilab é similar ao de outras linguagens,
porém, sua implementação tem características específicas que veremos a seguir.
As funções podem ter um número qualquer de argumentos de entrada e
argumentos de saída. Os argumentos de entrada da função não são por ela
modificados mas os argumentos de saída, sim. A sintaxe completa para a definição
de uma função é:
[o1, ... , on] = nome_da_funcao (i1, ... , in)
Os argumentos de entrada e de saída são separados por vírgulas mas os de
entrada são delimitados por parênteses enquanto que os de saída são delimitados
por colchetes.
As funções podem ser definidas de três modos distintos.
O primeiro deles corresponde a definir a função na própria Console e em seguida
utilizá-la através de chamadas a ela. Esse modo é útil quando a função é
relativamente simples (número não muito grande de instruções). Por outro lado, a
função só existe em quanto durar a sessão de trabalho. Se você fechar o Scilab, a
função se perde. Se você executar o comando clear a função também se perde,
pois esse comando limpa todo o espaço de trabalho do Scilab. Vejamos, na prática,
como funciona esse modo de definição de funções.
ATIVIDADE PRÁTICA Na console, digite as seguintes linhas: -->function [x2, x3] = potencias(x) -->x2=x^2; -->x3=x^3; -->endfunction Comentários
Toda definição de função começa com a palavra function e termina com
endfunction. Entre elas fica o corpo da função. A linha que começa com function é
chamada cabeçalho.
Agora a função está pronta para ser usada, isto é, podemos executar uma chamada de função. Execute o comando: -->[quadrado, cubo] = potencias(3) cubo =
27.
quadrado =
9.
O segundo modo é chamado de funções “inline” e também é feito na Janela de
Console, valendo apenas na sessão de trabalho atual. Também é bastante útil
quando a função é dada por uma ou mais expressões simples.
ATIVIDADE PRÁTICA Na console, digite os seguintes comandos. Examine a sintaxe do comando deff e observe os resultados. -->deff('[x2, x3]=potencias(x)',['x2=x^2';'x3=x^3;']) -->[x2, x3]=potencias(3) x3 = 27. x2 = 9.
O terceiro modo de definir uma função é armazenar o código da função em um
arquivo. Desta maneira, o código não se perde quando a sessão de trabalho é
encerrada.
As funções escritas pelo usuário, por uma convenção da comunidade Scilab, ficam
armazenadas em arquivos de extensão .sci. Como são arquivos de texto, qualquer
editor de texto pode ser utilizado para escrever uma função e gravar o arquivo .sci
correspondente. No entanto, o Scilab oferece um editor de texto próprio chamado
SciNotes que pode ser acessado através do menu da Janela Principal do Scilab pela
sequência Aplicativos -> SciNotes. É recomendável utilizar sempre o SciNotes,
porque esse editor apresenta uma série de realces que facilitam muito a escrita e a
leitura de um arquivo .sci (o mesmo vale para os arquivos de script que serão
vistos no próximo item).
Como esta segunda maneira de definir funções envolve arquivos precisamos
sempre saber qual é o diretório de trabalho atual; isso pode ser feito digitando, na
Console, o comando pwd. Para mudar o diretório atual, o mais simples é usar o
Menu Principal; selecione Arquivo -> Alterar o diretório atual...
A questão, agora, é: como uma função que está armazenada em um arquivo pode
ser chamada durante uma sessão de trabalho? O Scilab fornece um procedimento
em duas etapas. Na primeira etapa, a função passa a integrar o ambiente de
trabalho, ou seja, fica disponível para ser executada. Isto é feito através do
comando exec:
--> exec(“nome_da_funcao.sci”);
(supondo que o arquivo nome_da_funcao.sci esteja no diretório atual; se não
estiver, você pode fornecer o caminho completo até o local onde o arquivo está,
exemplo: exec(“C:\...\...\nome_da_funcao.sci”)
Observe que, com o comando exec a função não é executada, de fato; ela, apenas
fica disponível para uso. Vamos repetir a ATIVIDADE PRÁTICA anterior só que,
desta vez, utilizando arquivos.
ATIVIDADE PRÁTICA No Menu Principal, selecione Aplicativos->SciNotes. A janela do editor de texto
será exibida.
No editor, entre com o código da função ‘potencias’ da atividade anterior.
Selecione Arquivo->Salvar. Para ser usado corretamente, o nome do arquivo
deve ser exatamente igual ao nome da função, mas observe que a caixa de
diálogo apresentada pelo SciNotes já sugere esse nome. Certifique-se sobre o
diretório e salve o arquivo.
Agora, retorne para a Console do Scilab e execute os comandos e observe o
resultado.
-->exec("potencias.sci");
-->[quadrado, cubo]=potencias(4)
cubo =
64.
quadrado =
16.
Observe que, a partir do próprio SciNotes é possível carregar o arquivo .sci para
uso futuro. Para isso, no Menu Principal do SciNotes, selecione a opção Executar e,
a seguir, uma das opções exibidas. Experimente com elas e veja o resultado na
Console.
No Scilab, assim como em outras linguagens, é possível construir bibliotecas
inteiras de funções, mas não discutiremos esse assunto aqui.
2.2. SCRIPTS
De modo semelhante ao caso das Funções do item anterior, quando vários
comandos devem ser executados pode ser conveniente gravar a sequência de
comandos em um arquivo chamado script e invocar esse script toda vez que for
necessário.
Um script também pode ser criado com o editor SciNotes, mas uma diferença
entre funções e scripts é que, enquanto funções são armazenadas em arquivos
com extensão .sci, por convenção, scripts são armazenados em arquivos com
extensão .sce.
Tanto funções quanto scripts são “carregados” no ambiente de trabalho do Scilab
através do comando exec, mas uma diferença essencial é que as funções apenas
ficam disponíveis para uso enquanto que um script é executado imediatamente.
Assim, por exemplo, se você tem um arquivo chamado estudoconvergencia.sce
que contém a função cuja raiz você quer determinar e uma sequência de
chamadas a vários métodos numéricos com o objetivo de compará-los entre si, ao
executar
--> exec(“estudoconvergencia.sce”);
todos os comandos contidos no arquivo serão imediatamente executados.
Nos estudos de caso discutidos a seguir veremos alguns scripts em ação.
3. ESTUDO DE CASO: CONVERGÊNCIA DOS MÉTODOS NUMÉRICOS
Nossa primeira atividade prática envolvendo os métodos numéricos usados na
determinação de raízes de equações não lineares consiste em tomar uma equação não
linear, aplicar cada um dos métodos e compará-los através do número de iterações e
da evolução do erro relativo aproximado, por meio de gráficos. Utilizaremos a função
f(x)=e-x
-x, empregada por Chapra e Canale no livro Numerical Methods for Engineers
para exemplificar o comportamento de cada método.
3.1. ANATOMIA DOS SOLVERS
Antes de iniciarmos a atividade prática propriamente dita, vamos analisar a
estrutura dos programas (solvers). Com o editor SciNotes, visualize o arquivo
newtonraphson.sci, por exemplo. Você verá o seguinte conteúdo.
function [raiz, x, iter, ea]=newtonraphson(x0, f, fp, tol, imax)
iter = 0;
xr = x0;
x(iter+1)=x0;
ea(iter+1)=100;
while (1)
xrold = xr;
xr = xrold - f(xrold)/fp(xrold);
iter = iter+1;
x(iter+1) = xr;
if(xr ~= 0) then
ea(iter+1)=abs((xr-xrold)/xr)*100;
end;
if(ea(iter+1) <= tol) then
raiz = xr;
return;
end;
if(iter >= imax) then
error('Número Máximo de Iterações Alcançado');
end;
end
Interessa-nos aqui examinar os parâmetros de entrada e saída da função
newtonraphson. Observe que os parâmetros de entrada incluem todas as
informações que o método numérico precisa para determinar a raiz; no caso do
Método de Newton-Raphson, como vimos na aula teórica, esses parâmetros são:
x0 Estimativa inicial
f Função cuja raiz será determinada
fp Derivada da função f
tol Tolerância pré-especificada
imax Número Máximo de Iterações
Os parâmetros x0, tol e imax são números que podem ser fornecidos diretamente
na chamada da função newtonraphson. Os parâmetros f e fp são nomes de
funções que avaliam, respectivamente, o valor da função matemática, f, e da
derivada de f, fp, em um determinado ponto. Devem ser definidas separadamente,
de acordo com o que foi estudado no item 2.
Outros métodos numéricos precisam de parâmetros de entrada diferentes. Por
exemplo, Bissecção, Posição Falsa e Secante exigem duas estimativas iniciais e não
precisam da derivada da função. As respectivas definições são mostradas a seguir:
function [raiz, x, iter, ea]=biseccao(xl, xu, f, tol, imax)
function [raiz, x, iter, ea]=falsaposicao(xl, xu, f, tol, imax)
function [raiz, x, iter, ea]=pontofixo(x0, f, tol, imax)
function [raiz, x, iter, ea]=secante(xi, xf, f, tol, imax)
Os parâmetros de saída, por sua vez, são os mesmos para todos os métodos e é
importante que seja assim porque permite compará-los entre si.
raiz Valor encontrado para a raiz
x Vetor com a sequência de raízes
iter Número de Iterações Executadas
ea Vetor com a sequência de erros relativos
3.2. DETERMINAÇÃO DAS ESTIMATIVAS INICIAIS GRAFICAMENTE
Todos os métodos numéricos para determinação de raízes de equações não
lineares necessitam de uma estimativa inicial para a raiz, de modo a dar início ao
processo iterativo.
Como vimos nas aulas teóricas, uma maneira de determinar a estimativa inicial (ou
duas estimativas no caso de alguns dos métodos) é fazer o gráfico da função e
identificar, aproximadamente, em que ponto o gráfico cruza o eixo das abscissas.
Vamos fazer o gráfico da função y=e-x
-x, no intervalo [0,5]; esse intervalo pode ser
ajustado se for necessário para identificar melhor onde o gráfico cruza o eixo x. No
nosso caso, será suficiente e escolhemos 51 pontos igualmente espaçados para
avaliar a função. Também exibimos uma grade porque isso facilita a identificação
da estimativa inicial.
ATIVIDADE PRÁTICA: GRÁFICO DA FUNÇÃO -->// MÉTODO GRÁFICO PARA DETERMINAR A ESTIMATIVA INICIAL
-->x=linspace(0,5,51);
-->y=exp(-x)-x;
-->plot(x,y);
-->xgrid O resultado final é:
Examinando o gráfico podemos ver claramente que a raiz está localizada no
intervalo [0.5,1] e, de fato, bem mais próxima de 0.5 do que de 1. Então, tomemos
como estimativa inicial x0=0.5 e, para os métodos que necessitam de duas
estimativas iniciais, consideremos xl=0.5 e xu=0.7
3.3. DETERMINAÇÃO DA RAIZ
Na próxima atividade prática, você terá que digitar, na Console do Scilab, uma
longa sequência de comandos cujo resultado final é a determinação da raiz da
função de teste, através de cada um dos métodos numéricos estudados do ponto
de vista teórico em sala de aula.
Os comentários descrevem brevemente as etapas do cálculo. Inicialmente, você irá
definir, na linguagem do Scilab, a função e sua derivada (requerida por Newton-
Raphson) e, mais adiante, a função modificada (problema de ponto fixo) como
exige o método do Ponto Fixo. Nesta etapa, também são informados os
parâmetros do cálculo: estimativas iniciais, tolerância, número máximo de
iterações. A seguir, cada método (codificado no respectivo arquivo .sci) é
carregado no ambiente e a raiz é, então, calculada. Por último, é gerado um gráfico
com os erros relativos de cada método.
ATIVIDADE PRÁTICA // VÁ PARA A PASTA DE TRABALHO (ONDE ESTÃO AS FUNÇÕES QUE IMPLEMENTAM OS MÉTODOS // NO MENU PRINCIPAL, SELECIONE A SEQUÊNCIA "ARQUIVO -> ALTERAR DIRETÓRIO ATUAL..." // DEFININDO A FUNÇÃO CUJA RAIZ SE QUER DETERMINAR function [y] = expx_menos_x(x) y=exp(-x)-x; endfunction // DEFININDO A DERIVADA DA FUNÇÃO (PARA O MÉTODO DE NEWTON-RAPHSON) function [y] = deriv_expx_menos_x(x) y=-exp(-x)-1; endfunction // ESTIMATIVAS INICIAIS: XL e XU; SE APENAS 1 FOR NECESSARIA, X0 = XL xl = 0.5; xu = 0.7; x0 = xl; // TOLERANCIA (EM PORCENTAGEM) tol = 0.0001; // NUMERO MAXIMO DE ITERAÇÕES
imax = 100; //========================================================= // BISSECÇÃO //========================================================= // CARREGAR FUNÇÃO BISSECÇÃO NO AMBIENTE E CALCULAR exec("bisseccao.sci"); // PARA COMPARAR OS MÉTODOS ENTRE SI, VAMOS DEFINIR NOMES DIFERENTES PARA OS PARAMETROS DE SAIDA [raiz_bisec, x_bisec, iter_bisec, ea_bisec]=bisseccao(xl,xu,expx_menos_x,tol,imax) //========================================================= // FALSA POSICAO //========================================================= // CARREGAR FUNÇÃO FALSAPOSICAO NO AMBIENTE E CALCULAR exec("falsaposicao.sci"); [raiz_falsap, x_falsap, iter_falsap, ea_falsap]=falsaposicao(xl,xu,expx_menos_x,tol,imax) //========================================================= // PONTO FIXO //========================================================= // IMPORTANTE: NAO ESQUECER QUE O PROBLEMA TEM QUE SER REFORMULADO COMO UM PROBLEMA DE // PONTO FIXO. NESTE CASO, É IMEDIATO EXP(-X)-X=0 => X = EXP(-X) // ASSIM DEVEMOS DEFINIR UMA FUNÇÃO DIFERENTE PARA O PONTO FIXO: function [y]=expx_fp(x) y=exp(-x); endfunction // CARREGAR FUNÇÃO PONTOFIXO NO AMBIENTE E CALCULAR exec("pontofixo.sci"); [raiz_pfixo, x_pfixo, iter_pfixo, ea_pfixo]=pontofixo(x0,expx_fp,tol,imax)
//========================================================= // NEWTON-RAPHSON //========================================================= // CARREGAR FUNÇÃO NEWTONRAPHSON NO AMBIENTE E CALCULAR exec("newtonraphson.sci"); [raiz_newton, x_newton, iter_newton, ea_newton]=newtonraphson(x0,expx_menos_x,deriv_expx_menos_x,tol,imax) //========================================================= // SECANTE //========================================================= // CARREGAR FUNÇÃO SECANTE NO AMBIENTE E CALCULAR exec("secante.sci"); [raiz_secante, x_secante, iter_secante, ea_secante]=secante(xl,xu,expx_menos_x,tol,imax) // GRAFICO DOS ERROS RELATIVOS APROXIMADOS [nr nc]=size(ea_bisec); niter=linspace(1,nr,nr); plot(niter,ea_bisec,'ro-'); [nr nc]=size(ea_falsap); niter=linspace(1,nr,nr); plot(niter,ea_falsap,'gx-'); [nr nc]=size(ea_pfixo); niter=linspace(1,nr,nr); plot(niter,ea_pfixo,'bs-'); [nr nc]=size(ea_newton); niter=linspace(1,nr,nr); plot(niter,ea_newton,'cd-'); [nr nc]=size(ea_secante); niter=linspace(1,nr,nr); plot(niter,ea_secante,'vm-'); xlabel("Número de Iterações","fontsize",4,"color","red"); ylabel("Erro Relativo Aproximado","fontsize",4,"color","red"); title("Gráfico de Convergência","color","red","fontsize",4); //xtitle("Gráfico de Convergência f(x)=exp(-x)-x", "Número de Iterações", "Erro Relativo Aproximado"); legend("Bissecção", "Falsa Posição", "Ponto Fixo", "Newton-Raphson", "Secante");
O gráfico de convergência obtido deve ser o da figura abaixo:
ATIVIDADE PRÁTICA Com base no que foi aprendido nas aulas teóricas, interprete o gráfico de
convergência.
Verifique quantas iterações foram necessárias para cada método convergir
construindo uma tabela simples com o comando:
itermatriz=[..
"Bissecção ", msprintf('%d',iter_bisec);
"Falsa Posicao ", msprintf('%d',iter_falsap);
"Ponto Fixo ", msprintf('%d',iter_pfixo);
"Newton-Raphson", msprintf('%d',iter_newton);
"Secante ", msprintf('%d',iter_secante)]
No gráfico obtido, algumas curvas ficam sobrepostas. Use o seguinte comando
para limitar os valores nos eixos x e y:
set(gca(),"data_bounds",matrix([0,8,0,3],2,-1));
(por ora, não discutiremos esse commando, use-o como uma “caixa preta”).
O resultado é:
Agora é possível visualizar que Newton-Raphson converge um pouco mais
rápido, por exemplo.
3.4. USANDO SCRIPTS PARA A DETERMINAÇÃO DA RAIZ
Suponha que você deseje realizar vários estudos variando, por exemplo, os
parâmetros do problema como as estimativas iniciais, a tolerância, etc. Seria um
grande desperdício de tempo e energia digitar todos os comandos novamente.
Esta é uma típica situação em que scripts devem ser usados. Todos os comandos
digitados anteriormente podem ser coletados e armazenados em um arquivo de
script que, como vimos, possui extensão .sce. Para agilizar as atividades práticas,
isso já foi feito previamente e está no arquivo ConvergenciaMetodos.sce.
ATIVIDADE PRÁTICA Com o SciNotes, abra o arquivo ConvergenciaMetodos.sce e verifique que ele contém todos os comandos digitados na Atividade Prática anterior. Usando o script, podemos Como vimos, para executá-lo, basta usar o comando: exec(“ConvergenciaMetodos.sce”); Verifique que o resultado do script é exatamente o mesmo da longa sessão de digitação. A vantagem é que, agora, podemos simplesmente editar o arquivo .sce, alterar qualquer parâmetro e, até mesmo, determinar a raiz de outra equação não linear; basta alterar as definições das funções no início do arquivo.
Com base no procedimento apresentado, você agora pode determinar a raiz de
qualquer equação não linear.
ATIVIDADE PRÁTICA Altere o scrip ConvergenciaMetodos.sce (crie uma cópia) e encontre a raiz da função f(x)= ex-4x2 com erro relativo igual a 0.01%. Antes determine graficamente um intervalo que contenha a raiz.