Explorador da Árvore de Recursão

Escolha uma definição recursiva, estipule um valor para n e observe quantas chamadas são efetuadas e quantas delas solucionam um problema que já foi resolvido em outro ramo.

A carregar a simulação interativa...

Fibonacci, calculado pela contagem de uns 🖖

Atribua 20 a n. A árvore suporta 21.891 chamadas. Destas, 10.946 encerram num caso-base e as restantes 10.945 efetuam adições. Apenas fib(1) devolve 1 e fib(0) devolve 0, pelo que 6.765 dessas folhas carregam o número 1 e 4.181 não carregam nada. O cálculo de F(20) resulta em 6.765, que é com exatidão a quantidade de uns. Cada valor que a recursão retorna consiste na soma de alguns deles, e nada nessa lógica sofre alterações com o aumento de n. O cartão relativo aos casos-base exibe sempre F(n+1), ao passo que a contagem dos uns entre eles totaliza sempre F(n).

Duas árvores de 21.891 chamadas, e apenas uma se repete 🖖

A versão ingênua de fib(20) e o merge sort para 10.946 itens exigem precisamente a mesma quantidade de chamadas: 21.891, segmentadas em 10.946 casos-base e 10.945 passos de combinação. Três dos seis cartões apresentam leituras idênticas. Ambas as definições se ramificam por dois caminhos a cada nó interno, o que nos diz que não é a ramificação que as distingue. O cartão dos subproblemas distintos oferece a resposta: 21 contra 21.891. Uma tabela de memoização encolhe a árvore de Fibonacci para as 21 unidades de trabalho e preserva intacta a do merge sort nas 21.891. Isto ocorre porque as metades num merge sort formam fatias distintas de um único arranjo. O acesso à memória para resgatar uma resposta gravada de "uma sequência de 5.473 itens" iria devolver invariavelmente os 5.473 itens incorretos.

Um único 1 subtraído afasta φⁿ de 2ⁿ 🖖

Hanói e Fibonacci realizam duas chamadas recursivas cada um e atingem profundidades praticamente iguais, 20 contra 19. Hanói para n = 20 perfaz 2.097.151 chamadas. Fibonacci executa 21.891, uma contagem 95,8 vezes menor. A única diferença estrutural no código é o segundo argumento da recursão. Hanói chama a si próprio para n−1 duas vezes; Fibonacci atua sobre n−1 e, em seguida, sobre n−2. Essa subtração solitária derruba a base da exponencial de 2 para φ = 1,618, e o fosso não para de alargar-se. No limiar de n = 30 atingimos 2.147.483.647 contra 2.692.537, um fator de 798.

