Ir para o conteúdo

Algoritmo

O algoritmo da Torre de Hanói

Uma função curta e o exemplo mais claro de recursão que existe na computação. Execute-a aqui embaixo e veja cada chamada que ela faz.

Nesta página
  1. A ideia
  2. O algoritmo
  3. Uma partida resolvida, jogada a jogada
  4. Execute
  5. O que observar
  6. A árvore de recursão
  7. Por que está correto
  8. A recorrência, resolvida direito
  9. Custo de tempo e de memória
  10. Sem recursão
  11. A partir de uma posição que não é o começo
  12. Teste você mesmo
  13. O algoritmo na sua linguagem
  14. As palavras desta página
  15. Perguntas frequentes

O algoritmo da Torre de Hanói move uma pilha de n discos de uma torre para outra em 2ⁿ − 1 jogadas, que é o mínimo possível. Quase sempre é escrito de forma recursiva, porque o quebra-cabeça é recursivo: o problema de mover n discos contém duas cópias do problema de mover n − 1, com uma única jogada entre elas.

A ideia

O maior disco é o único que não pode ficar em cima de nada, então comece por ele. Ele só consegue se mover quando nada está sobre ele e nada está na torre para onde ele vai — ou seja, todos os outros discos precisam estar empilhados na terceira torre. Então a solução inteira são três passos:

  1. Mova os n − 1 discos menores da origem para a torre auxiliar.
  2. Mova o maior disco da origem para o destino.
  3. Mova os n − 1 discos menores da auxiliar para o destino.

Os passos 1 e 3 são o mesmo quebra-cabeça com um disco a menos e com outra torre no papel de auxiliar. Resolva-os do mesmo jeito, e os que estão dentro deles também, até que a pilha a mover fique vazia. Se recursão em si é novidade para você, a recursão explicada com a Torre de Hanói começa do zero.

Esses três passos são um algoritmo de dividir para conquistar, que é onde a maioria dos cursos encaixa este quebra-cabeça. Ele divide o problema em dois subproblemas, conquista cada um por recursão e os combina com o passo de combinação mais barato que existe: uma única jogada. O que ele não é: uma divisão equilibrada. O merge sort corta a entrada ao meio; aqui tira-se um disco. É exatamente essa diferença que faz o Teorema Mestre não se aplicar, que é a armadilha de que trata uma seção mais adiante.

O algoritmo

Escrito por inteiro, é uma função com duas metades. O caso base é a versão que ela responde direto, sem chamar a si mesma — aqui n == 0: nenhum disco para mover, nada a fazer — e o caso recursivo é todo o resto: as três linhas que desmontam o quebra-cabeça e devolvem as peças à mesma função. Algumas versões param em n == 1 e movem o disco ali; as jogadas são idênticas, e a versão abaixo mantém a jogada em uma única linha.

Na forma numerada, com as torres nomeadas pelo trabalho que cada uma faz na chamada, e não pela letra:

ALGORITMO TOH(n, source, target, spare)

  1. Se n = 0, retorne. Não há nada para mover.
  2. TOH(n − 1, source, spare, target) — tire do caminho os n − 1 discos de cima. Eles vão para a auxiliar, então a auxiliar é o destino desta chamada, e a torre para onde o maior disco está indo é a que precisa ficar livre.
  3. Mova o disco n de source para target. Esta é a única linha que move alguma coisa.
  4. TOH(n − 1, spare, target, source) — traga esses n − 1 discos para o destino. Eles partem da torre que era a auxiliar, e a torre onde todos os discos começaram agora é a que precisa ficar livre.

Uma partida resolvida, jogada a jogada

