Вы находитесь на странице: 1из 8

AULA

R ECURSO

Recurso um conceito fundamental em computao. Sua compreenso nos permite construir programas elegantes, curtos e poderosos. Muitas vezes, o equivalente no-recursivo
muito mais difcil de escrever, ler e compreender que a soluo recursiva, devido em especial
estrutura recursiva intrnseca do problema. Por outro lado, uma soluo recursiva tem como
principal desvantagem maior consumo de memria, na maioria das vezes muito maior que
uma funo no-recursiva equivalente.
Na linguagem C, uma funo recursiva aquela que contm em seu corpo uma ou mais
chamadas a si mesma. Naturalmente, uma funo deve possuir pelo menos uma chamada proveniente de uma outra funo externa. Uma funo no-recursiva, em contrapartida, aquela
para qual todas as suas chamadas so externas. Nesta aula construiremos funes recursivas
para solues de problemas.
Este texto baseado nas referncias [2, 7].

1.1 Definio
De acordo com [2], em muitos problemas computacionais encontramos a seguinte propriedade: cada entrada do problema contm uma entrada menor do mesmo problema. Dessa
forma, dizemos que esses problemas tm uma estrutura recursiva. Para resolver um problema
como esse, usamos em geral a seguinte estratgia:
se a entrada do problema pequena ento
resolva-a diretamente;
seno,
reduza-a a uma entrada menor do mesmo problema,
aplique este mtodo entrada menor
e volte entrada original.
A aplicao dessa estratgia produz um algoritmo recursivo, que implementado na linguagem C torna-se um programa recursivo ou um programa que contm uma ou mais funes
recursivas.
Uma funo recursiva aquela que possui, em seu corpo, uma ou mais chamadas a si
mesma. Uma chamada de uma funo a si mesma dita uma chamada recursiva. Como
sabemos, uma funo deve possuir ao menos uma chamada proveniente de uma outra funo,
externa a ela. Se a funo s possui chamadas externas a si, ento chamada de funo norecursiva. No semestre anterior, construimos apenas funes no-recursivas.

1.2 E XEMPLOS

Em geral, a toda funo recursiva corresponde uma outra no-recursiva que executa exatamente a mesma computao. Como alguns problemas possuem estrutura recursiva natural,
funes recursivas so facilmente construdas a partir de suas definies. Alm disso, a demonstrao da correo de um algoritmo recursivo facilitada pela relao direta entre sua
estrutura e a induo matemtica. Outras vezes, no entanto, a implementao de um algoritmo
recursivo demanda um gasto maior de memria, j que durante seu processo de execuo muitas informaes devem ser guardadas na sua pilha de execuo.

1.2 Exemplos
Um exemplo clssico de uma funo recursiva aquela que computa o fatorial de um nmero inteiro n > 0. A idia da soluo atravs de uma funo recursiva baseada na frmula
mostrada a seguir:

1,
se n 6 1 ,
n! =
n (n 1)! , caso contrrio .
A funo recursiva fat , que calcula o fatorial de um dado nmero inteiro no negativo n,
mostrada a seguir:

/* Recebe um nmero inteiro n >= 0 e devolve o fatorial de n */


int fat(int n)
{
int result;
if (n <= 1)
result = 1;
else
result = n * fat(n-1);
return result;
}

A sentena return pode aparecer em qualquer ponto do corpo de uma funo e por isso
podemos escrever a funo fat como a seguir:

/* Recebe um nmero inteiro n >= 0 e devolve o fatorial de n */


int fat(int n)
{
if (n <= 1)
return 1;
else
return n * fat(n-1);
}

Geralmente, preferimos a primeira verso implementada acima, onde existe apenas uma
sentena com a palavra reservada return posicionada no final do corpo da funo fat . Essa
maneira de escrever funes recursivas evita confuso e nos permite seguir o fluxo de execuo
FACOM

UFMS

1.2 E XEMPLOS

dessas funes mais naturalmente. No entanto, a segunda verso da funo fat apresentada
acima equivalente primeira e isso significa que tambm vlida. Alm disso, essa segunda
soluo mais compacta e usa menos memria, j que evita o uso de uma varivel. Por tudo
isso, essa segunda forma de escrever funes recursivas muito usada por programadores(as)
mais experientes.
A execuo da funo fat se d da seguinte forma. Imagine que uma chamada fat(3)
foi realizada. Ento, temos ilustrativamente a seguinte situao:
fat(3)

p
fat(2)

p
fat(1)

