Recursividade
Inhaúma Neves Ferraz
Departamento de Ciência da Computação
Universidade Federal Fluminense
[email protected]
Objetos e Procedimentos
Recursivos
Um objeto é dito recursivo se consiste
parcialmente em si mesmo ou é definido em
termos de si mesmo.
Procedimentos recursivos podem ser
processadas por procedimentos não
recursivos simulando a recursão.
2
Eventos que ocorrem no uso de
Procedimentos
Na chamada do procedimento
 Passagem dos argumentos
 Alocação e inicialização das variáveis locais
 Transferência do controle para a função (endereço de
retorno)
No retorno do procedimento
 Recuperação do endereço de retorno
 Liberação da área de dados
 Desvio para o endereço de retorno
3
Chamadas de procedimentos
(não recursivos)
4
Implementação de procedimentos
recursivos
Procedimentos recursivos só podem ser
implementados em alto nível de abstração.
As máquinas não executam procedimentos
recursivos.
Cabe ao “software” simular procedimentos
recursivos.
5
Simulação de Procedimentos
Recursivos
A simulação de recursão utilizará uma pilha
com os seguintes atributos gravados:




Parâmetros
Variáveis
Valor da função (se for o caso)
Endereço de retorno
6
Chamadas recursivas de funções
7
Exemplo de Procedimentos
Recursivos
Cálculo do fatorial de um inteiro
Série de Fibbonacci
Torres de Hanói
8
Cálculo do fatorial de um inteiro
public class Factorial {
public static void main(String[] args){
int input = Integer.parseInt(args[0]);
double result = factorial(input);
System.out.println(result);
}
public static double factorial(int x){
if (x<0) return 0.0;
else if (x==0) return 1.0;
else return x*factorial(x-1);
}
}
9
Série de Fibbonacci
public static int fib(int n)
{
int x,y;
if (n <= 1) return 1;
else {
x = fib(n-1);
y = fib(n-2);
return x + y;
}
}
10
Torres de Hanói
Denote os pinos por A, B, C
Seja n o número total de discos
Numere os discos de 1 (menor, no topo da
pilha) até n (maior, base da pilha)
Para mover n discos do pino A para o pino B:
Mova n-1 discos de A para C. Isto faz com que o
disco n fique isolado no pino A
2. Mova o disco n de A para B
3. Mova n-1 discos de C para B de maneira que eles
fiquem em cima do disco n
1.
11
Exemplo
Algoritmo do Fatorial
fact(n) = n * fact(n - 1)
se n=o
então
fact(0) = 1
senão
x = n-1
y = fact(x)
fact(n) = n * y
fim do se
13
Exemplo de uso de pilha
x = n-1
y = fact(x)
fact(n) = n * y
14
Simulação de Recursão
Programa simulador de recursão (1)
Para criar um programa que simule a
recursão deve-se partir do programa
recursivo e executar 3 alterações:
 Alteração inicial
 Substituição de cada chamada recursiva por um
conjunto de instruções
 Substituição de cada retorno da função por um
conjunto de instruções
16
Programa simulador de recursão (2)
O programa assim obtido será uma
simulação não recursiva do programa
recursivo.
Em geral o programa assim obtido não é
um programa bem estruturado e que,
freqüentemente, pode ser aperfeiçoado por
refinamentos subseqüentes.
17
Alteração inicial
Declarar uma pilha e inicializá-la como
vazia
2. Associar um rótulo ao primeiro comando
executável
3. Atribuir aos dados correntes os valores
adequados
1.
18
Chamadas recursivas
4.
5.
6.
7.
8.
9.
Criar o i-ésimo rótulo Li (ou label_i)
Quando os argumentos da chamada do procedimentos
forem expressões calcular o valor das expressões e
atribuir estes valores aos parâmetros formais
Empilhar os parâmetros, as variáveis locais e o endereço
de retorno i
Atualizar os valores dos parâmetros para o
prosseguimento do programa
Executar um desvio incondicional para o rótulo do início
do procedimento
Associar o rótulo criado no item (ou regra) 4 ao comando
subseqüente ao desvio incondicional
19
Substituição do return
10. Retornar se a pilha estiver vazia
11. Desempilhar os parâmetros, variáveis locais e
endereço de retorno
12. Se o procedimento for uma função avaliar as
expressões que se seguem ao return e empilhar o
resultado
13. Executar um desvio condicional para o rótulo
especificado no endereço de retorno corrente
20
Modelos de geração de
programas não recursivos
simulando programas
recursivos
Modelo de alteração inicial
Modelo de chamada recursiva
Modelo de retorno de chamada recursiva
21
Alteração Inicial
/* Passo 1: Pilha vazia
*/
s.top = MENOS_UM;
/* inicialização de área vazia
*/
currarea.x = 0;
/* por exemplo */
currarea.y = ‘’;
/* por exemplo */
currarea.retaddr = 0;
/* empilhar a área vazia
*/
push(&s,&currarea);
/* Passo 3: Atribuir aos parametros e endereço
de retorno os valores adequados */
currarea.param = n; /* por exemplo */
currarea.retaddr = 1; /* 1 para retornar a main.
2 para recursão */
start : /* Passo 2: associar rótulo inicial ao
primeiro comando executável */
22
Chamada Recursiva
/* simulação de chamada recursiva */
/* Passo 6: empilhamento
*/
push(&s,&currarea);
/* Passo 7: atualização dos valores dos
parâmetros
*/
currarea.x = ...;
/* por exemplo */
.
.
currarea.retaddr = i;
/* Passo 8: desvio incondicional para o rótulo
inicial
*/
goto start;
label_i : /* Passos 4 e 9: rótulo associado ao
comando subseqüente ao desvio
*/
23
Retorno de função
/* simulação de return */
/* Passo 11: desempilhar
*/
i = currarea.retaddr;
pop(&s,&currarea);
/* Passo 13: desvio condicional para o endereço
de retorno
*/
switch (i) {
case 1 : goto label1; /* tantos casos
quantos forem as chamadas recursivas + 1 */
.
.
case m : goto label2;
} /* end switch */
} /* end if (currarea.param == 0)
*/
24
Exemplos
Serão apresentados dois problemas
clássicos de recursão :
cálculo do fatorial de um inteiro
 problema conhecido como Torres de Hanói