Esta é a resposta desse algoritmo para três discos, antes de executar qualquer coisa. Sete jogadas, sete tabuleiros e o motivo de cada jogada ter de vir quando veio.

  1. Fase 1 · abrir caminho

    Disco 1, de A para C. O disco 3 não pode se mover enquanto os dois discos sobre ele não estiverem em B, e o disco 2 não pode ir para B enquanto o disco 1 estiver em cima dele. Então o menor disco se move primeiro, para C.

  2. Fase 1 · abrir caminho

    Disco 2, de A para B. O disco 2 está livre agora e B está vazia, então ele ocupa seu lugar na torre auxiliar.

  3. Fase 1 · abrir caminho

    Disco 1, de C para B. O disco 1 desce de volta sobre o disco 2. Os dois discos pequenos estão empilhados em B e o disco 3 está livre — que é todo o sentido das três primeiras jogadas.

  4. Fase 2 · o disco grande atravessa

    Disco 3, de A para C. Não há nada sobre o disco 3 nem em C, então o maior disco atravessa em uma única jogada. É a única das sete que a chamada mais externa faz por conta própria.

  5. Fase 3 · reconstruir por cima

    Disco 1, de B para A. A mesma dança de três jogadas de novo, ao contrário: agora B é a origem e A, onde todos os discos começaram, é a auxiliar. O disco 1 sai primeiro da frente do disco 2.

  6. Fase 3 · reconstruir por cima

    Disco 2, de B para C. O disco 2 atravessa para cima do disco 3. É válido porque o disco 3 é maior, que é a única condição que importa.

  7. Fase 3 · reconstruir por cima

    Disco 1, de A para C. O disco 1 pousa na pilha e o quebra-cabeça está resolvido em 7 jogadas — 2³ − 1, e o mínimo possível.

O quebra-cabeça inteiro de três discos, um tabuleiro por jogada, cada um como fica depois que aquela jogada foi feita — o disco sombreado é o que acabou de pousar. As sete jogadas formam três grupos: as jogadas 1 a 3 levam os dois discos pequenos para B, a jogada 4 é o maior disco atravessando, e as jogadas 5 a 7 trazem os pequenos de volta por cima dele. Os grupos um e três são o mesmo quebra-cabeça de dois discos, que é exatamente o que a função recursiva faz quando chama a si mesma duas vezes.

Repare no que as jogadas 1 a 3 e as jogadas 5 a 7 têm em comum: as duas são o quebra-cabeça de dois discos, jogado entre outro par de torres. Isso é a recursão, visível na saída antes mesmo de o programa rodar — e é por isso que a função abaixo precisa de apenas uma linha que move um disco.

Execute

As mesmas sete jogadas de novo, desta vez com o programa que as produz. Cada passo executa uma linha, e a pilha de chamadas lista todas as chamadas que começaram e ainda não terminaram. Experimente quatro ou cinco discos: o número de chamadas dobra a cada vez, e a pilha cresce um degrau.

hanoi(3, A, C, B)

Mover 3 discos de A para C.

Nada está rodando ainda. O primeiro passo faz esta chamada.

Passo 0 de 43

Pilha de chamadas

Profundidade 0 de no máximo 4

  1. Vazia. Nada foi chamado ainda.

Pseudocódigo

procedure hanoi(n, source, target, spare)
    if n == 0 then
        return
    hanoi(n - 1, source, spare, target)
    move disk n from source to target
    hanoi(n - 1, spare, target, source)
end procedure

Jogadas

7 no total

Escolha uma linha para levar o programa até aquela jogada.

Todas as jogadas que o programa faz com 3 discos
JogadaDiscoDePara
11AC
22AB
31CB
43AC
51BA
62BC
71AC
As 15 chamadas, na ordem em que o programa as faz
  1. hanoi(3, A, C, B)profundidade 1 · faz a jogada 4
  2. hanoi(2, A, B, C)profundidade 2 · faz a jogada 2
  3. hanoi(1, A, C, B)profundidade 3 · faz a jogada 1
  4. hanoi(0, A, B, C)profundidade 4 · n é 0 — retorna na hora
  5. hanoi(0, B, C, A)profundidade 4 · n é 0 — retorna na hora
  6. hanoi(1, C, B, A)profundidade 3 · faz a jogada 3
  7. hanoi(0, C, A, B)profundidade 4 · n é 0 — retorna na hora
  8. hanoi(0, A, B, C)profundidade 4 · n é 0 — retorna na hora
  9. hanoi(2, B, C, A)profundidade 2 · faz a jogada 6
  10. hanoi(1, B, A, C)profundidade 3 · faz a jogada 5
  11. hanoi(0, B, C, A)profundidade 4 · n é 0 — retorna na hora
  12. hanoi(0, C, A, B)profundidade 4 · n é 0 — retorna na hora
  13. hanoi(1, A, C, B)profundidade 3 · faz a jogada 7
  14. hanoi(0, A, B, C)profundidade 4 · n é 0 — retorna na hora
  15. hanoi(0, B, C, A)profundidade 4 · n é 0 — retorna na hora

