Você está na página 1de 25

Universidade Federal de Sergipe

Departamento de Sistemas de Informação

SINF0064 – Programação II


Prof. Dr. Alcides Xavier Benicasa
alcides@ufs.br

Roteiro

Recursividade
Definição
Função Recursiva
Condição de Parada

Efeitos da Recursão
Pilhas e Rotinas Recursivas

Recursividade
Quando e Como usar
Recursão vs. Iteração

2/49

1
Recursividade

Definição

 Uma função é dita recursiva quando é definida


em seus próprios termos, direta ou
indiretamente

Dicionário Michaelis: ato ou efeito de recorrer

• Recorrer: Correr de novo por; tornar a percorrer.


Repassar na memória; evocar.

 É uma função como qualquer outra

4/49

2
Recursividade

 Utiliza-se uma função que permite chamar a si


mesma (direta ou indiretamente)

 Exemplo: soma dos primeiros N inteiros


S(5) = 5 + 4 + 3 + 2 + 1
= 5 + S(4)
S(4) = 4 + S(3)
S(3) = 3 + S(2)
S(2) = 2 + S(1)
S(1) = 1 (solução trivial)

5/49

Recursividade

 2 passos:

Definir uma função recursiva, que decresce até


alcançar a solução mais simples (trivial)

• Ex: S(n) = n + S(n-1)

Definir a condição de parada (solução trivial)

• Ex.: S(1) = 1

6/49

3
Recursividade – Exemplo 01
#include <iostream> #include <locale.h> using std::cin;
using std::cout; using std::endl;

int soma (int n){


if (n==1)
return (1);
else
return (n + soma(n-1));
}

int main( ){
setlocale (LC_ALL,"portuguese");
int n;
cout << "Informe o valor de n:\n";
cin >> n;
cout << "A soma é " << soma(n) << endl;
return 0;
}

7/49

Recursividade – Exemplo 01

8/49

4
Recursividade – Exemplo 02

 Na matemática, o fatorial de um número natural n,


representado por n!, é o produto de todos os
inteiros positivos menores ou iguais a n.

 A notação n! foi introduzida


em 1808 por
Christian Kramp
(8 Julho 1760 – 13 Maio 1826).

9/49

Recursividade – Exemplo 02

 Na matemática, o fatorial de
um número natural n,
representado por n!, é o
produto de todos os inteiros
positivos menores ou iguais a
n.

10/49

5
Recursividade – Exemplo 02

 Implemente uma função recursiva para calcular o fatorial


de um número inteiro positivo

 Recursivo
int fatorial (int numero){
return numero==0?1:numero*fatorial(numero-1);
}

 Iterativo
int fatorial (int numero){
int resultado = numero;
if (numero == 0) resultado++;
while (numero > 1) resultado *= --numero;
return resultado;
}

11/49

Recursividade – Exemplo 02
#include <iostream> #include <locale.h> using std::cin;
using std::cout; using std::endl;

int fatorial (int n){


if (n==0)
return (1);
else
return (n * fatorial(n-1));
}

int main( ){
setlocale (LC_ALL,"portuguese");
int n;
cout << "Informe o valor de n:\n";
cin >> n;
cout << "O fatorial é " << fatorial(n) << endl;
return 0;
}

12/49

6
Recursividade – Exercício para casa!!!

 Considerando que o fatorial de um número n também pode


ser expresso pelo fatorial de (n+1) dividido por (n+1), como
segue:

n! = (n+1)! / (n+1)

Alguns exemplos:
• 3! = (3 + 1)! / (3 + 1) = 4! / 4 = 24 / 4 = 6
• 2! = (2 + 1)! / (2 + 1) = 3! / 3 = 6 / 3 = 2
• 1! = (1 + 1)! / (1 + 1) = 2! / 2 = 2 / 2 = 1
• 0! = (0 + 1)! / (0 + 1) = 1! / 1 = 1 / 1 = 1

 Implemente uma função recursiva para calcular o fatorial


de um número inteiro positivo de acordo com a equação
acima.

13/49

Efeitos da Recursão

7
Efeitos da Recursão
Pilhas e Rotinas Recursivas

 Usa-se uma pilha para armazenar os dados


usados em cada chamada de um procedimento
que ainda não terminou.

 Todos os dados não globais vão para a pilha,