Problemas resolvidos na íntegra

  1. A forma fechada da contagem de chamadas quando o fib(10) ingénuo faz 177 chamadas 6 passos

    A ferramenta assegura que o fib(10) ingênuo opera 177 chamadas e o fib(11) efetua 287. Deduza a forma fechada para a contagem das chamadas, prove a sua precisão, e calcule em seguida a que distância chega o Fibonacci ingênuo num segundo, recorrendo a uma máquina com capacidade para processar 108 chamadas por segundo.

    1. Acrescente uma unidade a cada contagem: 178 e 288. Ambas as contagens se revelam pares, com as respetivas metades assentes em 89 e 144, valores que correspondem ao F11 e F12. A conjectura assume então o formato C(n) = 2F(n+1) − 1, estipulando que F(1) = F(2) = 1.

    2. A árvore clarifica de imediato a origem da referida duplicação. Qualquer nó interno faz rigorosamente duas chamadas e cada folha não faz nenhuma. Como consequência, uma árvore com L folhas contém L − 1 nós internos, somando um total absoluto de 2L − 1 nós. A ferramenta reporta 89 casos-base ao redor de n = 10, e 2 × 89 − 1 = 177.

    3. Tratamos os casos-base primeiro. As funções fib(0) e fib(1) retornam desprovidas de ciclos recursivos, fixando C(0) = C(1) = 1. Em simultâneo, tanto 2F(1) − 1 como 2F(2) − 1 resultam inevitavelmente em 1.

    4. Avançamos ao passo de indução. Tome como certa a validade da fórmula em n − 1 e n − 2. A chamada no índice n traduz-se na adição de um nó às suas duas subárvores. Os dois números de Fibonacci adjacentes contraem-se devido à própria definição elementar da sequência. Fixando n em 11 obtemos a prova 2 × 144 − 1 = 287, refletindo a cifra que o sistema imprime.

    5. Deixamos as árvores por aqui. A fórmula de Binet impõe a igualdade F(m) = (φm − ψm)/√5 com φ = 1,6180 e |ψ| < 1, comprimindo F(m) no arredondamento mais próximo de φm/√5. Consequentemente, C(n) ≈ 2φn+1/√5. Um segundo com um desempenho de 108 chamadas por segundo rende 108 chamadas, o que fixa de forma peremptória φn+1 nos 1,1180 × 108.

    6. Aplique os logaritmos. Sabendo de antemão que ln(1,1180 × 108) = 18,5323 e que ln φ = 0,4812, conclui-se que n + 1 = 38,51 e por fim n = 37,5. O arredondamento tem obrigatoriamente de ser feito para baixo, uma vez que uma chamada fracionária jamais atinge o fim do processamento.

    Resposta

    n = 37. A ferramenta assegura a marcação dos dois vizinhos: 78.176.337 chamadas em n = 37, escalando até 126.491.971 em n = 38. A partir desse marco, qualquer avanço de 1,44 em n conduzirá a uma duplicação inequívoca da carga, devido ao facto de ln 2 / ln φ = 1,44. Em simultâneo, o cartão dos subproblemas distintos aponta 38 em n = 37. Setenta e oito milhões de chamadas com o intuito estrito de dar resposta a trinta e oito questões é o derradeiro argumento em defesa da programação dinâmica agrupado numa única linha.

  2. Três ramos que crescem mais devagar do que dois 6 passos

    As escadas de três passos chamam-se a si próprias três vezes em cada nó; as Torres de Hanói chamam-se duas. Carregue Escadas em 20, 3 ramos, descubra qual das duas árvores cresce mais depressa e decida quanto vale o fator de ramificação como guia.

    1. Leia a recorrência a partir da definição e não da imagem. Uma chamada para o próprio nó, depois mais três em n − 1, n − 2 e n − 3, e tudo o que esteja em 2 ou abaixo devolve o valor sem recorrer.

    2. Assuma que a contagem cresce geometricamente, C(n) ≈ A xⁿ, e substitua. Divida tudo por x elevado a n − 3 e o 1 inicial fica para trás como ruído de ordem inferior, restando uma cúbica. A sua única raiz real é 1,8392868, a constante tribonacci.

    3. A mesma receita nas outras duas definições. Fibonacci chama-se em n − 1 e n − 2, logo x² = x + 1 e a raiz é φ = 1,6180340. Hanói chama-se duas vezes em n − 1, logo x = 2 e não há nada a resolver.

    4. Confirme em vez de acreditar. Ponha n em 29, leia o cartão das chamadas efetuadas, ponha n em 30, leia-o outra vez e divida. As escadas dão 1,83929, Fibonacci 1,61803 e Hanói exatamente 2. Cinco casas decimais nas três.

    5. O que a raiz não dá é a contagem. 1,8393²⁰ é 196.331 enquanto o cartão marca 128.287, pelo que existe um fator de 0,65 à frente; as chamadas de Hanói são exatamente 2ⁿ⁺¹ − 1, um fator de 2. Os casos-base fixam essa constante e a recorrência fixa a raiz, e só a raiz decide quem ganha a longo prazo.

    6. O longo prazo chega depressa aqui. Em n = 20 Hanói faz 16,3 vezes mais chamadas do que as escadas, e em n = 30 faz 37,8 vezes mais. A razão duplica a cada 8,3 passos, uma vez que ln 2 a dividir por ln(2 ÷ 1,8393) dá 8,27.

    Resposta

    Hanói cresce mais depressa, 2 contra 1,8393, e o terceiro ramo não custa às escadas nada que não possam pagar. O fator de ramificação é um limite superior da taxa de crescimento, e só é atingido quando todos os filhos ficam um passo mais pequenos. As escadas gastam duas das suas três chamadas em argumentos que descem 2 e 3, e essas subárvores são baratas ao ponto de nunca somarem o ramo que pareciam ter ganho.

    O que se deve guardar é a receita, porque funciona numa definição que ninguém resolveu por si. Conte os filhos, anote quanto cada um baixa o argumento, escreva x elevado à maior descida como a soma de x elevado às descidas restantes e tome a maior raiz real. Vai com um aviso: isto é um facto sobre a árvore ingénua, e a árvore ingénua não é o que alguém escreveria. O cartão dos subproblemas distintos marca 21 em n = 20 para as escadas e 21 para Hanói, pelo que uma memoização elimina o expoente por completo e a taxa de crescimento deixa de importar.

Percurso de aprendizagem

Contar trabalho, não segundos

Conduz a Procura de caminhos

Problemas de exemplo

  • Fibonacci em 20 - 21.891 chamadas distribuídas por 21 subproblemas distintos: 1.042,4 chamadas para cada um
  • Merge sort, mesmo total - As mesmas 21.891 chamadas, mas correspondendo todas elas a um subproblema diferente
  • Hanói em 20 - 2.097.151 chamadas em 21 subproblemas distintos, e 1.048.575 movimentos de disco
  • Regra Pascal, C(20,10) - 369.511 chamadas para preencher 120 células do triângulo de Pascal
  • Escadas em 20, 3 ramos - Três ramificações em vez de duas: 128.287 chamadas, 21 subproblemas