O que observar

  • Os papéis giram. A torre B é a auxiliar da primeira chamada e o destino da chamada que ela faz. Cada nível de recursão reetiqueta as três torres, e esse reetiquetar é todo o truque.
  • A pilha nunca fica funda. Três discos nunca têm mais de 4 chamadas rodando ao mesmo tempo — uma por número de discos, de 3 até 0 — mesmo que o programa faça 15 chamadas ao todo.
  • Metade das chamadas não faz nada. Dessas 15 chamadas, 8 são de zero discos e retornam na hora. São o preço do caso base mais simples possível.
  • A linha que move roda exatamente 2ⁿ − 1 vezes. Sete, para três discos, ao longo dos 43 passos da execução.

O primeiro é o mais difícil de pegar enquanto o programa roda, porque nada se mexe no tabuleiro quando acontece: só mudam os nomes embaixo das torres. Aqui ele está parado: a primeira chamada, as duas abaixo dela a caminho da jogada 1, e a chamada que traz a pilha de volta.

  1. hanoi(3, A, C, B)

    A primeira chamada: mover os 3 discos de A para C. Isso deixa B como auxiliar. Origem A, destino C, auxiliar B.

  2. hanoi(2, A, B, C)

    A primeira chamada dela. O disco 3 não pode se mover com os discos 1 e 2 em cima, e eles não podem ir para C, para onde o disco 3 está indo. Então B, que há pouco era a auxiliar, é o destino, e C é a auxiliar. Origem A, destino B, auxiliar C.

  3. hanoi(1, A, C, B)

    Um nível abaixo, a mesma troca outra vez. Para liberar o disco 2, o disco 1 vai para C, e B volta a ser a auxiliar. Origem A, destino C, auxiliar B.

  4. hanoi(2, B, C, A)

    O disco 3 já atravessou. A segunda chamada da raiz traz os discos 1 e 2 de volta de B para cima dele, então agora B é a origem e A, onde todos os discos começaram, é a auxiliar. Origem B, destino C, auxiliar A.

hanoi(3, A, C, B) e três das chamadas abaixo dela, na ordem em que rodam, cada uma no tabuleiro como aquela chamada o encontra. Os discos sombreados são os que a chamada tem de mover. As torres ficam onde estão; só os nomes mudam. Cada chamada passa suas três torres adiante com duas delas trocadas — destino e auxiliar para a primeira chamada que faz, origem e auxiliar para a segunda — e é nisso que consiste todo o reetiquetar.

O segundo é mais fácil de duvidar do que de conferir, então aqui está a execução inteira com a altura da pilha em cada um de seus passos:

  • A execução leva 43 passos e faz 15 chamadas ao todo.
  • O número de chamadas na pilha sobe e desce entre 0 e 4, e chega a 4 em 8 desses passos — toda vez que uma chamada sem disco para mover começa e retorna.
  • A profundidade 4 é n + 1 para n = 3, e nenhum passo da execução passa disso.
Cada passo da execução com três discos, com o número de chamadas na pilha naquele instante. A forma é larga porque o trabalho dobra a cada disco acrescentado — 43 passos aqui, e 15 chamadas — e plana porque a memória não dobra: a pilha guarda uma chamada por número de discos, de 3 até 0, então ela encosta em 4 e para. Essa diferença entre a largura e a altura é Θ(2ⁿ) de tempo contra Θ(n) de memória, desenhada.

A árvore de recursão