25
Torres de Hanói
26
Passagem de 4 discos do pino A para o pino C
27
28
Chamadas de funções (não recursivas)
29
Chamadas recursivas de funções
30
Fatorial recursivo
long int fact(int n)
{
int x;
long int y;
if (n < 0) {
printf("parâmetro negativo: %d\n",n);
exit(1);
} /* end if */
if (n == 0)
return (1);
x = n-1;
y = fact(x);
return(n*y);
}
/* end fact
*/
31
Simulação da recursão (1)
long int simfact1(int n)
{
// Simulação de fatorial
struct dataarea currarea;
short int i;
long int result;
s.top = MENOS_UM;
/* inicialização de área vazia
*/
currarea.param
= 0;
currarea.x
= 0;
currarea.y
= 0;
currarea.retaddr = 0;
/* empilhar a área vazia
*/
if (push(&s,&currarea) == MENOS_DOIS) {
printf("Estouro de pilha\n");
exit(1);
}
/* end if
*/
/* Atribuir ao parametro e endereço de retorno os valores adequados */
currarea.param
= n;
currarea.retaddr = 1; /* 1 para retornar a main. 2 para recursão */
32
Simulação da recursão (2)
start : /*inicio da rotina de simulação de fatorial
if (currarea.param == 0) { /* fact(0) */
/* simulação de return */
result = 1;
/* fact(0) == 1; */
i = currarea.retaddr;
if (pop(&s,&currarea) == MENOS_UM) {
printf("Pilha vazia\n");
}
/* end if */
switch (i) {
case 1 : goto label1;
case 2 : goto label2;
} /* end switch */
} /* end if (currarea.param == 0)
*/
/* currarea.param != 0
*/
currarea.x = currarea.param - 1;
/* simulação de chamada recursiva */
if (push(&s,&currarea) == MENOS_DOIS) {
printf("Estouro de pilha\n");
exit(1);
}
/* end if
*/
currarea.param = currarea.x;
currarea.retaddr = 2;
goto start; /* chamada recursiva */
*/
33
Simulação da recursão (3)
label2 : /* ponto de retorno da chamada recursiva */
/* atribui-se o valor retornado a cuurarea.y */
currarea.y = result;
/* simulação de return(n*y)
*/
result = currarea.param * currarea.y;
i = currarea.retaddr;
/* voltar ao ambiente original */
if (pop(&s,&currarea) == MENOS_UM) {
printf("Pilha vazia\n");
}
/* end if */
switch (i) {
case 1 : goto label1;
case 2 : goto label2;
} /* end switch */
label1 : /* ponto de retorno ao programa principal
*/
return (result);
} /* end simfact1 */
34
O programa simulador obtido
O programa simulador obtido ( fat_nr01)
não tem uma boa estrutura.
Ele pode, e deve, ser aperfeiçoado por
refinamentos subseqüentes.
Inicialmente será criado o programa
fat_nr02.
35
Passagem de fat_nr01 para fat_nr02
O fatorial corrente y e o valor corrente x podem
ser globais não necessitando ser empilhados.
Como só há um endereço de retorno de chamada
recursiva, só há um endereço de retorno, pois o
retorno ao programa chamador é testado pela
condição de pilha vazia. Portanto não é preciso
empilhar endereços de retorno usando desvio
incondicional
36
Passagem de fat_nr02 para fat_nr03
switch(pop(&s,&currarea)){ aparece
duas vezes e agrupando estas ocorrências
pode-se simplificar o algoritmo
x e currparam nunca são usadas
simultaneamente e podem ser uma só
variável x
37
Passagem de fat_nr03 para fat_nr04
O laço criado pelo teste inicial e terminado
por goto start pode ser substituído por um
laço while
O laço iniciado por label2 e terminado por
switch(pop(... pode ser substituído por
outro laço while
38
Passagem de fat_nr04 para fat_nr05
O primeiro laço while empilha 1 a n, sendo 1
no topo
O segundo laço while multiplica o resultado
corrente pelo topo da pilha
Sabendo disso não é preciso nem empilhar
nada
39
Algoritmo recursivo das Torres de Hanói
void towers(int n, char from, char to, char aux)
{
/* Caso haja um só disco, mova-o e retorne
*/
if (n == 1) {
printf("Mover disco 1 do pino %c para o pino %c\n",
from, to);
return;
}
/* end if */
/* Mover os n-1 discos superiores de A para B usando C como
auxiliar */
towers(n-1,from,aux,to);
/* Mover o disco restante de A para C */
printf("Mover disco %d do pino %c para o pino %c\n",
n, from, to);
/* Mover os n-1 discos de B para C usando A como auxiliar */
towers(n-1,aux,to,from);
return;
}
/* end towers
*/
40
Download

Recursividade