registrando o estado corrente da computação.

 Quando uma ativação anterior prossegue, os


dados da pilha são recuperados.

15/49

Pilhas e Rotinas Recursivas


//...
int fatorial (int n){
if (n==0)
return (1);
else
return (n * fatorial(n-1));
}

int main( ){
cout << "O fatorial de 4 é " << fatorial(4) << endl;
return 0;
}
n ret n ret n ret n ret n ret

8
Pilhas e Rotinas Recursivas
//...
int fatorial (int n){
if (n==0)
return (1);
else
return (n * fatorial(n-1));
}

int main( ){
cout << "O fatorial de 4 é " << fatorial(4) << endl;
return 0;
}
n ret N ret n ret n ret n ret

4 4x?=?

Pilhas e Rotinas Recursivas


//...
int fatorial (int n){
if (n==0)
return (1);
else
return (n * fatorial(n-1));
}

int main( ){
cout << "O fatorial de 4 é " << fatorial(4) << endl;
return 0;
}
n ret n ret n ret n ret n ret

3 3x?=?

4 4x?=? 4 4x?=?

9
Pilhas e Rotinas Recursivas
//...
int fatorial (int n){
if (n==0)
return (1);
else
return (n * fatorial(n-1));
}

int main( ){
cout << "O fatorial de 4 é " << fatorial(4) << endl;
return 0;
}
n ret n ret n ret n ret n ret

2 2x?=?

3 3x?=? 3 3x?=?

4 4x?=? 4 4x?=? 4 4x?=?

Pilhas e Rotinas Recursivas


//...
int fatorial (int n){
if (n==0)
return (1);
else
return (n * fatorial(n-1));
}

int main( ){
cout << "O fatorial de 4 é " << fatorial(4) << endl;
return 0;
}
n ret n ret n ret n ret n ret

1 1x?=?

2 2x?=? 2 2x?=?

3 3x?=? 3 3x?=? 3 3x?=?

4 4x?=? 4 4x?=? 4 4x?=? 4 4x?=?

10
Pilhas e Rotinas Recursivas
//...
int fatorial (int n){
if (n==0)
return (1);
else
return (n * fatorial(n-1));
}

int main( ){
cout << "O fatorial de 4 é " << fatorial(4) << endl;
return 0;
}
n ret n ret n ret n ret n ret
0 1
1 1x?=? 1 1x?=?

2 2x?=? 2 2x?=? 2 2x?=?

3 3x?=? 3 3x?=? 3 3x?=? 3 3x?=?

4 4x?=? 4 4x?=? 4 4x?=? 4 4x?=? 4 4x?=?

Pilhas e Rotinas Recursivas


//...
int fatorial (int n){
if (n==0)
return (1);
else
return (n * fatorial(n-1));
}

int main( ){
cout << "O fatorial de 4 é " << fatorial(4) << endl;
return 0;
}
n ret n ret n ret n ret n ret

1 1x1=1

2 2x?=? 2 2x?=?

3 3x?=? 3 3x?=? 3 3x?=?

4 4x?=? 4 4x?=? 4 4x?=? 4 4x?=?

11
Pilhas e Rotinas Recursivas
//...
int fatorial (int n){
if (n==0)
return (1);
else
return (n * fatorial(n-1));
}

int main( ){
cout << "O fatorial de 4 é " << fatorial(4) << endl;
return 0;
}
n ret n ret n ret n ret n ret

2 2x1=2

3 3x?=? 3 3x?=?

4 4x?=? 4 4x?=? 4 4x?=?

Pilhas e Rotinas Recursivas


//...
int fatorial (int n){
if (n==0)
return (1);
else
return (n * fatorial(n-1));
}

int main( ){
cout << "O fatorial de 4 é " << fatorial(4) << endl;
return 0;
}
n ret n ret n ret n ret n ret

3 3x2=6

4 4x?=? 4 4x?=?

12
Pilhas e Rotinas Recursivas
//...
int fatorial (int n){
if (n==0)
return (1);
else
return (n * fatorial(n-1));
}

int main( ){
cout << "O fatorial de 4 é " << fatorial(4) << endl;
return 0;
}
n ret n ret n ret n ret n ret

4 4x6=24

Pilhas e Rotinas Recursivas