Desenhe cada chamada como uma caixa sob a chamada que a fez e o programa vira uma árvore. Coloque cada caixa acima do número da jogada que ela faz e aparece outra coisa: ler as caixas da esquerda para a direita dá as jogadas em ordem. Com três discos, a jogada da própria primeira chamada — o maior disco — é a jogada 4, bem no meio, com a solução inteira de três jogadas para dois discos de cada lado.

  1. Jogada 1: a chamada com n = 1, de A para C, move o disco 1.
  2. Jogada 2: a chamada com n = 2, de A para B, move o disco 2, entre suas duas chamadas com n = 1.
  3. Jogada 3: a chamada com n = 1, de C para B, move o disco 1.
  4. Jogada 4: a chamada com n = 3, de A para C, move o disco 3, entre suas duas chamadas com n = 2.
  5. Jogada 5: a chamada com n = 1, de B para A, move o disco 1.
  6. Jogada 6: a chamada com n = 2, de B para C, move o disco 2, entre suas duas chamadas com n = 1.
  7. Jogada 7: a chamada com n = 1, de A para C, move o disco 1.
As 7 chamadas de hanoi(3, A, C, B) que movem um disco. Cada chamada fica acima das duas que faz, e cada uma está colocada acima do número da jogada que faz — então as caixas, lidas da esquerda para a direita, são a solução em ordem. As 8 chamadas com n = 0 não estão desenhadas: há uma pendurada de cada lado de cada caixa de baixo, e todas retornam sem fazer nada. Execute o programa acima e esta árvore o acompanha: a chamada em execução acende, as chamadas que esperam por ela ficam contornadas, e cada número de jogada se preenche quando aquela jogada é feita. Dê ao programa quatro ou cinco discos e a árvore é redesenhada naquele tamanho.

Essa simetria vale em qualquer tamanho. A jogada 2ⁿ⁻¹ é sempre o maior disco atravessando, e as jogadas antes e depois são a solução de (n − 1) discos jogada duas vezes, com duas torres trocando de papel. É por isso que uma lista de jogadas de qualquer tamanho se lê de relance, e por isso a solução de cinco discos é a de quatro, uma jogada, e a de quatro de novo.

Por que está correto

O algoritmo nunca quebra uma regra e sempre termina, e a demonstração é curta o bastante para ser dada por inteiro. A afirmação, para todo n, é:

Se os n discos do topo da torre de origem são os discos 1 a n, e todo disco em qualquer outro lugar é maior que n, então hanoi(n, source, target, spare) move esses n discos para o destino, de forma válida, e não toca em mais nada.

Para n = 0 não há nada para mover e nada é tocado.

Suponha que a afirmação vale para n − 1. Então cada um dos três passos é válido:

  1. A primeira chamada move os discos 1 a n − 1 para a auxiliar. Ela tem esse direito: qualquer outro disco, inclusive o disco n, é maior que n − 1.
  2. O disco n vai para o destino. Agora não há nada em cima dele, e o destino só tem discos maiores que n.
  3. A segunda chamada move os discos 1 a n − 1 da auxiliar para o destino. Válida pelo mesmo motivo: o disco n, agora embaixo deles, é maior.

Todos os n discos estão no destino e nada mais se moveu. Por indução, a afirmação vale para todo n, e o quebra-cabeça completo é o caso em que a origem tem todos os discos.

  1. Supostoa afirmação para n − 1

    A hipótese diz que uma chamada sobre n − 1 discos os move para onde mandarem, de forma válida, e não toca em mais nada. Aplicada aqui, ela leva os discos 1 a n − 1 da origem para a auxiliar. Ela tem esse direito: qualquer disco em que pudesse pousá-los — inclusive o disco n — é maior que qualquer um dos que está carregando.

  2. Provado aquiuma jogada válida

    Este é o único passo que a demonstração precisa defender sozinha, e é uma única jogada. O disco n está livre, porque o passo anterior o liberou. O destino não contém nada além de discos maiores que n, porque tudo o que é menor está na auxiliar. Então a jogada é válida.

  3. Supostoa afirmação para n − 1, de novo

    A mesma hipótese, usada uma segunda vez, traz os discos 1 a n − 1 da auxiliar para o destino. Válida pelo mesmo motivo de antes: o disco n, agora embaixo deles, é maior. Todos os n discos estão no destino e nada mais se moveu — que é a afirmação para n.

