Recursividade Inhaúma Neves Ferraz Departamento de Ciência da Computação Universidade Federal...

Preview:

Citation preview

Recursividade

Inhaúma Neves Ferraz

Departamento de Ciência da Computação

Universidade Federal Fluminense

ferraz@ic.uff.br

2

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.

3

Eventos que ocorrem no uso de Procedimentos

Na chamada do procedimentoPassagem dos argumentosAlocação e inicialização das variáveis locaisTransferência do controle para a função (endereço de retorno)

  No retorno do procedimento

Recuperação do endereço de retornoLiberação da área de dadosDesvio para o endereço de retorno

 

4

Chamadas de procedimentos (não recursivos)

5

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.

6

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

7

Chamadas recursivas de funções

8

Exemplo de Procedimentos Recursivos

Cálculo do fatorial de um inteiro

Série de Fibbonacci

Torres de Hanói

9

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); } }

10

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; }}

11

Torres de HanóiDenote os pinos por A, B, C Seja n o número total de discosNumere 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:1. 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

Exemplo

13

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

14

Exemplo de uso de pilha

x = n-1y = fact(x)fact(n) = n * y

Simulação de Recursão

16

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

17

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.

18

Alteração inicial

1. 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

19

Chamadas recursivas

4. Criar o i-ésimo rótulo Li (ou label_i) 5. Quando os argumentos da chamada do procedimentos

forem expressões calcular o valor das expressões e atribuir estes valores aos parâmetros formais

6. Empilhar os parâmetros, as variáveis locais e o endereço de retorno i

7. Atualizar os valores dos parâmetros para o prosseguimento do programa

8. Executar um desvio incondicional para o rótulo do início do procedimento

9. Associar o rótulo criado no item (ou regra) 4 ao comando subseqüente ao desvio incondicional

20

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

21

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

22

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 */

23

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 */

24

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) */

25

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

26

Torres de Hanói

27

Passagem de 4 discos do pino A para o pino C

28

29

Chamadas de funções (não recursivas)

30

Chamadas recursivas de funções

31

Fatorial recursivolong 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 */

32

Simulação da recursão (1)

long int simfact1(int n){// Simulação de fatorialstruct 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 */

33

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 */

34

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 */

35

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.

36

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

37

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

38

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

39

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

40

Algoritmo recursivo das Torres de Hanóivoid 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 */

Recommended