p
devolve 1
x
devolve 2 1 = 2 fat(1)
x
devolve 3 2 = 3 fat(2)
x
Repare nas indentaes que ilustram as chamadas recursivas funo.
Vamos ver um prximo exemplo. Considere o problema de determinar um valor mximo
de um vetor v com n elementos. O tamanho de uma entrada do problema n > 1. Se n = 1
ento v[0] o nico elemento do vetor e portanto v[0] mximo. Se n > 1 ento o valor que
procuramos o maior dentre o mximo do vetor v[0..n 2] e o valor armazenado em v[n 1].
Dessa forma, a entrada v[0..n 1] do problema fica reduzida entrada v[0..n 2]. A funo
maximo a seguir implementa essa idia.

/* Recebe um nmero inteiro n > 0 e um vetor v de nmeros inteiros com n elementos e devolve um elemento mximo de v */
int maximo(int n, int v[MAX])
{
int aux;
if (n == 1)
return v[0];
else {
aux = maximo(n-1, v);
if (aux > v[n-1])
return aux;
else
return v[n-1];
}
}

Segundo P. Feofiloff [2], para verificar que uma funo recursiva est correta, devemos seguir o seguinte roteiro, que significa mostrar por induo a correo de um algoritmo ou programa:

FACOM

UFMS

1.2 E XEMPLOS

Passo 1: escreva o que a funo deve fazer;


Passo 2: verifique se a funo de fato faz o que deveria fazer quando a entrada pequena;
Passo 3: imagine que a entrada grande e suponha que a funo far a coisa certa para entradas menores; sob essa hiptese, verifique que a funo faz o que dela se espera.
Isso posto, vamos provar ento a seguinte propriedade sobre a funo maximo descrita
acima.
Proposio 1.1. A funo maximo encontra um maior elemento em um vetor v com n > 1 nmeros
inteiros.
Demonstrao. Vamos provar a afirmao usando induo na quantidade n de elementos do
vetor v.
Se n = 1 o vetor v contm exatamente um elemento, um nmero inteiro, armazenado em
v[0]. A funo maximo devolve, neste caso, o valor armazenado em v[0], que o maior elemento no vetor v.
Suponha que para qualquer valor inteiro positivo m menor que n, a chamada externa funo maximo(m, v) devolva corretamente o valor de um maior elemento no vetor v contendo
m elementos.
Suponha agora que temos um vetor v contendo n > 1 nmeros inteiros. Suponha que
fazemos a chamada externa maximo(n, v) . Como n > 1, o programa executa a sentena
descrita abaixo:
aux = maximo(n-1, v);

Ento, por hiptese de induo, sabemos que a funo maximo devolve um maior elemento no
vetor v contendo n 1 elementos. Esse elemento, por conta da sentena acima, armazenado
ento na varivel aux . A estrutura condicional que se segue na funo maximo compara o
valor armazenado em aux com o valor armazenado em v[n-1] , o ltimo elemento do vetor
v. Um maior valor entre esses dois valores ser ento devolvido pela funo. Dessa forma,
a funo maximo devolve corretamente o valor de um maior elemento em um vetor com n
nmeros inteiros.

Exerccios
1.1 A n-sima potncia de um nmero x, denotada por xn , pode ser computada recursivamente observando a seguinte a frmula:

1,
se n = 0 ,
n
x =
n1
xx
, se n > 1 .
Considere neste exerccio que x e n so nmeros inteiros.

FACOM

UFMS

1.2 E XEMPLOS

(a) Escreva uma funo no-recursiva com a seguinte interface:


int pot(int x, int n)

que receba dois nmeros inteiros x e n e calcule e devolva xn .


(b) Escreva uma funo recursiva com a seguinte interface:
int potR(int x, int n)

que receba dois nmeros inteiros x e n e calcule e devolva xn .