O passo indutivo, e o motivo de a demonstração ter quatro frases. Dois dos três painéis não fazem trabalho novo nenhum — são a hipótese para n − 1, gasta uma vez em cada sentido. Só o painel do meio precisa ser defendido, e é uma jogada. É isso que a indução compra: n discos custam exatamente uma ideia nova a mais que n − 1, e o caso base n = 0 não custa nenhuma, porque não há nada para mover.

A recorrência, resolvida direito

Seja T(n) o número de jogadas que o algoritmo faz para n discos. A função faz duas chamadas sobre n − 1 discos e uma jogada própria, e não faz jogada alguma quando não há discos:

T(n) = 2 · T(n − 1) + 1 T(0) = 0

Para resolvê-la, substitua a recorrência nela mesma e procure o padrão. Isso se chama desenrolar, ou telescopar:

T(n) = 2T(n − 1) + 1   = 2(2T(n − 2) + 1) + 1 = 4T(n − 2) + 3   = 4(2T(n − 3) + 1) + 3 = 8T(n − 3) + 7   = 2ᵏ · T(n − k) + (2ᵏ − 1) depois de k substituições. Tome k = n: T(n) = 2ⁿ · T(0) + 2ⁿ − 1 = 2ⁿ − 1

Enxergar um padrão não é prová-lo, então confirme por indução. T(0) = 0 = 2⁰ − 1. E se T(n − 1) = 2ⁿ⁻¹ − 1, então T(n) = 2(2ⁿ⁻¹ − 1) + 1 = 2ⁿ − 2 + 1 = 2ⁿ − 1. A forma fechada é exata para todo n.

Há um terceiro caminho, e ele é uma imagem que você já viu. A árvore de recursão acima é uma árvore binária completa: a raiz move n discos, as duas chamadas abaixo dela movem n − 1 cada, as quatro abaixo dessas movem n − 2, e assim por diante. A profundidade k tem, portanto, 2ᵏ chamadas, o último nível é a profundidade n − 1, e cada uma dessas chamadas faz exatamente uma jogada. Some os níveis e a conta sai como uma série geométrica:

T(n) = 1 + 2 + 4 + ⋯ + 2ⁿ⁻¹ n termos, cada um o dobro do anterior:   = 2ⁿ − 1

Três métodos — desenrolar, indução e a árvore — e uma resposta só. Vale conhecer, porque existe um quarto que é oferecido o tempo todo e está errado.

Esta não é uma recorrência do Teorema Mestre

Uma resposta comum — inclusive em tutoriais muito copiados — resolve isto com o Teorema Mestre. Ele não se aplica.

  • O Teorema Mestre vale para T(n) = aT(n/b) + f(n) com b > 1: cada subproblema é uma fração do original, como no merge sort.
  • Esta recorrência é T(n) = 2T(n − 1) + 1: o subproblema é um disco menor, não uma fração menor, então não há nenhum b para colocar.

Recorrências assim têm o seu próprio resultado, o de subtrair e conquistar: quando T(n) = aT(n − b) + f(n) com a > 1 e f um polinômio, T(n) cresce no máximo como aⁿᐟᵇ vezes esse polinômio. Bate com a resposta acima — mas desenrolar é mais curto que lembrar disso.

A mesma aritmética conta as chamadas. Toda chamada com n {">"} 0 discos faz mais duas, então há C(n) = 2C(n − 1) + 1 chamadas com C(0) = 1, o que desenrola para 2ⁿ⁺¹ − 1. São 2ⁿ − 1 chamadas que movem um disco, mais 2ⁿ que retornam na hora.

Custo de tempo e de memória

