Recursividade Conceitos e Aplicações Recursividade • Um algoritmo é dito recursivo quando ele chama a si mesmo ou chama uma seqüência de outros algoritmos, e um deles chama novamente o primeiro algoritmo. Suas características são: • • • • Divisão do problema original em pequenas ocorrências Abordagem “dividir para conquistar” (divide and conquer) Dificuldade no reconhecimento das aplicações e também no equilíbrio entre espaço (memória ocupada) e tempo (tempo de execução) Fornece uma boa compreensão da solução do problema Recursividade – Função Fatorial Definição da função: n! = 1, se n = 0 n! = n * (n – 1)! se n > 0 Calculando 4! usando a última definição, faríamos: 4! = 4 x3! = 4 x (3 x 2!) = 4 x (3 x (2 x 1!)) = 4 x (3 x (2 x (1 x 0!))). = 4 x (3 x (2 x (1 x 1))) = 4 x (3 x (2 x 1)) = 4 x (3 x 2) =4x6 = 24 Recursividade – Funções Recursivas Possuem, sempre, duas partes: • • Solução trivial : dada por definição, obtida sem recursão Solução geral : parte do problema que em essência é igual ao problema original, sendo, porém, menor Seu controle é feito através de pilhas: • • • Chamadas e retornos de rotinas: utilizam a pilha Quando uma rotina é chamada, empilha-se: o endereço de retorno + todas as variáveis locais Quando ocorre o retorno da rotina, as variáveis locais que foram criadas deixam de existir Recursividade – Função Fatorial • Versão recursiva da função fatorial public int Fatorial(int n) { if (n==0) return 1; else return n*Fatorial(n-1); } • Vamos realizar uma simulação da execução da função fatorial com o valor 4. Recursividade – Função Fatorial • Outra função recursiva: máximo divisor comum public int MDC(int p1, int p2) { if (p2==0) MDC = p1; else return MDC(p2, p1 % p2); } Recursividade – Algoritmos Recursivos • • • • • • • Pode ser conciso e elegante Pode ser difícil a compreensão dos detalhes de seu funcionamento computacional Pode requerer um exaustivo acompanhamento do programa O computador pode utilizar pilhas para esse controle A mente humana não é tão boa para tais tarefas Parece ser difícil para uma pessoa guardar uma longa seqüência de resultados e então voltar para terminar um trabalho Para acompanhar tais algoritmos, podemos utilizar, por exemplo, árvores de recursão Recursividade – Torres de Hanói Na criação do mundo, aos sacerdotes do Templo de Brahma (montanhas de Hanoi – sacerdotes brâmanes), foi dada uma plataforma de metal na qual existiam três hastes de diamante. Na primeira haste existiam 64 discos de ouro, cada um ligeiramente menor em relação ao que estava por baixo. Os sacerdotes foram incumbidos com a tarefa de mover todos os discos de ouro da primeira haste para a terceira, porém, com as seguintes condições : - Mover apenas um disco por vez; - Nenhum disco poderia ficar sobre um disco maior. Aos sacerdotes foi dito que quando eles terminassem de movimentar os 64 discos de ouro, o mundo terminaria!!! Recursividade – Torres de Hanói 1 • 3 Como poderíamos ajudar os sacerdotes? - • 2 Criando um programa que liste a ordem de operações que eles devem realizar... Pode-se resumir a tarefa na seguinte instrução: Move (64,1,3,2) • Que significa: “Mova 64 discos da torre 1 para a torre 3, usando a torre 2 como armazenamento temporário” Recursividade – Torres de Hanói • • • • • • Concentrar nossa atenção não no primeiro passo Mas sim, no passo mais difícil: mover o último disco. Como acessar o disco número 64? Lembre-se que somente um disco pode ser movido por vez E o último (o maior) não pode nunca estar sobre os demais Quando movemos o disco 1, não pode existir nenhum outro disco nas torres 1 e 3 Recursividade – Torres de Hanói • A primeira chamada da função, para mover os 64 discos é: Move (64, 1, 3, 2) • E a função Move, recursiva, pode ser descrita como: public void Move (int count, int start, int finish, int temp) { if (count > 0) { Move (count–1, start, temp, finish); /* Escrever na tela que o usuário deve mover o disco do topo da haste <start> para a <finish>*/ Move (count-1, temp, finish, start); } } Recursividade – Torres de Hanói • Observando a árvore de recursão, podemos concluir o seguinte: - Nr de movimentos para movimentar todos 64 discos é de: 264 –1 - Para tentar aproximar o valor, consideremos 210 103 - Número de passos de aproximadamente 1,6 * 1019 - Existem aproximadamente 3,2 * 107 segundos num ano - Vamos supor que as instruções sejam executadas num ritmo de um movimento por segundo - A tarefa total levará quase 5 x 1011 anos - Estimativa de vida do universo: mínimo 20 bilhões (2 * 1010) anos Recursividade – Torres de Hanói • Não existe um computador que rode o programa de Torres de Hanói para 64 discos • Ele falharia por falta de tempo • Mas certamente não por falta de espaço • O espaço necessário é somente para fazer o controle de 64 chamadas recursivas • Mas o tempo necessário requerido, é aquele para fazer 264 cálculos Recursividade – Criando Algoritmos a) Ache o passo chave: - “Como este problema pode ser dividido ? “ - “Como posso desenvolver o passo chave da solução ?” b) Ache uma regra de parada: - Uma regra de parada indica que o problema ou uma parte razoável do mesmo foi feito - Ela é geralmente pequena e dá a solução trivial, sem a necessidade da recursão c) Monte seu algoritmo: - Combine a regra de parada e o “passo chave” usando uma instrução IF Recursividade – Criando Algoritmos d) Verifique o término: - Certificar que a recursão sempre terminará - Comece com uma situação geral - Verifique se num número finito de passos, a regra de parada será satisfeita e a recursão termina e) Desenhe uma árvore de recursão: - É a ferramenta chave para analisar algoritmos recursivos Recursividade – Recursão de Cauda • Essa situação na qual a chamada recursiva é o último comando da função, é denominada recursão de cauda • Isso forma um “looping” que será repetido até que uma condição de parada seja satisfeita • A recursão de cauda pode sofrer transformações até ser obtido uma estrutura de iteração, quando observaremos ganhos em tempo e espaço Recursividade – Recursão de Cauda // Fatorial recursiva public int Fatorial(int n) { if (n==0)return 1; else return n*fatorial(n-1); } // Fatorial iterativa public int Fatorial (int n) { int count, product = 1; for (count = 1; count < = n; count ++) product*= count; return product; } Recursividade – Conclusão • A recursão é considerada uma abordagem top-down para resolver um problema • A interação está mais para um abordagem bottom-up • É sempre verdadeiro que a recursão pode ser substituída pela interação e pilhas • É verdade também, por outro lado, que qualquer programa interativo que manipula uma pilha pode ser substituído por um programa recursivo sem nenhuma pilha Fim