(c) Escreva um programa que receba dois nmeros inteiros x e n, com n > 0, e devolva
xn . Use as funes em (a) e (b) para mostrar os dois resultados.
Programa 1.1: Soluo do exerccio 1.1.
#include <stdio.h>
/* Recebe um dois nmeros inteiros x e n e devolve x a n-sima potncia */
int pot(int x, int n)
{
int i, result;
result = 1;
for (i = 1; i <= n; i++)
result = result * x;
return result;
}
/* Recebe um dois nmeros inteiros x e n e devolve x a n-sima potncia */
int potR(int x, int n)
{
if (n == 0)
return 1;
else
return x * potR(x, n-1);
}
/* Recebe dois nmeros inteiros x e n e imprime x a n-sima potncia chamando duas funes: uma no-recursiva e uma recursiva */
int main(void)
{
int x, n;
scanf("%d%d", &x, &n);
printf("No-resursiva: %d^%d = %d\n", x, n, pot(x, n));
printf("Resursiva
: %d^%d = %d\n", x, n, potR(x, n));
return 0;
}

FACOM

UFMS

1.2 E XEMPLOS

1.2 O que faz a funo abaixo?

void imprime_alguma_coisa(int n)
{
if (n != 0) {
imprime_alguma_coisa(n / 2);
printf("%c", 0 + n % 2);
}
}

Escreva um programa para testar a funo imprime_alguma_coisa .


1.3

(a) Escreva uma funo recursiva que receba dois nmeros inteiros positivos e devolva
o mximo divisor comum entre eles usando o algoritmo de Euclides.
(b) Escreva um programa que receba dois nmeros inteiros e calcule o mximo divisor
comum entre eles. Use a funo do item (a).

1.4

(a) Escreva uma funo recursiva com a seguinte interface:


float soma(int n, float v[MAX])

que receba um nmero inteiro n > 0 e um vetor v de nmeros com ponto flutuante
com n elementos, e calcule e devolva a soma desses nmeros.
(b) Usando a funo do item anterior, escreva um programa que receba um nmero
inteiro n, com n > 1, e mais n nmeros reais e calcule a soma desses nmeros.
1.5

(a) Escreva uma funo recursiva com a seguinte interface:


int soma_digitos(int n)

que receba um nmero inteiro positivo n e devolva a soma de seus dgitos.


(b) Escreva um programa que receba um nmero inteiro n e imprima a soma de seus
dgitos. Use a funo do item (a).
1.6 A seqncia de Fibonacci uma seqncia de nmeros inteiros positivos dada pela seguinte frmula:

F1 = 1
F = 1
2
Fi = Fi1 + Fi2 , para i > 3.
(a) Escreva uma funo recursiva com a seguinte interface:
int Fib(int i)

que receba um nmero inteiro positivo i e devolva o i-simo termo da seqncia de


Fibonacci, isto , Fi .
FACOM

UFMS

1.2 E XEMPLOS

(b) Escreva um programa que receba um nmero inteiro i > 1 e imprima o termo Fi da
seqncia de Fibonacci. Use a funo do item (a).
1.7 O piso de um nmero inteiro positivo x o nico inteiro i tal que i 6 x < i + 1. O piso
de x denotado por x.
Segue uma amostra de valores da funo log2 n:
n
log2 n

15
3

16
4

31
4

32
5

63
5

64
6

127
6

128
7

255
7

256
8

511
8

512
9

(a) Escreva uma funo recursiva com a seguinte interface:


int piso_log2(int n)

que receba um nmero inteiro positivo n e devolva log2 n.


(b) Escreva um programa que receba um nmero inteiro n > 1 e imprima log2 n. Use
a funo do item (a).
1.8 Considere o seguinte processo para gerar uma seqncia de nmeros. Comece com um
inteiro n. Se n par, divida por 2. Se n mpar, multiplique por 3 e some 1. Repita esse
processo com o novo valor de n, terminando quando n = 1. Por exemplo, a seqncia de
nmeros a seguir gerada para n = 22:
22

11

34

17

52

26

13

40

20

10

16

conjecturado que esse processo termina com n = 1 para todo inteiro n > 0. Para uma
entrada n, o comprimento do ciclo de n o nmero de elementos gerados na seqncia.
No exemplo acima, o comprimento do ciclo de 22 16.
(a) Escreva uma funo no-recursiva com a seguinte interface:
int ciclo(int n)

que receba um nmero inteiro positivo n, mostre a seqncia gerada pelo processo
descrito acima na sada e devolva o comprimento do ciclo de n.
(b) Escreva uma verso recursiva da funo do item (a) com a seguinte interface:
int cicloR(int n)

que receba um nmero inteiro positivo n, mostre a seqncia gerada pelo processo
descrito acima na sada e devolva o comprimento do ciclo de n.
(c) Escreva um programa que receba um nmero inteiro n > 1 e determine a seqncia
gerada por esse processo e tambm o comprimento do ciclo de n. Use as funes em
(a) e (b) para testar.
FACOM

UFMS

1.2 E XEMPLOS

1.9 Podemos calcular a potncia xn de uma maneira mais eficiente. Observe primeiro que se
n uma potncia de 2 ento xn pode ser computada usando seqncias de quadrados.
Por exemplo, x4 o quadrado de x2 e assim x4 pode ser computado usando somente duas
multiplicaes ao invs de trs. Esta tcnica pode ser usada mesmo quando n no uma
potncia de 2, usando a seguinte frmula:

se n = 0,
1,
(1.1)
xn =
(xn/2 )2 , se n par,

n1
xx
, se n mpar.
(a) Escreva uma funo com interface
int potencia(int x, int n)

que receba dois nmeros inteiros x e n e calcule e devolva xn usando a frmula (1.1).
(b) Escreva um programa que receba dois nmeros inteiros a e b e imprima o valor de
ab .

FACOM

UFMS

Вам также может понравиться