Cada chamada faz uma quantidade constante de trabalho além das suas duas chamadas recursivas, então o tempo de execução é proporcional ao número de chamadas: Θ(2ⁿ). Nenhum algoritmo mais esperto consegue superar isso, porque a própria saída tem 2ⁿ − 1 jogadas de comprimento — e 2ⁿ − 1 é um mínimo comprovado, não apenas o melhor que alguém encontrou. A prova de que nenhuma solução é mais curta tem página própria: por que o mínimo é 2ⁿ − 1.

A memória é outra história. As chamadas não existem todas ao mesmo tempo: uma chamada termina antes de a irmã começar, então a pilha guarda apenas uma chamada por nível, de n até 0. O espaço usado é Θ(n) — sessenta e cinco quadros de pilha para os sessenta e quatro discos da lenda, o que qualquer computador segura sem perceber. O que acaba é o tempo: 18.446.744.073.709.551.615 jogadas a um bilhão por segundo ainda são 584 anos.

Esse número é o da lenda, e a lenda é mais nova do que parece. Um templo onde sacerdotes movem sessenta e quatro discos de ouro, e o mundo acabando quando terminarem, aparece impresso pela primeira vez em 1884 — num artigo que anunciava um quebra-cabeça posto à venda em Paris no ano anterior, sob o nome de um professor que não existia. No ritmo dos monges, uma jogada por segundo, restam ao mundo 585 bilhões de anos, cerca de quarenta vezes a idade atual do universo. O matemático por trás do quebra-cabeça e da história foi Édouard Lucas, que provou à mão que 2¹²⁷ − 1 é primo e morreu aos 49 anos de uma infecção causada por um corte de um prato que um garçom deixou cair num banquete.

RecursivoIterativo
Jogadas feitas2ⁿ − 12ⁿ − 1
Trabalho realizado2ⁿ⁺¹ − 1 chamadas2ⁿ − 1 voltas do laço
TempoΘ(2ⁿ)Θ(2ⁿ)
Memória adicionalΘ(n) de pilha de chamadasΘ(n) — as torres, ou só um contador de n bits

Ponha números nessas fórmulas e os dois tipos de custo se separam. Cada disco a mais faz um pouco mais do que dobrar as jogadas e as chamadas, e acrescenta um único quadro à maior profundidade que a pilha alcança:

DiscosJogadasChamadasPilha mais profundaA um bilhão de jogadas por segundo
371547 nanossegundos
41531515 nanossegundos
53163631 nanossegundos
101.0232.047111 microssegundo
201.048.5752.097.151211 milissegundo
324.294.967.2958.589.934.591334 segundos
6418.446.744.073.709.551.61536.893.488.147.419.103.23165584 anos

A calculadora de jogadas mínimas calcula as jogadas e o tempo para qualquer número de discos, e a última linha — o tamanho da lenda — tem uma página só dela.

Sem recursão

As mesmas jogadas podem ser produzidas por um laço simples com duas regras:

  • Jogadas ímpares: o menor disco avança uma torre num circuito fixo — A → C → B para um número ímpar de discos, A → B → C para um número par.
  • Jogadas pares: faça a única jogada válida que não envolve o menor disco. Sempre existe exatamente uma.

Essas duas regras reproduzem a solução recursiva jogada a jogada; a página da solução iterativa mostra por quê, e a joga em um tabuleiro.

Há uma versão ainda mais estranha, que não guarda torre nenhuma: o disco, a origem e o destino da jogada m podem ser lidos nos dígitos binários de m. Essa ligação, e o código de Gray escondido dentro dela, estão em Torre de Hanói, binário e código de Gray.

A partir de uma posição que não é o começo

A função dos livros pressupõe que todos os discos começam na torre de origem. Dê a ela um quebra-cabeça pela metade e ela não tem nada a dizer. Mais um caso resolve isso. Olhe para o maior disco:

  • Já está na torre que ele precisa alcançar? Ele não precisa de jogada nenhuma. Resolva os discos menores mandando-os para essa mesma torre.
  • Está em outro lugar? Tire os discos menores para a torre que não é nem onde ele está nem para onde ele vai, mova-o e siga como antes.

