Recursão
Mover uma Torre de Hanói de 64 discos leva 18 446 744 073 709 551 615 movimentos; uma função recursiva descreve a solução em quatro linhas.
Uma função que chama a si mesma
Alguns problemas contêm uma cópia menor de si mesmos. O fatorial de 5, escrito 5!, é 5 × 4 × 3 × 2 × 1 = 120. Olhe de novo: é 5 × (4 × 3 × 2 × 1), ou seja, 5 × 4!. Em geral n! = n × (n − 1)!, então para calcular um fatorial você pode calcular um fatorial menor e multiplicar. Uma função que resolve um problema chamando a si mesma numa versão menor é recursiva.
function factorial(n) {
if (n <= 1) return 1; // caso base: para aqui
return n * factorial(n - 1); // caso recursivo: um problema menor
}
console.log(factorial(5)); // mostra 120Toda função recursiva precisa de duas partes. O caso base é uma entrada pequena o bastante para ser respondida direto, sem outra chamada: aqui, o fatorial de 1 é 1. O caso recursivo diminui o problema e chama a função de novo. Cada chamada precisa chegar mais perto do caso base. Se não chegar, por exemplo se você escrever factorial(n + 1) por engano, as chamadas nunca terminam.
A pilha de chamadas
Como o computador acompanha todas essas chamadas inacabadas? Quando factorial(5) roda, ele chega a 5 * factorial(4) e precisa esperar a resposta. O computador põe a chamada que espera numa pilha, um monte em que o último item colocado é o primeiro a sair. factorial(4) espera factorial(3), e assim por diante até factorial(1), que devolve 1 na hora. Aí a pilha se desfaz: factorial(2) devolve 2, factorial(3) devolve 6, factorial(4) devolve 24 e factorial(5) devolve 120.
A pilha tem tamanho limitado. Uma recursão que nunca chega ao caso base continua acrescentando chamadas até o espaço acabar, e o JavaScript para com um erro; no Chrome e no Node.js a mensagem é "RangeError: Maximum call stack size exceeded". O mesmo limite faz com que uma recursão muito profunda, de centenas de milhares de níveis, fique melhor escrita como laço.
A Torre de Hanói
A Torre de Hanói é um quebra-cabeça publicado pelo matemático francês Édouard Lucas em 1883. Há três pinos e uma pilha de discos de tamanhos diferentes no primeiro pino, o maior embaixo. O objetivo é levar a pilha inteira para o terceiro pino. Você só pode mover um disco por vez, só o disco de cima de um pino, e nunca pôr um disco maior sobre um menor.
Pensar passo a passo para onde vai cada disco fica confuso rápido. Pensar de forma recursiva deixa tudo curto. Para mover n discos de A para C: primeiro tire do caminho os n − 1 discos de cima, de A para B. Depois mova o maior disco de A para C. Depois mova os n − 1 discos de B para cima dele, em C. Mover n − 1 discos é o mesmo quebra-cabeça, um tamanho menor, e mover um disco é o caso base. Para 3 discos isso dá 7 movimentos; o laboratório mostra a pilha de chamadas esperando a cada movimento.
function hanoi(n, from, to, via) {
if (n === 0) return;
hanoi(n - 1, from, via, to);
console.log("disk " + n + ": " + from + " -> " + to);
hanoi(n - 1, via, to, from);
}
hanoi(3, "A", "C", "B"); // 7 movimentosArraste para girar. Role ou faça pinça para dar zoom.
Quantos movimentos levam n discos? Mover n discos custa dois movimentos de n − 1 discos mais um: M(n) = 2M(n − 1) + 1, com M(1) = 1. Isso dá 1, 3, 7, 15, 31, então M(n) = 2ⁿ − 1, e nenhuma solução consegue usar menos. Dez discos levam 1023 movimentos. Sessenta e quatro discos levam 2⁶⁴ − 1 = 18 446 744 073 709 551 615 movimentos; a um movimento por segundo, isso dá cerca de 585 bilhões de anos. É o crescimento O(2ⁿ) da unidade anterior.
Recursão, laços e trabalho repetido
Tudo o que se escreve com recursão pode ser escrito com um laço e uma pilha própria, e casos simples como o fatorial muitas vezes ficam mais claros como laços. A recursão brilha quando o próprio problema tem forma de árvore: dividir uma lista no merge sort, explorar pastas dentro de pastas ou buscar num labirinto, que é o assunto da última unidade. Uma armadilha é o trabalho repetido. Os números de Fibonacci 0, 1, 1, 2, 3, 5, 8, … seguem fib(n) = fib(n − 1) + fib(n − 2). Escrito direto como recursão, fib(10) = 55 faz 177 chamadas, fib(20) faz 21 891 e fib(30) faz 2 692 537, porque os mesmos valores menores são calculados de novo e de novo. Lembrar as respostas já encontradas, ou construir de baixo para cima com um laço, acaba com esse desperdício.
| recursão | Uma função que resolve um problema chamando a si mesma numa versão menor |
| caso base | Uma entrada respondida direto, que faz as chamadas pararem |
| caso recursivo | A parte que diminui o problema e chama de novo |
| pilha de chamadas | O monte de chamadas esperando uma resposta, último a entrar, primeiro a sair |
| estouro de pilha (stack overflow) | Chamadas esperando demais: RangeError em JavaScript |
| fatorial n! | n × (n − 1) × … × 1; 5! = 120 |
| Torre de Hanói | Lucas, 1883: 2ⁿ − 1 movimentos para n discos |
- Quanto é 6! (6 fatorial)?
- Quantos movimentos a Torre de Hanói leva com 4 discos?
- Quantos movimentos com 10 discos?
- O que o caso base faz?
- Responde direto a uma entrada pequena e interrompe a cadeia de chamadas
- Faz a função rodar mais rápido
- Chama a função com uma entrada maior
- Mostra o resultado
- O que acontece se uma função recursiva nunca chega ao caso base?
- Devolve 0
- A pilha de chamadas enche e o programa para com um erro
- Fica num laço silencioso para sempre, sem erro
- O JavaScript escolhe um caso base para você
- Em que ordem uma pilha tira os itens?
- Último a entrar, primeiro a sair
- Primeiro a entrar, primeiro a sair
- O menor primeiro
- Ao acaso
- Enquanto factorial(5) está calculando factorial(1), quantas chamadas de factorial estão na pilha, contando essa?
- Para mover n discos de A para C, qual é o primeiro passo do plano recursivo?
- Mover os n − 1 discos de cima de A para B
- Mover o maior disco para C
- Mover o menor disco para C
- Mover todos os discos para B
- Quem publicou o quebra-cabeça da Torre de Hanói em 1883?
- Leonhard Euler
- Édouard Lucas
- Tony Hoare
- Euclides
- O fib recursivo simples faz 177 chamadas para fib(10). Quanto é fib(10)?
- Por que o fib recursivo simples é lento?
- Calcula os mesmos valores menores muitas vezes
- A recursão é sempre lenta
- As somas custam caro
- Não tem caso base
- Uma função recursiva chama a si mesma numa versão menor do problema.
- Ela precisa de um caso base que interrompa as chamadas.
- As chamadas que esperam ficam na pilha de chamadas, último a entrar, primeiro a sair.
- Torre de Hanói: 2ⁿ − 1 movimentos; 64 discos levariam cerca de 585 bilhões de anos a um movimento por segundo.
- Cuidado com o trabalho repetido, como no Fibonacci recursivo simples.
Fontes deste curso (11)
- MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
- lecture notes 1 Introduction, 3 Sorting, 9 Breadth-First Search, 10 Depth-First Search (ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes) and Fall 2011 lecture 13 BFS notes (ocw.mit.edu/courses/6-006-introduction-to-algorithms-fall-2011)
- Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (MIT Press)
- with the CLRS-based Khan Academy algorithms unit by Thomas Cormen and Devin Balkcom of Dartmouth (khanacademy.org/computing/computer-science/algorithms: binary search, selection sort analysis, merge sort, quicksort overview and analysis)
- MacTutor History of Mathematics, Al-Khwarizmi (mathshistory.st-andrews.ac.uk/Biographies/Al-Khwarizmi: word algorithm from the Latin title Algoritmi de numero Indorum)
- D. E. Joyce, Euclid's Elements Book VII Propositions 1 and 2 (mathcs.clarku.edu/~djoyce/elements/bookVII)
- Wolfram MathWorld, Merge Sort (mathworld.wolfram.com/MergeSort.html: proposed by John von Neumann in 1945, after Knuth 1998 p. 159)
- Stanford CS "Tony Hoare: Quicksort" (cs.stanford.edu/people/eroberts/courses/soco/projects/2008-09/tony-hoare/quicksort.html: devised while a student at Moscow State University for Russian-English machine translation with a tape dictionary, programmed in Algol 60 and published 1961; the year 1959 from the Wikipedia Quicksort article and its cited sources)
- Bodleian Libraries, Ada Lovelace (blogs.bodleian.ox.ac.uk/adalovelace: 1843 paper, Note G Bernoulli numbers)
- Fourmilab text of Menabrea's Sketch with Lovelace's notes (fourmilab.ch/babbage/sketch.html)
- Seven Bridges of Konigsberg (Euler 1736) and Tower of Hanoi (Edouard Lucas, 1883) checked against the Wikipedia articles and their cited sources.
Data de verificação: 2026-10-17