//...
int fatorial (int n){
if (n==0)
return (1);
else
return (n * fatorial(n-1));
}

int main( ){
cout << "O fatorial de 4 é " << fatorial(4) << endl;
return 0;
}
n ret n ret n ret n ret n ret

13
Efeitos da Recursão
Pilhas e Rotinas Recursivas

 A cada chamada
Empilham-se na memória os dados locais (variáveis
e parâmetros) e o endereço de retorno
• A função corrente só termina quando a função chamada
terminar

Executa-se a nova chamada (que também pode ser


recursiva)

Ao retornar, desempilham-se os dados da memória,


restaurando o estado antes da chamada recursiva

27/49

Recursividade – Exemplo 03

 Simule a execução da função para um vetor de tamanho 3 e


mostre a situação da memória a cada chamada recursiva

 Recursivo

void imprime(int v[], int tamanho, int indice_atual)


{
if (indice_atual<tamanho) {
cout << v[indice_atual] << endl;
imprime(v,tamanho,indice_atual+1);
}
}

28/49

14
Recursividade – Exemplo 03
#include <iostream>
#include <locale.h>
using std::cin;
using std::cout;
using std::endl;

void imprime(int v[], int tamanho, int indice_atual) {


if (indice_atual<tamanho) {
cout << v[indice_atual] << endl;
imprime(v,tamanho,indice_atual+1);
}
}

int main( )
{
setlocale (LC_ALL,"portuguese");
int v[3] = {10,4,54};
imprime(v,3,0);
return 0;
}
29/49

Alternativa: tem o mesmo efeito? (Exemplo 04)


#include <iostream> //...