Essa versão encontra a solução mais curta a partir de qualquer posição válida, e é ela que o solucionador da Torre de Hanói executa.

Teste você mesmo

Três perguntas que a página já respondeu. Resolva-as antes de abrir — se alguma não sair, a seção citada junto com a resposta é a que vale reler.

  1. Três discos são sete jogadas. Quantas são 5 discos, e quantas delas movem o próprio disco 5?

    Ver a resposta

    31 jogadas, que é 2⁵ − 1 — e exatamente uma delas move o maior disco. Todos os discos, menos o maior, se movem mais de uma vez, porque cada uma das duas metades da solução tem de movê-los. A recorrência, resolvida direito.

  2. hanoi(3, A, C, B) faz hanoi(2, A, B, C) como primeira chamada. Por que o destino dessa chamada é B e não C?

    Ver a resposta

    Porque o disco 3 vai para C, e só pode pousar lá se C estiver vazia. Então os dois discos acima dele têm de ficar estacionados em outro lugar, e esse outro lugar só pode ser B. A torre que uma chamada mira vira a torre que a primeira chamada dela precisa manter livre — e é por isso que dois dos três argumentos trocam de lugar na entrada. O que observar.

  3. Resolver 20 discos faz 2.097.151 chamadas. Quantas delas estão na pilha no mesmo instante, no máximo?

    Ver a resposta

    21 — uma por número de discos, de 20 até 0. Uma chamada termina antes de a irmã começar, então as chamadas nunca existem todas ao mesmo tempo: o trabalho é exponencial e a memória é linear. Custo de tempo e de memória.

O algoritmo na sua linguagem

A mesma função em seis linguagens, cada uma com um programa que roda, uma explicação linha a linha, o tabuleiro rodando ao lado e o que é específico daquela linguagem — o que passa a importar assim que você tenta contar as jogadas de sessenta e quatro discos. Essas seis páginas estão em inglês.

As palavras desta página

Caso base
A versão do problema que uma função recursiva responde de imediato, sem chamar a si mesma. É o que faz a recursão parar. Aqui é n = 0: nenhum disco para mover, então não há nada a fazer e a chamada retorna. Toda cadeia de chamadas, por mais funda que seja, termina em um deles.
Caso recursivo
Tudo o que não é o caso base: a parte da função que resolve uma versão menor do mesmo problema e constrói a resposta a partir dela. Aqui são as três linhas que tiram n − 1 discos do caminho, movem o disco n e trazem os n − 1 de volta.
Pilha de chamadas
A lista de chamadas de função que começaram e ainda não terminaram, a mais recente por último. Uma chamada entra quando começa e sai quando retorna, então a pilha é como um programa lembra para onde voltar. É o que o painel acima desenha, e é memória de verdade — por isso a profundidade dela é o custo de espaço do algoritmo.
Quadro de pilha
Uma entrada da pilha de chamadas: os argumentos e as variáveis locais de uma única chamada. Cada linha da pilha no painel é um quadro, com o n daquela chamada e a ideia própria dela de qual torre é a origem, o destino e a auxiliar. Sessenta e quatro discos precisam de sessenta e cinco quadros, o que não é nada; o problema são as jogadas.
Telescopar
Também chamado de desenrolar: resolver uma recorrência substituindo-a nela mesma até o padrão ficar óbvio, e então fixar o número de substituições para que só reste o valor inicial conhecido. Chama-se telescopar porque os termos do meio se encaixam como as seções de uma luneta.
Θsaid “theta maiúsculo”
Uma taxa de crescimento que é ao mesmo tempo limite superior e inferior: Θ(2ⁿ) quer dizer que o custo cresce como 2ⁿ, e não apenas que não cresce mais rápido que isso. O(2ⁿ) prometeria só o teto. Θ é a notação honesta aqui, porque este algoritmo realmente faz 2ⁿ − 1 jogadas — ele não tem como dar sorte e terminar antes.

Perguntas frequentes

Como funciona o algoritmo recursivo da Torre de Hanói?

