Você está na página 1de 9

UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL

INSTITUTO DE MATEMÁTICA
DEPARTAMENTO DE MATEMÁTICA PURA E APLICADA
MAT01092 – ALGORITMOS E PROGRAMAÇÃO I
PROF. RESPONSÁVEL: RUDNEI DIAS DA CUNHA

SOLUÇÃO COMENTADA DA LISTA DE EXERCÍCIOS Nº 1 – ALGORITMOS

1. Escreva um algoritmo que avalie um polinômio, pn(x)=anxn+an-1xn-1+ ... +a1x+a0, usando o


método da multiplicação aninhada (fórmula de Horner), pn(x)= a0+x(a1+x(a2+x(...))).

Armazenando os coeficientes num arranjo “a”, podemos escrever o algoritmo da


seguinte forma:

Horner(n,a,x,p):
1. p = a(n)
2. para i=n,...,1 faça
3. p = a(i-1)+x*p
4. fim para

Considere o polinômio p4(x)=5x4-3x3+10x2-6x+2; como n=4, o arranjo “a” conterá os


valores: a(0)=2; a(1)=-6; a(2)=10; a(3)=-3; a(4)=5. Simulando a execução do algoritmo
acima, temos:

i p
a(4)=5
4 a(3)+x*5=-3+5*x
3 a(2)+x*(-3+5*x)=10+x*(-3+5*x)
2 a(1)+x*(10+x*(-3+5*x)=-6+x*(10+x*(-3+5*x))
1 a(0)+x*(-6+x*(10+x*(-3+5*x)))=2+x*(-6+x*(10+x*(-3+5*x)))

O resultado final é p=2+x*(-6+x*(10+x*(-3+5*x))). Expandindo e rearranjando os


2 3 4
termos, obtemos p=2-6*x+10*x -3*x +5*x , idêntico ao polinômio p4(x).

Note que, se passarmos um valor como x, o algoritmo acima irá avaliar o polinômio em
x, sem calcular qualquer potência de x. Essa é a grande vantagem do método das
multiplicações aninhadas: não só se economiza em termos de operações de ponto
flutuante, como também a qualidade numérica da avaliação é melhor.

_____________________________________________________________________________________
Correção Lista 1 - Algoritmos Página 1 09/06/2014
2. Escreva um algoritmo para calcular os polinômios lj(x), os quais formam a base de Lagrange,
usados no problema de interpolação polinomial de pontos (x0,y0), (x1,y1), ...,(xn,yn):
x  xi
l j ( x)  x
0  i  n j  xi
. Esses polinômios são usados para expressar o polinômio interpolador,

i j
pn(x)= y0l0(x)+y1l1(x)+...+ynln(x).

O algoritmo pode ser expresso como:

Lagrange(n,x,j,l):
1. l = 1
2. para i=0,...,n
3. se i/=j então
4. l = l*(x-x(i))/(x(j)-x(i))
5. fim se
6. fim para

Para mostrar o funcionamento do algoritmo acima, considere apenas dois pontos,


(x0,y0)=(10,10) e (x1,y1)=(20,20). Nesse caso, n=1. Calculando o polinômio lj(x) para j=0,
temos:

j i l ação
1
0 0 1 Não faz nada, pois i==j
1 1*(x-x(1))/(x(0)-x(1))=(x-20)/(10-20)=2-x/10

Agora, calculando o polinômio lj(x) para j=1, temos:

j i l ação
1
1 0 1*(x-x(0))/(x(1)-x(0))=(x-10)/(20-10)=x/10-1
1 1*(x-x(0))/(x(1)-x(0))=(x-10)/(20-10)=x/10-1 Não faz nada, pois i==j

Com isso, podemos escrever o polinômio interpolador como pn(x)= y0l0(x)+y1l1(x)=10(2-


x/10)+20(x/10-1)=x, o que evidentemente está correto, já que o polinômio que passa por
aqueles dois pontos é a reta y=x.

3. Escreva um algoritmo para calcular a(s) solução(ões) de uma equação de 2º grau, escrita na
forma x2  b1x  c1  0 . Considere todas as situações possíveis.

Raízes_eq2(b1,c1,x1,x2):
1. se b1==0 E c1==0 então
2. x1 = 0 ; x2 = 0
3. senão se c1==0 então
4. x1 = 0 ; x2 = -b1
5. senão se b1==0 então
6. x1 = sqrt(-c1) ; x2 = -x1
7. senão
8. x1 = -(b1)/2-sqrt((b1)^2-4*c1)/2 ; x2 = -(b1)/2+sqrt((b1)^2-4*c1)/2
9. fim se

_____________________________________________________________________________________
Correção Lista 1 - Algoritmos Página 2 09/06/2014
4. Sabendo que a área situada entre a curva de uma função f(x) e o eixo x pode ser aproximada num
intervalo [a, b] através de um trapézio, escreva um algoritmo que, tendo recebido como
argumentos a função e os extremos do intervalo, calcule tal área.

Uma maneira de calcular tal área é considerar a área trapézio como sendo a soma da
área de um retângulo e de um triângulo eqüilátero, como mostrado no algoritmo abaixo:

Aproxima_integral(a,b,f,I):
1. f_a = f(a) ; f_b = f(b)
2. d = abs(b-a)
3. se f_a>f_b então
4. I = d*f_b+d*(f_a-f_b)/2
5. senão
6. I = d*f_a+d*(f_b-f_a)/2
7. fim se

8
 (x  4)dx , o algoritmo acima devolveria o
2
Por exemplo, para a integral definida
4
valor de I como sendo 176. Calculando a integral, obtem-se o valor 165,3333, o que indica
que a aproximação pelo algoritmo acima é razoável (verifique fazendo o gráfico da função
no intervalo de integração e desenhando o trapézio).

5. Escreva um algoritmo que permita gerar os pontos ao longo de uma circunferência de raio r,
( xi , yi )  r cos i , r sin i , i  1,2,, n . O ângulo  i correspondente a cada ponto deve ser
calculado em função de um incremento angular, determinado em função da quantidade de pontos
que se deseja obter.

O algoritmo pode ser expresso como:

Gera_pontos_circulo(n,r,x,y):
1. pi = acos(-1) ! calcula o valor de Pi como arco-coseno(-1)
2. d_theta = 2*pi/n
3. theta = 0
4. para i=1,...,n faça
5. x(i) = r*cos(theta) ; y(i) = r*sin(theta)
6. theta = theta+d_theta
7. fim para

Suponha que desejamos calcular apenas n=5 pontos no círculo de raio r=10. Observe
que o valor calculado para d_theta é 1,25663706144 rad (o que equivale a
aproximadamente 72°). O algoritmo procederá da seguinte forma:

i theta x(i) y(i) theta'


1 0 10,0000 0,0000 1,2566
2 1,2566 3,0901 9,5105 2,5132
3 2,5132 -8,0901 5,8778 3,7699
4 3,7699 -8,0901 -5,8778 5,0265
5 5,0265 3,0901 -9,5105 6,2831

Note a simetria dos valores x(i) e y(i) em torno da origem (0,0).

_____________________________________________________________________________________
Correção Lista 1 - Algoritmos Página 3 09/06/2014
6. O algoritmo de pesquisa binária permite localizar rapidamente uma chave num arranjo. Escreva
um algoritmo que, dado um intervalo [a, b] e uma função f(x), determine o valor mínimo de f no
intervalo, usando a mesma ideia do algoritmo de pesquisa binária. O processo de busca do valor
mínimo deverá ser terminado quando |b-a|<tol, onde tol<<1. Determine, ainda, quantas iterações
deverão ser realizadas, no mínimo, para se localizar o valor mínimo da função, em termos do
intervalo inicial e da tolerância tol.

O processo de busca é o seguinte: a cada iteração, calcula-se o ponto médio do


intervalo de busca, m, e o valor da função em m. Após, comparam-se os valores da
função, avaliada nos extremos do intervalo de busca, a e b. Se f(a)<f(b), então armazena-
se m em b; senão, armazena-se m em a. Repete-se a iteração até que o intervalo seja
suficientemente pequeno, ou seja excedido um número máximo de iterações. O algoritmo
pode ser expresso como:

Valor_minimo(a,b,f,tol,k_max,m):
1. f_a = f(a) ; f_b = f_b
2. k = 0
3. repita
4. k = k+1
5. m = a+(b-a)/2
6. f_m = f(m)
7. se f_a<f_b então
8. b = m ; f_b = f_m
9. senão
10. a = m ; f_a = f_m
11. fim se
12. até abs(b-a)<tol OU k>=k_max
13. m = a+(b-a)/2
2 -5
Por exemplo, para f(x)=x -4x+8 e a=0, b=7, tol=10 e k_max=100, as primeiras 4
iterações do algoritmo são:

k a f_a b f_b m f_m a' f_a' b' f_b'


1 0 8 7 29 3,5 6,25 3,5 6,25
2 0 8 3,5 6,25 1,75 4,0625 1,75 4,0625
3 1,75 4,0625 3,5 6,25 2,625 4,390625 2,625 4,390625
4 1,75 4,0625 2,625 4,390625 2,1875 4,03516 2,1875 4,03516

Após 20 iterações, o algoritmo obterá m=2,00036 e f_m=4,00000. Observe que se


calculássemos a derivada f'(x) e obtivéssemos a raiz de f'(x)=0, obteríamos o ponto de mínimo
em x=2. A vantagem é que muitas vezes é custoso calcular a derivada de uma função, e o
processo acima não utiliza a derivada.
Para determinar quantas iterações, no mínimo, serão realizadas, observe que, a cada
iteração, o intervalo de busca tem a metade do comprimento do intervalo de busca na iteração
anterior. Com isso, podemos dizer que, na iteração K, o comprimento do intervalo de busca é
|ba| |ba|
K
. Como o algoritmo para quando  tol , podemos calcular o valor de K quando
2 2K
|ba|
 tol ; na iteração K+1, então a desigualdade será satisfeita. O valor de K é dado,
2K
 log r  |ba|
nesse caso, por K    ,r (observe que tomamos a parte inteira da razão
 log 2  tol
log r
, já que K é um número inteiro). No exemplo acima, obteríamos K=19, o que confere com
log 2
o resultado obtido.

_____________________________________________________________________________________
Correção Lista 1 - Algoritmos Página 4 09/06/2014
7. Escreva um algoritmo para calcular os primeiros 10 termos da sequência de Fibonacci, definida
por Fn=Fn-1+Fn-2, n=2, 3, ..., 10; F1 = 1, F0 = 0. Mostre, ao testar o algoritmo, que a razão Fn/Fn-1
tende a aproximar a razão áurea, φ=(1+√5)/2, à medida que n cresce.

O algoritmo pode ser implementado como segue:

Sequencia_Fibonacci(F,phi):
1. F(0) = 0 ; F(1) = 1
2. para i=2,...,10 faça
3. F(i) = F(i-1)+F(i-2)
4. phi = F(i)/F(i-1)
5. fim para

Ao executá-lo, obtemos os seguintes resultados:

i F(i-2) F(i-1) F(i) F(i)/F(i-1)


2 0 1 1 1
3 1 1 2 2
4 1 2 3 1,5
5 2 3 5 1,6666
6 3 5 8 1,6
7 5 8 13 1,625
8 8 13 21 1,61538461538
9 13 21 34 1,61904761905
10 21 34 55 1,61764705082

O valor aproximado de φ é 1,61803398875; observe que as últimas três razões


calculadas tem duas casas decimais corretas.

8. Escreva um algoritmo que determine a frequência das letras num texto, construindo uma tabela
de frequências. Suponha que o texto encontra-se armazenado numa matriz, contendo n linhas e
80 colunas.

O algoritmo é bastante simples; o único detalhe é que utiliza-se uma função


“numero_letra(c)” que devolve um número associado à uma letra do alfabeto: A=1, B=2, ...,
Z=26. O algoritmo pode ser expresso como:

Frequencia_letras(texto,tabela_frequencia):
1. para i=1,...,26 faça
2. tabela_frequencia(i) = 0
3. fim para
4. para i=1,...,n faça
5. para j=1,...,80 faça
6. k = numero_letra(texto(i,j))
7. tabela_frequencia(k) = tabela_frequencia(k)+1
8. fim para j
9. fim para i

_____________________________________________________________________________________
Correção Lista 1 - Algoritmos Página 5 09/06/2014
9. Sabendo que as letras e, a, r são as que em média mais aparecem num texto em língua inglesa,
nas proporções 50, 40, 30 (em relação à letra q, a menos frequente), escreva um algoritmo que
utilizando a tabela de frequência calculada no exercício anterior, verifique se as frequências das
referidas letras encontram-se aproximadamente na mesma proporção.

O detalhe a ser cuidado aqui é como estabelecer a comparação entre as proporções


das letras. Utilizando a noção de erro relativo entre duas quantidades “a” e “b”, definido
como |a-b|/|a|, e permitindo que esse erro não seja maior do que 0,5, podemos escrever o
algoritmo como segue:

Texto_em_ingles(texto,resultado):
1. Frequencia_letras(texto,tabela_frequencia)
2. indice_e = numero_letra(‘E’)
3. indice_a = numero_letra(‘A’)
4. indice_r = numero_letra(‘R’)
5. indice_q = numero_letra(‘Q’)
6. prop_e = tabela_frequencia(indice_e)/tabela_frequencia(indice_q)
7. prop_a = tabela_frequencia(indice_a)/tabela_frequencia(indice_q)
8. prop_r = tabela_frequencia(indice_r)/tabela_frequencia(indice_q)
9. erro_e = abs(prop_e-50)/50
10. erro_a = abs(prop_a-40)/40
11. erro_r = abs(prop_r-30)/30
12. se erro_e<0,5 E erro_a<0,5 E erro_r<0,5 então
13. resultado = verdadeiro
14. senão
15. resultado = falso
16. fim se

10. Hoje em dia, todas as comunicações importantes feitas entre órgãos de segurança, bancos e
grandes conglomerados econômicos são feitas utilizando-se mensagens cifradas. Um dos
processos de cifragem mais antigos de que se tem conhecimento é o chamado código de Júlio
César. Cada letra da mensagem a ser enviada é trocada por outra, a qual é especificada como,
por exemplo, A→D, B→E, C→F, D→G, ..., X→A, Y→B, Z→C; observe que a distância num
par de letras (original e cifrada) é sempre a mesma. Escreva um algoritmo que receba uma
mensagem (armazenada numa matriz com n linhas e 80 colunas) e uma distância entre um par de
letras (como especificado acima), e produza uma mensagem cifrada.

O algoritmo pode ser facilmente implementado, utilizando-se a função “numero_letra”;


basta obter o número correspondente de uma letra na mensagem a cifrar e somar o
deslocamento, obtendo um índice (se o resultado for um número maior do que 26, calcula-
se o complemento de 26 do índice). Utiliza-se então esse índice como argumento de uma
função “letra”, que retorna a letra associada a um número (é a função inversa da
“numero_letra):

Cifra_Julio_Cesar(mensagem,deslocamento,mensagem_cifrada):
1. para i=1,...,n faça
2. para j=1,...,80 faça
3. letra_cifrada = numero_letra(mensagem(i,j))+deslocamento
4. se letra_cifrada>26 então
5. letra_cifrada = letra_cifrada-26
6. fim se
7. mensagem_cifrada = letra(letra_cifrada)
8. fim para
9. fim para

Por exemplo, se mensagem='JULIOCESAR', então o algoritmo acima produziria


mensagem_cifrada='MXOLRFHVDU'.

_____________________________________________________________________________________
Correção Lista 1 - Algoritmos Página 6 09/06/2014
11. Escreva um algoritmo para localizar um string de caracteres dentro de outro, retornando a
posição no qual ele foi localizado. Por exemplo, se A=”CASA” e B=”A CASA É AMARELA”,
então a posição a ser retornada é 3; porém, se B=”A CASCATA É GRANDE”, então um valor
inválido deve ser devolvido (por exemplo, -1).

Uma implementação simples do procedimento acima consiste em comparar caracter a


caracter os strings “a” e “b”, sempre que o primeiro caracter de “a” for encontrado em “b”:

Localiza_string(a,b,posicao):
1. n_a = tamanho(a) ; n_b = tamanho(b)
2. i = 1 ; posicao = -1
3. enquanto i<=n_b E posição==-1 faça
4. se b(i)==a(1) então
5. k=1;j=1
6. enquanto j<n_a E b(i+j)==a(j+1) faça
7. j = j+1
8. fim enquanto
9. se j==n_a então
10. posicao = i
11. fim se
12. fim se
13. i = i+1
14. fim enquanto

Para o exemplo citado no enunciado do exercício, o algoritmo procederia com n_a=4,


n_b=16 e obteríamos os seguintes valores:

i posicao b(i) a(1) k j b(i+j) b(i+j)==a(j+1)


1 -1 A C
2 -1 ““ C
3 -1 C C 1 1 A VERDADEIRO
2 S VERDADEIRO
3 A VERDADEIRO
4
4 3

O algoritmo termina, retornando posicao=3.

_____________________________________________________________________________________
Correção Lista 1 - Algoritmos Página 7 09/06/2014
12. Escreva um algoritmo que, dado um string de caracteres, devolva outro string de caracteres,
contendo os caracteres do string de entrada em ordem inversa.

Esse também tem uma implementação bastante simples:

Inverte_string(a,b):
1. n = tamanho(a)
2. para i=1,...,n faça
3. b(n-i+1) = a(i)
4. fim para

13. Simule a execução do algoritmo de ordenamento Mergesort sobre um arranjo contendo os


valores 1,7,4,6,2,8,3,5 (nessa ordem).

Como n=8, m=4, de onde o algoritmo Mergesort trabalha inicialmente sobre o arranjo
a[0..m-1] = [1,7,4,6], chamando-o recursivamente, agora sobre esse arranjo;
particionando-o sucessivamente, ele fará chamadas aos arranjos [1,7]; [1]; e [7];
mesclando [1] e [7], o resultado é [1,7]; após, trabalhando sobre [4,6]; [4]; e [6],
mesclando [4] e [6], o resultado é [4,6]. Agora, ao mesclar [1,7] com [4,6], o resultado é
[1,4,6,7].

Continuando agora com o arranjo a[m..n-1] = [2,8,3,5], o algoritmo o subdividirá e


mesclará de forma semelhante, de tal forma, por analogia com o mostrado acima,
teremos:
 [2,8]; [2]; e [8]; mesclado, o resultado é [2,8]
 [3,5]; [3]; e [5]; mesclado, o resultado é [3,5]
 [2,8] e [3,5]; mesclado, o resultado é [2,3,5,8]

Por fim, mesclando [1,4,6,7] com [2,3,5,8], obteremos [1,2,3,4,5,6,7,8], ordenado em


ordem crescente.

14. Moste o funcionamento do algoritmo de ordenamento Quicksort sobre um arranjo contendo os


valores 8,6,4,2,1,3,5,7 (nessa ordem); considere que o ordenamento direto será realizado quando
n=min_n=2.

Como n=8, x:=a[8 div 2]=a[4]=1 (observe que os índices dos arranjos iniciam em 0).
Durante a iteração (laço na variável lógica “continua”), terá sido feita uma troca entre os
elementos i=0 e j=4, resultando no arranjo com os elementos a:=[1,6,4,2,8,3,5,7]. Ao
final dessa iteração, obteremos i=1, j=0; logo, será feita uma chamada recursiva ao
Quicksort, sobre o arranjo a[i..n-1] = a[1..7] = [6,4,2,8,3,5,7].

Na 1ª chamada recursiva, temos n=7, x:=a[7 div 2]=a[3]=8. Durante a iteração, será
feita uma troca entre os elementos i=3 e j=6, resultando no arranjo com os elementos
a:=[6,4,2,7,3,5,8]. Ao final dessa iteração, obteremos i=6, j=5; logo, será feita uma
chamada recursiva ao Quicksort, agora sobre o arranjo a[0..j] = a[0..5] = [6,4,2,7,3,5].

Na 2ª chamada recursiva, temos n=6, x:=a[6 div 2]=a[3]=7. Durante a iteração, será
feita uma troca entre os elementos i=3 e j=5, resultando no arranjo com os elementos
a:=[6,4,2,5,3,7]. Ao final dessa iteração, obteremos i=5 e j=4, de onde será feita uma
chamada recursiva ao Quicksort, agora sobre o arranjo a[0..j] = a[0..4] = [6,4,2,5,3].

Na 3ª chamada recursiva, temos n=5, x:=a[5 div 2]=a[2]=2. Será feita uma troca entre
os elementos i=0 e j=2, resultando no arranjo com os elementos a:=[2,4,6,5,3]. Ao final
dessa iteração, obteremos i=1 e j=0; será feita, portanto uma chamada recursiva ao
Quicksort, agora sobre o arranjo a[i..n-1] = a[1..4] = [4,6,5,3].

Na 4ª chamada recursiva, temos n=4, x:=a[4 div 2]=a[2]=5. Serão feitas agora duas
trocas, entre os elementos i=1, j=3 e i=2, j=2, resultando em a:=[4,3,5,6]. Ao final dessa
iteração, obteremos i=3, j=1; uma nova chamada recursiva será feita, dessa vez sobre
o arranjo a[0..j] = a[0..1] = [4,3].

_____________________________________________________________________________________
Correção Lista 1 - Algoritmos Página 8 09/06/2014
Na 5ª chamada recursiva, o arranjo tem apenas dois elementos e, portanto, é ordenado
diretamente, resultando em a:=[3,4].

Retomando então os resultados mostrados acima, vemos que, antes de ser feita a 1ª
chamada recursiva, o arranjo estava com o primeiro valor na sua posição correta; a:=
[1,6,4,2,8,3,5,7]. Antes da 2ª chamada recursiva, temos a:=[1,6,4,2,7,3,5,8]; antes da 3ª
chamada recursiva, temos a:=[1,6,4,2,5,3,7,8]; antes da 4ª chamada recursiva, temos
a:=[1,2,4,6,5,3,7,8]; e, antes da 5ª chamada recursiva, temos a:=[1,2,4,3,5,6,7,8]. Ao
final dessa, o arranjo está ordenado, a:=[1,2,3,4,5,6,7,8].

_____________________________________________________________________________________
Correção Lista 1 - Algoritmos Página 9 09/06/2014

Você também pode gostar