void imprime2(int v[], int tamanho, int indice_atual) {


if (indice_atual<tamanho) {
cout << v[indice_atual] << endl;
//imprime3(v,tamanho,indice_atual+1);}
}

void imprime1(int v[], int tamanho, int indice_atual) {


if (indice_atual<tamanho) {
cout << v[indice_atual] << endl;
imprime2(v,tamanho,indice_atual+1);}
}

void imprime0(int v[], int tamanho, int indice_atual) {


if (indice_atual<tamanho) {
cout << v[indice_atual] << endl;
imprime1(v,tamanho,indice_atual+1);}
}

int main( ) {
setlocale (LC_ALL,"portuguese");
Mesmo resultado, com
int v[3] = {10,4,54}; diferença de haver
imprime0(v,3,0);
return 0; }
duplicação de código
30/49

15
Efeitos da Recursão
Pilhas e Rotinas Recursivas

 Mesmo resultado, com diferença de haver


duplicação de código

O que isso quer dizer?

Funções recursivas são sempre melhores do que


funções não recursivas?

• Depende do problema, pois nem sempre a recursão é


a melhor forma de resolver o problema, já que pode
haver uma versão simples e não recursiva da função
(que não duplica código e não consome mais memória)

31/49

Recursividade

Quando e como usar

16
Recursão

 Quando usar: quando o problema pode ser


definido recursivamente de forma natural

 Como usar
1º ponto: definir o problema de forma recursiva,
ou seja, em termos dele mesmo
2º ponto: definir a condição de término (ou
condição básica)
3º ponto: a cada chamada recursiva, deve-se
tentar garantir que se está mais próximo de
satisfazer a condição de término (caso mais
simples)
• Caso contrário, qual o problema?

33/49

Recursão

 Problema do fatorial

1º ponto: definir o problema de forma recursiva


• n! = n * (n-1)!

2º ponto: definir a condição de término


• n=0

3º ponto: a cada chamada recursiva, deve-se tentar


garantir que se está mais próximo de satisfazer a
condição de término
• A cada chamada, n é decrementado, ficando mais
próximo da condição de término

34/49

17
Recursão

 Quem é melhor?

//versão recursiva //versão iterativa


int fatorial(int n) { int fatorial(int n) {
int fat; int i, fat=1;

if (n==0) fat=1; for (i=2;i<=n;i++)


else fat=fat*i;
fat=n*fatorial(n-1);

return(fat); return(fat);
} }

35/49

Exemplo 05 - Fibonacci

 Implemente uma função recursiva para calcular o enésimo número


de Fibonacci

1º ponto: definir o problema de forma


recursiva

2º ponto: definir a condição de término

3º ponto: a cada chamada recursiva,


deve-se tentar garantir que se está
mais próximo de satisfazer a
condição de término
Leonardo Fibonnaci – Pisa (Itália)
(1170 – 1250)
36/49

18
Exemplo 05 - Fibonacci

 Implemente uma função recursiva para calcular o enésimo


número de Fibonacci

1º ponto: definir o problema de forma recursiva


• f(0)=0, f(1)=1, f(n)=f(n-1)+f(n-2) para n>=2

2º ponto: definir a condição de término


• n=0 e/ou n=1

3º ponto: a cada chamada recursiva, deve-se tentar garantir


que se está mais próximo de satisfazer a condição de término
• n é decrementado em cada chamada

37/49

38/49

19
39/49

40/49

20
41/49

21
Exemplo 05 - Fibonacci

 É uma sucessão de números que, misteriosamente, aparece em muitos


fenômenos da natureza. Descrita no final do século 12 pelo italiano
Leonardo Fibonacci, ela é infinita e começa com 0 e 1. Os
números seguintes são sempre a soma dos dois núme-
ros anteriores. Portanto, depois de 0 e 1, vêm 1, 2, 3,
5, 8, 13, 21, 34…
 Ao transformar esses números em quadrados e dispô-
los de maneira geométrica, é possível traçar uma espiral
perfeita, que também aparece em diversos organis- mos
vivos. Outra curiosidade é que os termos da se-
quência também estabelecem a chamada
“proporção áurea”, muito usada na arte, na
arquitetura e no design por ser considerada
agradável aos olhos. Seu valor é de 1,618
e, quanto mais você avança na sequência de
Fibonacci, mais a divisão entre um termo e
seu antecessor se aproxima desse número. Leonardo Fibonnaci – Pisa (Itália)
(1170 – 1250)
43/49

Recursividade – Exemplo 05
#include <iostream> #include <locale.h> using std::cin;
using std::cout; using std::endl;

int fib(int n) {
int resultado;
if (n<2)
resultado=n;
else
resultado=fib(n-1)+fib(n-2);

return(resultado);
}

int main( )
{
setlocale (LC_ALL,"portuguese");
int n;
cout << "Informe o valor de n:\n";
cin >> n;
cout<< "O " << n <<"º número de Fibonacci é "<< fib(n) <<endl;
return 0;
} 44/49

22
Exemplo 05 - Fibonacci

 Quem é melhor?

n 10 20 30 50 100
Recursão 8ms 1s 2min 21dias 109anos

Iteração 1/6ms 1/3ms 1/2ms 3/4ms 1,5ms

Estimativa de tempo para Fibonacci


(Brassard e Bradley, 1996) implementado
em Pascal em um CDC CYBER 835 (foto
ao lado).

45/49

Recursão vs. Iteração

 Programas recursivos que possuem chamadas


ao final do código são ditos terem
recursividade de cauda.

 Programas com essa característica são


facilmente transformáveis em uma versão não
recursiva.

Uma solução iterativa (não recursiva) será mais


eficiente

46/49

23
Recursão vs. Iteração

 Podemos observar que o principal objetivo da


função recursiva fatorial é criar um “looping”
que se repete até que a condição de parada
seja satisfeita.

int fatorial(int n) {
int fat;

if (n==0) fat=1;
else
fat=n*fatorial(n-1);
looping
return(fat);
}
47/49

Recursão vs. Iteração

 Embora a função fatorial seja mais


eficientemente implementada de forma
iterativa, ela ainda é um excelente exemplo
para se entender o que é recursão.

48/49

24
Referências

 Ziviani, N. (2004). Projeto de Algoritmos com Implementações


em Pascal e C. Editora Cengage Learning.

 Cormen, T.H.; Leiserson, C.E.; Rivest, R.L.; Stein, C. (2002).


Algoritmos: Teoria e Prática. Editora Campus.

 Tenenbaum, A.M.; Langsam, Y.; Augenstein, M.J. (1995).


Estruturas de Dados Usando C. Makron Books.

 Mizrahi, V.V. (2008). Treinamento em Linguagem C. Prentice


Hall.

 Amancio, D.R. Notas de Aula de Introdução à Ciências da


Computação. Departamento de Ciências da Computação,
ICMC, USP (2014).

49/49

25

Você também pode gostar