Para mover n discos de uma torre de origem para uma torre de destino, ele primeiro move os n − 1 discos de cima para a torre auxiliar, depois move o maior disco para o destino, e depois move os n − 1 discos da auxiliar para o destino. Cada uma dessas duas transferências menores é o mesmo problema com um disco a menos, então a função chama a si mesma para elas, e uma chamada sem disco para mover simplesmente retorna.

A Torre de Hanói é dividir para conquistar ou programação dinâmica?

É dividir para conquistar. O problema é dividido em dois subproblemas de n − 1 discos, cada um é conquistado por recursão, e o passo de combinação é uma única jogada do maior disco. Não é programação dinâmica, e memoização não ajuda — mesmo que os subproblemas de fato se repitam. Com três torres só há seis maneiras de nomear origem, destino e auxiliar, então hanoi(1, A, C, B) é realmente chamada duas vezes ao resolver três discos. Guardar isso em cache não rende nada, porque a resposta de um subproblema é uma lista de 2ᵏ − 1 jogadas, e repetir uma lista guardada desse tamanho custa o mesmo que produzi-la. Aqui o trabalho é a saída, e nenhuma tabela deixa a saída mais curta.

O Teorema Mestre se aplica à recorrência da Torre de Hanói?

Não. O Teorema Mestre cobre recorrências da forma T(n) = aT(n/b) + f(n), em que cada subproblema é uma fração do original. A recorrência da Torre de Hanói é T(n) = 2T(n − 1) + 1: o subproblema é um disco menor, não uma fração do tamanho. Desenrolá-la dá T(n) = 2ⁿ − 1 direto, e a indução confirma.

Até que profundidade vai a recursão?

Com caso base em n = 0, há no máximo n + 1 chamadas na pilha ao mesmo tempo: uma por número de discos, de n até 0. Com caso base em n = 1, são n. De um jeito ou de outro a profundidade cresce linearmente, e é por isso que a solução recursiva precisa de apenas O(n) de memória mesmo fazendo um número exponencial de jogadas.

Quantas chamadas de função a solução recursiva faz?

2ⁿ⁺¹ − 1 chamadas quando o caso base é n = 0: as 2ⁿ − 1 chamadas que movem um disco, mais 2ⁿ chamadas que não encontram nada para mover e retornam. Três discos fazem 15 chamadas. Escrito com caso base em n = 1, o mesmo programa faz exatamente 2ⁿ − 1 chamadas, uma por jogada.

O algoritmo iterativo é mais rápido que o recursivo?

Não de um jeito que importe. Os dois precisam produzir 2ⁿ − 1 jogadas, então os dois levam tempo proporcional a 2ⁿ. A versão iterativa evita o custo das chamadas de função e não tem como estourar a pilha, mas a recursão aqui tem só n níveis de profundidade: estourar a pilha não é risco real para nenhum número de discos que você pudesse esperar.

Como é o algoritmo da Torre de Hanói em um fluxograma?

Não fica grande coisa, e essa é a resposta honesta. Um fluxograma mostra o controle passando por caixas uma vez, e a segunda caixa deste algoritmo é o fluxograma inteiro de novo — três níveis abaixo, com duas torres trocadas e um disco a menos. Um fluxograma não tem notação para isso, então um fluxograma da solução recursiva ou está errado, ou é a figura de uma única chamada com duas caixas escritas “chamar hanoi”, o que não ensina nada além do que o pseudocódigo de quatro linhas ensina. O diagrama que expressa isso é a árvore de recursão: cada chamada desenhada sob a chamada que a fez, com cada caixa acima da jogada que ela faz. Ela está nesta página, e ler as caixas da esquerda para a direita dá as jogadas em ordem.

O algoritmo consegue resolver um quebra-cabeça já pela metade?

A versão dos livros não: ela pressupõe que todos os discos começam em uma torre. Ela se generaliza com um caso a mais — se o maior disco já está no destino dele, pule-o e resolva os discos acima — e essa versão encontra a solução mais curta a partir de qualquer posição válida. O solucionador deste site executa exatamente isso. Abrir o solucionador →