Atividade Prática 11: Encontrando a raiz de uma funç˜ao com busca

Propaganda
Atividade Prática 11: Encontrando a raiz de uma
função com busca binária.
Algoritmos e Programação de Computadores
Raoni F. S. Teixeira
Introdução
Nesta aula, vamos aprender como utilizar o algoritmo de busca binária estudado em sala para encontrar a raiz de uma equação. As atividades desta aula
vão estimular sua intuição sobre o processo de solução numérica que pode ser
aplicado em uma variedade de problemas de engenharia.
Objetivos
Ao fim do exercı́cio, você será capaz de:
• Identificar a estrutura repetitiva do problema de busca de raı́zes;
• Construir códigos que encontram soluções numéricas e
• Testar programas numéricos.
Arquivos incluı́dos nesta atividade
• raizes.c: disponı́vel em: ieng.ufmt.br/algoritmos/code/raizes.c
Este arquivo será utilizado na primeira parte da atividade.
1
Busca binária
Busca binária é um algoritmo eficiente para o problema de busca em um vetor ordenado. Mais especificamente, dado um inteiro x e um vetor crescente
v[0..n-1] de inteiros, o algoritmo de busca binária encontra um ı́ndice m tal
que v[m] == x. Note que se x não está v[0..n-1] então a solução para o
problema é um ı́ndice inválido (−1 por exemplo).
1
O algoritmo de busca binária é baseado no mesmo princı́pio que te ajuda a
buscar um nome em uma lista telefônica ou dicionário 1 . O algoritmo realiza
sucessivas divisões do espaço de busca comparando o elemento buscado (x) com
o elemento no meio do vetor (v[m]). Se v[m] == x, então a busca termina. Caso
contrário, como v[0..n-1] está em ordem crescente, podemos sempre ignorar
metade dos elementos do vetor. Se x > v[m], então a busca continua na metade
posterior do vetor (v[m+1..n-1]). E finalmente, se x < v[m] , então a busca
continua na metade anterior do vetor(v[0...m-1]).
Em C, tal como vimos na aula isto pode ser feito da seguinte forma:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
ini = 0;
fim = n - 1;
r
= -1;
while ( ini <= fim ) {
meio = ( ini + fim )/2;
if ( A [ meio ] == x ) {
r = meio ;
break ;
} else if ( x < A [ meio ])
fim = meio - 1;
else
ini = meio + 1;
}
/* neste ponto r e a r e s p o s t a da busca */
...
Copie e cole este algoritmo no Code::Blocks e teste o código com vários
exemplos.
2
Roots Bloody Roots
Encontrar as raizes de uma função f é um problema recorrente em engenharia
e com certeza se você ainda não topou topará com ele alguma vezes na sua
carreira! Talvez você fique um pouco surpreso mas o algoritmo de busca binária
apresentado na seção anterior pode ser utilizado para resolver este problema
também :)
Como você deve saber, a raiz de um função f , f : R 7→ R2 , é um ponto x,
x ∈ R, para o qual f (x) é igual a 0. Assim, por exemplo, o ponto x = 5 é a raiz
da função f (x) = (x − 5)3 , porque f (x) vale 0 quando x é igual a 5 (n.b. que
(5 − 5)3 = 0). Como outro exemplo, considere a função f (x) = x2 − 4, ilustrada
na Figura 1, que tem duas raizes: 2 e −2.
1 É...
eu sei... você não usa lista telefônica :)
2
4
2
−4
−2
2
4
−2
−4
Figura 1: Gráfico da função f (x) = x2 − 4 com duas raizes.
Encontrar as raizes de uma função f é portanto encontrar (buscar) o ponto
x em que f (x) é igual a zero. A pergunta que talvez você esteja se fazendo
agora é: Como podemos usar a busca binária para encontrar a raiz da função?
É... Como?
Com um pouco de atenção, podemos notar que o o segredo do algoritmo de
busca binária é que a cada iteração o espaço de busca é reduzido pela metade.
Como podemos fazer o mesmo para o problema das raizes?
Para responder esta pergunta vamos considerar a função f (x) = x2 − 4.
Agora que já escolhemos uma função, podemos pensar em como definir o espaço
de busca. Como a função escolhida é unidimensional o espaço de busca só pode
ser um intervalo [ini, fim] 2 .
Mas como escolher os valores deste intervalo? Esta é fácil: precisamos escolher o intervalo onde há ao menos uma raiz! Olhando a Figura 1 podemos
obervar, por exemplo, que há uma raiz no intervalo [0, 3].
Como regra você pode
assumir que, se a função
f é contı́nua e f (ini) é
De uma maneira geral, sempre haverá uma raiz
negativa e f (fim) é pono intervalo se a função f for contı́nua e os
sitiva, então existe uma
sinais de f (ini) e f (fim) forem opostos (um
raiz no intervalo [ini, fim].
positivo e outro negativo).
Para confirmar esta regra podemos verificar que
f (x) = x2 − 4 é contı́nua
e que f (0) é igual a −4 (negativo) e f (3) é igual a 5 (positivo).
2 Mais
formalmete, temos que [ini, fim] = {x ∈ R, tal que ini ≤ x ≤ fim}
3
Agora que já definimos o intervalo, podemos pensar em como implementar
a busca binária. A primeira coisa a ser feita é calcular o meio do intervalo. Não
é difı́cil perceber que isto pode ser feito da seguinte maneira:
m = (ini + ini)/2;
(1)
Outro passo importante é descobrir se f (m) é uma raiz de f . Para fazer
isto, devemos verificar se f (m) é bem próximo de zero. Como há erros de representação (veja o guia de computação numérica da IBM), quase nunca dois
números reais vão ser exatamente iguais. Podemos fazer isto isto verificando se
fabs(m*m-2) < 0.01, por exemplo.
Para terminar, precisamos definir como reduzir o espaço de busca (i.e., reduzir o intervalo). Podemos notar que se sinal de f (m) é igual ao sinal de f (fim),
então pela discussão anterior há ao menos uma raiz entre ini e m e todos valores
entre m e fim podem ser ignorados, Podemos aplicar o mesmo raciocı́nio para
o inı́cio do intervalo e perceber que se sinal de f (m) é igual ao sinal de f (ini),
então há ao menos uma raiz entre m e fim.
O código a seguir implementa esta ideia. Complete os balões para que o
programa calcule a raiz da função f (x) = x2 − 4. Um versão online deste código
está disponı́vel em: ieng.ufmt.br/algoritmos/code/raizes.c
1 include <stdio.h>
2 ...
3 int main() {
4
float ini = 0, fim = 3,
5
r;
while(
m = (ini+fim)/2;
if(fabs(m*m-4) < 0.01)
r = m;
break;
}
6
7
8
9
10
11
12
13
//Condiç~
ao de parada
{
if (sig(fim*fim-4) == sig(m*m-4))
fim=m;
else
14
15
16
17
18 }
) {
}
//sinal f(fim) == sinal de f(m)
return 0;
4
O comando sig(z) devolve o sinal de z. Pronto. Você escreveu o seu
primeiro programa numérico! Teste o programa.
3
Desafio
Escreva
um programa em C que lê um número inteiro n e imprime o resultado
√
de n sem utilizar a biblioteca math.h.
√
Antes de escrever o código pense em como você faria para calcular n? Qual
seria a raiz de função? Qual intervalo você utilizaria?
5
Download