250 likes | 367 Views
Torres de Hanoi. Marco Antonio Montebello Júnior marco.antonio@aes.edu.br. Estrutura de Dados. Torres de Hanoi.
E N D
Torres de Hanoi Marco Antonio Montebello Júnior marco.antonio@aes.edu.br Estrutura de Dados
Torres de Hanoi “No século XIX, um jogo chamado Torres de Hanoi apareceu na Europa, junto com um material promocional explicando que o jogo representava uma tarefa conduzida no Templo de Brahma (montanhas de Hanoi – sacerdotes brâmanes). Dizia, ainda, que na criação do mundo, aos sacerdotes foi dada uma plataforma de metal na qual existiam três hastes de diamante. Estrutura de Dados
Torres de Hanoi Na primeira haste existiam 64 discos de ouro, cada um ligeiramente menor em relação ao que estava por cima. (A versão mais exótica vendida na época na Europa tinha 8 (oito) discos e 3 (três) hastes de madeira). Estrutura de Dados
Torres de Hanoi Os sacerdotes foram incumbidos com a tarefa de mover todos os discos de ouro do primeiro disco para o terceiro, porém, com as seguintes condições: a. Mover apenas um disco por vez; b. E nenhum disco poderia ficar sobre um disco menor. Estrutura de Dados
Torres de Hanoi Entretanto, poderiam utilizar qualquer haste de diamante como descanso para os discos. Aos sacerdotes foi dito que quando eles terminassem de movimentar os 64 discos de ouro, significaria o fim do mundo!!!” Estrutura de Dados
Torres de Hanoi Estrutura de Dados
Desenvolvendo uma solução para Torres de Hanoi • 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 Estrutura de Dados
Resumindo os passos do algoritmo de Torres de Hanoi • Move (63, 1, 2, 3) • Move 63 discos da torre 1 para 2 (torre 3 temporária) • Escreva: “Mova o disco número 64 da torre 1 para torre 3” • Move (63, 2, 3, 1) • Move 63 discos da torre 2 para torre 3 (torre 1 temporária) Estrutura de Dados
Refinamento • Para escrever o algoritmo na sua forma final, nós devemos saber em cada passo, qual torre será utilizada como armazenamento temporário, e assim, nós construímos a função “move” com as seguintes especificações: • void move(int qtd, int inicio, int fim, int aux) • Pré-condição: Existe no mínimo qtd discos na torre inicio. O disco de topo (se existir) nas torres aux e fim, é maior do que qualquer outros qtd discos presentes na torre inicio. • Pós-Condições: Os qtd discos que estavam na torre inicio foram movidos para a torre fim. A torre temp (usada como armazenamento temporário) retornou para sua posição inicial. Estrutura de Dados
Refinamento • Nossa tarefa deve terminar num número finito de passos. • Uma regra óbvia é que, quando não existir mais discos para ser movido, não há mais nada para fazer... Estrutura de Dados
Analisando um programa que considera as regras anteriores #define discos = 64; //faça essa constante ser menor para testar o programa /* Pré: Nenhum Pós: A simulação das torres de Hanoi terminou */ void move(int qtd, int inicio, int fim, int aux); main() { move (discos, 1, 3, 2); } void move(int qtd, int inicio, int fim, int aux) { if(qtd > 0) { move(qtd – 1, inicio, aux, fim); printf(“Mova disco %i de %i para %i”, qtd, inicio, fim); move(qtd - 1, aux, fim, inicio); } } Estrutura de Dados
Acompanhamento / Análise • Acompanhamento e análise do programa recursivo de Torres de Hanói com árvore de recursão. • Demonstração: • move(3, 1, 3, 2); Estrutura de Dados
Analisando a árvore de recursão • Uma instrução é exibida para cada vértice (ou nó) • Exceto para as folhas que são chamadas com count == 0 • O número de vértices tipo não-folhas é: • 1 + 2 + 4 +...+ 263 = 20 + 21 + 22+ . . . + 263 = 264 –1 Estrutura de Dados
Podemos concluir o seguinte • Número de movimentos para movimentar todos 64 discos é de: 264 -1 • Este valor pode ser estimado por: • 103 = 1000 1024 = 210 • Número aproximado de movimentos é: • 264 = 24 x 260 24 x 1018 = 1,6x1019 • Existem aproximadamente 3,2x107 segundos num ano • Suponha instruções num ritmo de um movimento por segundo • A tarefa total levará quase 5x1011 anos • Estimativa de vida do universo: mínimo 20 bilhões (2x1010) anos • Duração do mundo: 25 vezes mais do que já tem!!! Estrutura de Dados
Consideração de tempo (execução) e espaço (memória) do algoritmo recursivo de Torres de Hanoi • Não existiria um computador que rodaria o programa de Torres de Hanoi • 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 das 64 chamadas recursivas • Mas o tempo necessário requerido, é aquele para fazer 264 cálculos Estrutura de Dados
Desenvolvendo algoritmos recursivos • Ache o passo chave • Como este problema pode ser dividido? • Como posso desenvolver o passo chave da solução? • 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 • Monte seu algoritmo • Combine a regra de parada e o “passo chave” usando uma instrução if Estrutura de Dados
Desenvolvendo algoritmos recursivos • 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 • Desenhe uma árvore de recursão • É a ferramenta chave para analisar algoritmos recursivos Estrutura de Dados
Tipos de recursividadeRecursã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 obtida uma estrutura de interação, quando observaremos ganhos em tempo e espaço Estrutura de Dados
ExemploFatorial /* Fatorial : versão recursiva Pós : n é um inteiro não negativo Pré : retorna o valor do fatorial de n */ int fatorial(int n) { if (n == 0) return 1; else return n*fatorial(n-1); } /* fatorial : versão interativa Pós : n é um inteiro não negativo Pré : retorna o valor do fatorial de n */ int fatorial(int n) { int count, product = 1; for(count = 1; count < = n; count ++) product*= count; return product; } Estrutura de Dados
Considerando os exemplos anteriores • Qual dos programas usa menos espaço de memória? • Acompanhe a execução recursiva para n = 5 fatorial(5) = 5*fatorial(4) = 5*(4*fatorial(3)) = 5*(4*(3*fatoriall(2))) = 5*(4*(3*(2*fatorial(1)))) = 5*(4*(3*(2*(1*(fatorial(0)))))) = 5*(4*(3*(2*(1*)))) = 5*(4*(3*(2*1))) = 5*(4*(3*2)) = 5*(4*6) = 5*24 = 120 Estrutura de Dados
Diretrizes e conclusões gerais • A árvore de recursão é uma boa possibilidade para ajudar a decisão entre utilizar um algoritmo recursivo e não recursivo • Se ela tiver uma forma simples, a versão interativa pode ser melhor • Se ela envolver tarefas de duplicação, outras estruturas que não sejam pilhas poderão ser mais apropriadas, então a necessidade de recursão pode desaparecer • Se a árvore de recursão parece ser consistente, com poucas tarefas de duplicação, então a recursão pode ser o método mais natural Estrutura de Dados
Ainda mais... • 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 Estrutura de Dados
Mensagem sobre a utilização da recursão • “Um programador cuidadoso deve não somente perguntar, se a recursão deve ser removida, mas também perguntar, quando um programa usa pilhas, se a introdução da recursão poderia produzir um programa mais natural e de melhor entendimento, agregando melhorias na abordagem e nos resultados do problema” Estrutura de Dados