Prévia do material em texto
1 Universidade Estadual de Santa Cruz Departamento de Engenharias e Computação Engenharia Química Roberta S. Nascimento(202011222). Análise Computacional de Algoritmos para Resolução de Equações Não-Lineares e Sistemas de Equações Lineares Ilhéus - Ba 2025 2 Roberta S. Nascimento (202011222). Análise Computacional de Algoritmos para Resolução de Equações Não-Lineares e Sistemas de Equações Lineares Relatório técnico sobre a implementação e análise de métodos numéricos para resolução de equações não-lineares e sistemas lineares utilizando Linguagem C++. Inclui estudo dos métodos da Bisseção, Newton-Raphson, Secante e Ponto Fixo, além da Eliminação Gaussiana, Decomposição LU e Decomposição de Cholesky, com avaliação de desempenho, convergência e estabilidade numérica, fornecendo critérios para a escolha adequada de cada método. Orientador (a) : Prof. Geizane Lima da Silva. Ilhéus - Ba 3 2025 SUMÁRIO INTRODUÇÃO............................................................................................................. 4 OBJETIVO................................................................................................................... 5 RESULTADOS E DISCUSSÃO................................................................................... 6 CONCLUSÃO............................................................................................................ 12 4 INTRODUÇÃO Os métodos numéricos são ferramentas essenciais para a resolução de problemas matemáticos complexos, especialmente aqueles que não possuem soluções analíticas ou que seriam inviáveis de resolver por métodos tradicionais. Este relatório apresenta uma análise detalhada e comparativa de métodos numéricos importantes, divididos em duas categorias principais: métodos iterativos para equações não-lineares e métodos diretos para sistemas de equações lineares. No estudo das equações não-lineares, foram implementados quatro métodos clássicos. O Método da Bisseção baseia-se no Teorema do Valor Intermediário e utiliza uma busca incremental, dividindo o intervalo ao meio a cada iteração. Apesar de apresentar convergência linear, garante encontrar a raiz se a função for contínua e apresentar troca de sinal no intervalo inicial. O Método de Newton-Raphson destaca-se pela convergência quadrática, utilizando a derivada da função para gerar aproximações sucessivas segundo a relação: 𝑥 𝑛+1 = 𝑥 𝑛 − 𝑓( 𝑥 𝑛 ) 𝑓'( 𝑥 𝑛 ) Exige, contudo, uma boa estimativa inicial e o cálculo da derivada. O Método da Secante surge como uma alternativa eficiente ao método de Newton, aproximando a derivada por diferenças finitas e apresentando convergência superlinear, sem necessidade de cálculo direto da derivada. Já o Método do Ponto Fixo transforma a equação na forma , 𝑓(𝑥) = 0 𝑥 = 𝑔(𝑥) cuja convergência depende diretamente da escolha apropriada da função de iteração . 𝑔(𝑥) Para a solução de sistemas lineares, três métodos fundamentais foram estudados. A Eliminação Gaussiana é um método direto que transforma o sistema original em um sistema triangular equivalente, por meio de operações elementares sobre as 5 linhas, sendo relativamente simples de implementar. A Decomposição LU expande essa ideia ao dividir a matriz do sistema em duas matrizes triangulares, L (lower) e U (upper), sendo vantajosa quando se precisa resolver múltiplos sistemas com a mesma matriz de coeficientes. A Decomposição de Cholesky, por sua vez, é um caso especial para matrizes simétricas positivas definidas, aproveitando sua estrutura para maior eficiência e estabilidade numérica, sendo amplamente utilizada em análise estrutural e métodos de elementos finitos. A linguagem de programação escolhida foi C++. Programar em C++ envolve compreender a estrutura básica da linguagem, que combina simplicidade com grande poder de abstração. Todo programa começa pela inclusão de bibliotecas, como a , responsável pela comunicação de entrada e saída, e pela função principal main(), que é o ponto de partida da execução. Dentro dessa função, o programador pode declarar variáveis de diferentes tipos, como inteiros, reais ou caracteres, utilizar operadores matemáticos e lógicos para realizar cálculos e tomar decisões, além de empregar estruturas de controle como if, switch, for e while para organizar o fluxo lógico do programa. Um dos grandes diferenciais do C++ é a possibilidade de criar funções, que permitem dividir o código em partes menores, facilitando a leitura, a reutilização e a manutenção. Além disso, a linguagem se destaca pelo suporte à programação orientada a objetos, em que o desenvolvedor pode definir classes que agrupam atributos e métodos relacionados, gerando objetos capazes de representar elementos do mundo real dentro do programa. Isso torna possível aplicar conceitos como encapsulamento, herança e polimorfismo, que contribuem para a construção de sistemas mais organizados, robustos e escaláveis. Outro recurso fundamental é a Standard Template Library (STL), que oferece estruturas de dados já prontas, como vetores, filas, pilhas e mapas, além de algoritmos de ordenação e busca, o que otimiza o processo de desenvolvimento. Combinando a eficiência de baixo nível — por meio do controle direto de memória e ponteiros — com a flexibilidade da programação genérica e orientada a objetos, o C++ proporciona ao programador tanto a capacidade de criar aplicações simples, 6 como calculadoras e jogos básicos, quanto sistemas complexos de alta performance, como motores gráficos, softwares científicos e simuladores. OBJETIVO O objetivo deste estudo é implementar esses métodos utilizando a linguagem computacional C++, comparar seu desempenho por meio de métricas objetivas, analisar taxas de convergência e estabilidade numérica, e fornecer critérios técnicos para a escolha adequada do método de acordo com o problema. A avaliação será realizada a partir de estudos de caso representativos, incluindo funções não-lineares típicas e sistemas lineares de média complexidade, com geração sistemática de tabelas de convergência para permitir uma análise quantitativa do desempenho. A compreensão dessas técnicas é fundamental para profissionais de engenharia, matemática aplicada e ciências computacionais, que precisam resolver problemas numéricos complexos de forma eficiente, precisa e confiável. RESULTADOS E DISCUSSÃO 1. ANÁLISE DA FUNÇÃO , 𝑓(𝑥) = 𝑥3 + 𝑥 − 1 ε = 10−6 7 Objetivo: Implementar e comparar quatro métodos numéricos (Bisseção, Newton-Raphson, Secante e Ponto Fixo) para encontrar a raiz da função , . Gerar tabelas de convergência detalhadas e realizar 𝑓(𝑥) = 𝑥3 + 𝑥 − 1 ε = 10−6 análise comparativa do desempenho, considerando número de iterações, velocidade de convergência e estabilidade numérica de cada método. Tabela 1: Método da Bisseção. Tabela 1.2: Método de Newton. 8 Tabela 1.3: Método da Secante. Tabela 1.4: Comparação de Convergência dos Métodos Entre os métodos analisados, Newton-Raphson destacou-se pela maior eficiência, alcançando a solução em 4 iterações com precisão de . A Secante surgiu como 10−16 boa alternativa, convergindo em 6 iterações sem exigir derivadas. Já o Ponto Fixo e a Bisseção foram os mais lentos, necessitando de 12 e 20 iterações, respectivamente, refletindo sua convergência linear. Assim, confirma-se a hierarquia: Newton (quadrática), Secante (superlinear) e, por último, Ponto Fixo e Bisseção (lineares). 9 2. ANÁLISE DA FUNÇÃO , 𝑓(𝑥) = 𝑥 − 𝑒𝑥 − 4 ε = 10−5Objetivo: Implementar os métodos da Bisseção, Newton-Raphson e Ponto Fixo, determinando o número de iterações necessárias e investigando casos de divergência. Comparar a rapidez de convergência entre os métodos e implementar mecanismos de detecção de divergência. Tabela 2: Método da Bisseção. 10 Tabela 2.1: Método de Newton Tabela 2.2: Comparação entre os métodos. O método de Newton demonstrou superioridade significativa, exigindo apenas 6 iterações contra 17 da Bisseção. O método do Ponto Fixo mostrou divergência completa devido à incompatibilidade fundamental entre as funções de iteração naturais e a localização da raiz negativa, resultando em crescimento exponencial dos valores e overflow numérico já nas primeiras iterações." 3. ANÁLISE DO SISTEMA. Objetivo: Resolver um sistema linear de 7 equações e 7 incógnitas utilizando dois métodos diretos: Eliminação Gaussiana e Decomposição LU. Implementar ambos os algoritmos, comparar os resultados obtidos e analisar as vantagens de cada abordagem. Validar a solução através da concordância entre os métodos e discutir a aplicabilidade de cada técnica para sistemas lineares de média dimensão. Tabela 3: Matriz do Sistema. 11 Tabela 3.2: Vetor de Termos independentes. Tabela 3.3: Comparação das Soluções Os dois métodos analisados apresentaram resultados idênticos dentro da precisão de ponto flutuante adotada (6 casas decimais). A diferença absoluta encontrada 12 entre as soluções foi nula, o que confirma que, do ponto de vista matemático, ambos são equivalentes quando considerados sob aritmética de precisão infinita. Isso mostra que, em termos práticos, a escolha entre um e outro não afeta o valor final obtido para este sistema específico. No que diz respeito à estabilidade numérica, observou-se que tanto a Eliminação Gaussiana quanto a Decomposição LU são suscetíveis a erros de arredondamento. A Eliminação Gaussiana, quando aplicada sem pivotamento, pode sofrer maior propagação desses erros ao longo das etapas de eliminação. A Decomposição LU, apesar de também ser sensível, tende a oferecer um controle numérico ligeiramente melhor devido à sua estrutura de fatoração, que organiza o processo em etapas mais estáveis. Um aspecto importante a ser considerado é o pivotamento. Neste caso, não foi necessário implementá-lo porque os elementos da diagonal principal não apresentaram valores nulos ou muito pequenos e a matriz analisada não mostrou sinais de mal-condicionamento. Dessa forma, a resolução pôde ser conduzida sem comprometer a estabilidade do processo. 4. DECOMPOSIÇÃO CHOLESKY. Resolver um problema de análise estrutural representado pelo sistema KU=F, onde K é a matriz de rigidez global e F o vetor de forças nodais. Verificar as propriedades de simetria e positiva definição da matriz K, implementar a decomposição de Cholesky e determinar os deslocamentos nodais U. Validar a solução obtida e interpretar fisicamente os resultados, analisando a consistência numérica e a aplicação do método em problemas de engenharia estrutural. Tabela 4: Matriz L (Cholesky). Tabela 4.2:Solução do Sistema KU = F. 13 A análise do sistema usando a Decomposição de Cholesky mostrou que o método funciona bem tanto do ponto de vista numérico quanto físico. A matriz de rigidez 𝑘 manteve suas propriedades essenciais, como simetria e positividade, o que garante que os resultados fazem sentido. Os deslocamentos calculados ficaram na faixa de metros, compatíveis com as 10−5 cargas aplicadas. O nó 3 teve o maior deslocamento horizontal, enquanto todos os deslocamentos verticais foram positivos, mostrando que a estrutura tende a se elevar levemente. O resíduo numérico de confirma que os cálculos foram 10−12 precisos e estáveis. Além disso, a Cholesky foi eficiente, exigindo cerca de metade das operações da decomposição LU e sem precisar de pivotamento. Os resultados mostram que a estrutura é rígida e segura para as cargas aplicadas, e o método é confiável para análises mais complexas de engenharia estrutural. CONCLUSÕES Este trabalho permitiu analisar e comparar diferentes métodos numéricos, mostrando suas vantagens e limitações em contextos variados. A aplicação prática dos algoritmos possibilitou entender melhor a eficácia de cada método em problemas matemáticos e de engenharia. 14 Na resolução de equações não-lineares (Questões 1 e 2), ficou claro que a escolha do método precisa levar em conta as características da função. O Newton-Raphson se destacou pela rapidez e precisão, atingindo convergência quadrática, mas depende de uma boa estimativa inicial e do cálculo da derivada. O Método da Bisseção, embora mais lenta, se mostrou confiável e sempre convergente. Já o Ponto Fixo mostrou-se muito sensível à função de iteração, podendo levar a divergência, como foi visto na Questão 2. Para sistemas lineares (Questão 3), a comparação entre Eliminação Gaussiana e Decomposição LU mostrou que ambos dão resultados consistentes. A escolha entre eles depende do contexto: a Gaussiana é mais simples de implementar para sistemas únicos, enquanto a LU é mais eficiente quando é necessário resolver várias vezes o mesmo sistema. No problema de análise estrutural (Questão 4), o Método da Decomposição de Cholesky se mostrou eficiente e também respeitou as propriedades físicas da estrutura. A simetria e a positividade da matriz de rigidez foram confirmadas, e os deslocamentos calculados estavam coerentes com a física do problema, validando a modelagem e a solução numérica. De forma geral, o trabalho mostrou que não existe um método melhor para todos os casos, mas sim escolhas adequadas para cada situação. O sucesso depende do entendimento teórico, da análise das propriedades do problema e de uma implementação cuidadosa, levando em conta estabilidade e precisão. Os resultados obtidos servem como referência para escolher métodos numéricos em problemas futuros de engenharia e ciências computacionais, reforçando a importância do pensamento crítico na aplicação dessas ferramentas. 15 CÓDIGO DA QUESTÃO 1: #include #include #include using namespace std; double f(double x) { return x*x*x + x - 1; } double df(double x) { 16 return 3*x*x + 1; } // Método da Bisseção void bissecao(double a, double b, double epsilon) { cout epsilon && iter epsilon && iter"Raiz aproximada: " epsilon && iter epsilon && iter #include #include 22 using namespace std; double f2(double x) { return x - exp(x) - 4; } double df2(double x) { return 1 - exp(x); } // Função original que causa divergência double g2_divergente(double x) { return exp(x) + 4; } // Tentativa alternativa (também problemática) double g2_alternativa(double x) { return (x + exp(x) + 4) / 2; } void bissecao_questao2(double a, double b, double epsilon) { 23 cout = 0) { cout epsilon && iter epsilon && iter = 2 && erro > 1e10) { cout = 5 || x > 1e50) { divergencia_detectada = true; break; } } while (erro > epsilon && iter 1" = 2 && erro > 1e10) { cout = 5 || x > 1e50) { divergencia_detectada = true; break;} } while (erro > epsilon && iter 1" > 1) → CONDIÇÃO VIOLADA" 1 na trajetória do método" #include #include #include using namespace std; class SistemaLinear { private: vector> A; vector b; int n; public: SistemaLinear(const vector>& matriz, const vector& vetor) { A = matriz; 34 b = vetor; n = matriz.size(); } // Eliminação de Gauss vector eliminacaoGauss() { vector> Ab = A; vector b_copy = b; // Eliminação progressiva for (int k = 0; k x(n); for (int i = n-1; i >= 0; i--) { x[i] = b_copy[i]; for (int j = i+1; j >, vector>> decomposicaoLU() { vector> L(n, vector(n, 0)); vector> U = A; for (int i = 0; i resolverLU() { auto [L, U] = decomposicaoLU(); // Resolver Ly = b vector y(n); for (int i = 0; i x(n); for (int i = n-1; i >= 0; i--) { x[i] = y[i]; for (int j = i+1; j & x, const string& metodo) { cout > A = { {3, -9, 8, 6, 9, 4, -1}, {-9, 3, 8, 9, -12, 6, 3}, {1, -9, 2, -3, 1, -5, 5}, {4, 8, -10, 8, -1, 4, -4}, {-5, 5, 4, 11, 3, 8, 7}, {6, -2, 9, -7, -5, -3, 8}, {8, 7, 2, 5, 2, 1, -3} }; vector b = {-0.108, 26.24, 92.808, 53.91, 143.55, -6.048, 137.94}; SistemaLinear sistema(A, b); auto sol_gauss = sistema.eliminacaoGauss(); 39 sistema.imprimirSolucao(sol_gauss, "ELIMINAÇÃO GAUSSIANA"); auto sol_lu = sistema.resolverLU(); sistema.imprimirSolucao(sol_lu, "DECOMPOSIÇÃO LU"); return 0; } ………………………………………………………………………………………………….. CÓDIGO DA QUESTÃO 4: #include #include #include #include using namespace std; class CholeskySolver { private: vector> A; vector b; 40 int n; public: CholeskySolver(const vector>& matriz, const vector& vetor) { A = matriz; b = vetor; n = matriz.size(); } bool isSimetrica() { for (int i = 0; i 1e-10) { return false; } } } return true; } bool isPositivaDefinida() { 41 // Verificação simplificada - todos os autovalores positivos // Para uma verificação mais rigorosa, usar decomposição auto L = decomposicaoCholesky(); if (L.empty()) return false; for (int i = 0; i > decomposicaoCholesky() { vector> L(n, vector(n, 0)); for (int i = 0; i} } } return L; } vector resolver() { auto L = decomposicaoCholesky(); if (L.empty()) return {}; 43 // Resolver Ly = b vector y(n); for (int i = 0; i x(n); for (int i = n-1; i >= 0; i--) { x[i] = y[i]; for (int j = i+1; j >& mat, const string& nome) { cout & x) { cout > K = { {7.1111e8, 0, -1.7778e8, 5.3333e8, 0, 0}, {0, 2.1333e9, -5.3333e8, 5.3333e8, 0, 0}, {-1.7778e8, -5.3333e8, 7.1111e8, 0, -1.7778e8, 5.3333e8}, {5.3333e8, 5.3333e8, 0, 2.1333e9, -5.3333e8, 5.3333e8}, {0, 0, -1.7778e8, -5.3333e8, 3.5556e8, -5.3333e8}, {0, 0, 5.3333e8, 5.3333e8, -5.3333e8, 1.0667e9} }; vector F = {0, 0, -10000, 0, -5000, 0}; CholeskySolver solver(K, F); cout