Veja neste artigo como desenvolver, em C#, um programa que utiliza o chamado “Método da Bissecção” para encontrar a raíz aproximada de uma função que geralmente necessitaria de um pouco mais de trabalho para ser resolvida da forma convencional, isolando variáveis e realizando operações aritméticas simples.

De fato este artigo é um pouco diferente dos demais existentes no portal no que se refere ao tema. Resolução de equações não é algo comumente visto no meio de programação, banco de dados ou semelhantes. Este tema é mais comum a estudantes e profissionais de engenharia e áreas das ciências exatas. Porém, não quer dizer que não possa chegar ao conhecimento de todos, não é mesmo? E por isso estou compartilhando com vocês.

Suponha que você é um engenheiro eletricista e se depara com um circuito elétrico cuja corrente i é dada por i = 9e^(-t)cos(2̃πt), onde t é dado em segundos. Você precisa determinar em que instante a corrente no circuito será igual a 3 amperes.

Primeiro vamos organizar as equações, se queremos i=3, temos:

9e^(-t)cos(2̃πt) = 3 à 9e^(-t)cos(2̃πt) - 3 = 0

Essa será nossa função f, da qual buscaremos encontrar a raíz:

f(t) = 9e^(-t)cos(2̃πt) - 3

Para termos uma noção de quanto vale t quando i=3, vamos plotar o gráfico da função. Como falamos em tempo, avaliaremos apenas instantes em que t>=0 (no caso, usaremos t de 0s a 1s) e obteremos os seguinte gráfico:

Gráfico da função

Figura 1. Gráfico da função 9e^(-t)cos(2̃πt) - 3

Observamos que o primeiro instante t em que i=3 está entre 0.1s e 0.2s, mas não temos um valor mais preciso. É aqui que entra o método numérico da bissecção.

Este método tem esse nome por que ele se baseia em intervalos para encontrar uma raiz aproximada de forma iterativa e, a cada iteração, ele secciona o intervalo ao meio, obtendo dois intervalos menores.

Após seccionar o intervalo original, verificamos se a raiz está no primeiro subintervalo ou no segundo. Para fazer essa verificação, analisamos o valor da função no início do intervalo [a,b] e no fim. Se f(a)*f (b)<0 indica que a está antes da raíz e b está depois da raíz, ou seja, a raíz está no intervalo.

Inicialmente, o valor que definimos para a raíz R é o valor médio do intervalo [a,b], ou seja, (a+b)/2. Se esse valor satisfaz nossa necessidade, paramos, senão, continuamos até obter ruma maior precisão.

O algoritmo do método é o seguinte:

Listagem 1: algoritmo do método da bissecção


ENTRADA: Inicio, Fim: reais; Precisão, Iterações: inteiros;
VARIÁVEIS: Raiz, Erro, Temp: real; 
INÍCIO
	Erro = 1; //Definimos um erro muito grande para iniciar as iterações
	Cont = 0;
	ENQUANTO Erro > Precisão E Cont < Iterações 
          //Enquanto não obtivermos a precisão desejada ou enquanto não  
          //atingirmos um número máximo de iteações
		Temp = Raiz; //Armazenamos o valor anteriormente encontrado	
		Raiz = (Inicio + Fim)/2;
		Erro = ABS((Raiz - Temp)/Raiz);//Usamos o módulo (valor ABSoluto);
		SE f(Inicio)*f(Raiz) < 0 
         //Se essa condição for verdadeira, implica dizer que a raiz está entre 
         //Inicio e Raiz (que é a metade do intervalo), o novo intervalo passa  
         //a ser [Inicio, Raiz] e dentro dele faremos novamente a verificação
			Fim = Raiz;
		SENÃO
         //Senão, a raiz está no segundo subintervalo, e o intervalo passa a ser 
         //[Raiz, Fim] e dentro dele executaremos o processo novamente
			Início = Raiz;
		FIM_SE
		Cont = Cont + 1;
          //Esse contador é utilizado para fazer um controle do método. 
          //Se dentro de um número máximo de Iterações, não se obtiver um 
          //valor satisfatório, paramos
FIM_ENQUANTO
FIM

Agora vamos ao C#. Primeiramente definiremos nossa função f como já foi apresentado:

Listagem 2: função f da qual se busca a raiz


private double f(int t)
       	{
       		return 9 * Math.Exp(-1 * t) * Math.Cos(2 * Math.PI * t) - 3;
       	}

E então implementamos o método da bissecção:

Listagem 3: implementação do método da bissecção.


private double Bisseccao(double inicio, double fim, double precisao, int iteracoes)
        {
            double Erro = 1;
            int Cont = 0;
            double Raiz = inicio;
            double Temp;
            while (Erro > precisao && Cont < iteracoes)
            {
                Temp = Raiz;
                Raiz = (inicio + fim) / 2;
                Erro = Math.Abs((Raiz - Temp) / Raiz);
                if (f(inicio) * f(Raiz) < 0)
                    fim = Raiz;
                else
                    inicio = Raiz;
                Cont++;
            }
            return Raiz;
        }

Como conhecemos um intervalo inicial , podemos usar o método:

Listagem 4: utilizando o método da bissecção


double raiz = Bisseccao(0.1, 0.2, 0.001, 100);

No exemplo, utilizamos o intervalo inicial que encontramos observando o gráfico, [0.1, 0.2], e definimos uma precisão de 0.001, ou seja, não aceitamos um erro superior a isso. Determinamos, ainda, que o número máximo de iterações é 100.

Se executarmos os códigos acima, encontraremos uma raiz aproximadamente 0.1842, mas de fato esse valor não é exato. O C#, bem como outras linguagens de alto nível, não possui uma boa precisão para estes cálculos.

Vale lembrar, ainda, que a função f pode ser modificada, apenas utilizei um exemplo que achei interessante. E para encontrar o intervalo inicial, podemos plotar o gráfico e observar onde a curva intercepta o eixo X.

Bom, esse é apenas um dos métodos numéricos utilizados e é também o mais simples deles. Por hora ficaremos por aqui, espero que gostem.

Até a próxima.