Explorateur d'arbres de récursivité

Choisissez une définition récursive. Fixez n. Regardez combien d'appels sont effectués, et combien résolvent un problème déjà traité ailleurs.

Chargement de la simulation interactive...

Fibonacci, calculé en comptant des un 🖖

Fixez n à 20. L'arbre contient 21 891 appels. Parmi eux, 10 946 atteignent un cas de base, et les 10 945 autres sont des additions. Seul fib(1) renvoie 1, fib(0) renvoyant 0. Sur toutes ces feuilles, 6 765 portent donc un un et 4 181 ne portent rien. F(20) vaut 6 765, ce qui correspond très exactement au nombre de un. Chaque valeur renvoyée par la récursivité est une somme de certains d'entre eux. Ce principe ne change pas avec n : la carte des cas de base indique toujours F(n+1), et la quantité de un parmi eux est toujours égale à F(n).

Deux arbres de 21 891 appels, mais un seul se répète 🖖

Le calcul naïf de fib(20) et le tri fusion de 10 946 éléments font très exactement le même nombre d'appels : 21 891, répartis en 10 946 cas de base et 10 945 étapes de combinaison. Trois des six cartes affichent des résultats identiques, et les deux définitions créent deux branches à chaque nœud interne. Ce n'est donc pas la ramification qui les sépare. La carte des sous-problèmes distincts est formelle : 21 contre 21 891. Un tableau de mémoïsation réduit l'arbre de Fibonacci à 21 unités de travail, mais laisse celui du tri fusion à 21 891. Les moitiés du tri fusion sont des tranches différentes d'un seul et même tableau. Une réponse mise en cache pour « une suite de 5 473 éléments » correspondrait inévitablement aux 5 473 mauvais éléments.

Un seul 1 soustrait sépare φⁿ de 2ⁿ 🖖

Hanoï et Fibonacci font tous deux deux appels récursifs et atteignent presque la même profondeur, 20 contre 19. Pour n = 20, Hanoï effectue 2 097 151 appels. Fibonacci en fait 21 891, soit 95,8 fois moins. La seule différence dans le code se situe au niveau du deuxième argument récursif : Hanoï s'appelle deux fois sur n−1, Fibonacci sur n−1 puis sur n−2. Cette simple soustraction fait chuter la base de l'exponentielle de 2 à φ = 1,618. Et l'écart continue de se creuser. À n = 30, nous avons 2 147 483 647 appels contre 2 692 537, soit un facteur de 798.

Problèmes entièrement résolus

  1. Formule explicite du nombre d'appels quand fib(10) naïf effectue 177 appels 6 étapes

    L'outil indique que fib(10) naïf effectue 177 appels et que fib(11) en fait 287. Trouvez la forme explicite du nombre d'appels, prouvez-la, puis déterminez jusqu'où va l'algorithme de Fibonacci naïf en une seconde sur une machine capable de traiter 108 appels par seconde.

    1. Ajoutez 1 à chaque compte : 178 et 288. Ces deux nombres sont pairs, et leurs moitiés sont 89 et 144, qui correspondent à F11 et F12. La conjecture est donc C(n) = 2F(n+1) − 1, avec F(1) = F(2) = 1.

    2. L'arbre explique l'origine de ce doublement. Chaque nœud interne effectue exactement deux appels et chaque feuille n'en fait aucun. Un arbre avec L feuilles possède donc L − 1 nœuds internes, soit un total de 2L − 1 nœuds. L'outil signale 89 cas de base pour n = 10, et 2 × 89 − 1 = 177.

    3. Les cas de base d'abord. fib(0) et fib(1) retournent leur valeur sans faire d'appel récursif. Nous avons donc C(0) = C(1) = 1, et les deux expressions 2F(1) − 1 et 2F(2) − 1 sont égales à 1.

    4. Passez maintenant à la récurrence. Supposons la formule vraie pour n − 1 et n − 2. L'appel en n correspond à un nœud plus ses deux sous-arbres, et les deux nombres de Fibonacci sous-jacents fusionnent selon la définition même de la suite. Fixer n = 11 donne 2 × 144 − 1 = 287. C'est exactement ce qu'affiche l'outil.

    5. Laissez l'arbre de côté. La formule de Binet donne F(m) = (φm − ψm)/√5 avec φ = 1,6180 et |ψ| < 1. F(m) correspond donc à φm/√5 arrondi à l'entier le plus proche, et C(n) ≈ 2φn+1/√5. Une seconde à 108 appels par seconde permet de réaliser 108 appels. Cela fixe φn+1 à 1,1180 × 108.

    6. Passez aux logarithmes. ln(1,1180 × 108) = 18,5323 et ln φ = 0,4812, donc n + 1 = 38,51 et n = 37,5. Arrondissez à l'entier inférieur, car un appel fractionnaire ne se termine jamais.

    Réponse

    n = 37. L'outil confirme les deux valeurs voisines : 78 176 337 appels pour n = 37, et 126 491 971 pour n = 38. Au-delà, chaque augmentation de 1,44 de n double le travail, car ln 2 / ln φ = 1,44. Pendant ce temps, la carte des sous-problèmes distincts affiche 38 pour n = 37. Soixante-dix-huit millions d'appels pour répondre à trente-huit questions : voilà, en une seule phrase, tout l'argument de la programmation dynamique.

  2. Trois branches qui croissent moins vite que deux 6 étapes

    L'escalier à trois pas s'appelle trois fois à chaque nœud ; les tours de Hanoï s'appellent deux fois. Chargez Marches à 20, 3 branches, cherchez lequel des deux arbres croît le plus vite, et décidez ce que vaut le facteur de branchement comme repère.

    1. Lisez la récurrence sur la définition plutôt que sur l'image. Un appel pour le nœud lui-même, puis trois autres en n − 1, n − 2 et n − 3, et tout ce qui est à 2 ou en dessous renvoie sans récursion.

    2. Supposez que le nombre croît géométriquement, C(n) ≈ A xⁿ, et substituez. Divisez le tout par x à la puissance n − 3 : le 1 de tête reste en arrière comme bruit d'ordre inférieur, et il ne demeure qu'une cubique. Sa seule racine réelle vaut 1,8392868, la constante tribonacci.

    3. La même recette sur les deux autres définitions. Fibonacci s'appelle en n − 1 et n − 2, donc x² = x + 1 et la racine vaut φ = 1,6180340. Hanoï s'appelle deux fois en n − 1, donc x = 2 et il n'y a rien à résoudre.

    4. Vérifiez-le plutôt que d'y croire. Mettez n à 29, lisez la carte des appels effectués, mettez n à 30, relisez, divisez. Les marches donnent 1,83929, Fibonacci 1,61803, Hanoï exactement 2. Cinq décimales pour les trois.

    5. Ce que la racine ne vous donne pas, c'est le nombre. 1,8393²⁰ vaut 196 331 alors que la carte affiche 128 287 : il y a donc un facteur 0,65 devant. Les appels de Hanoï valent exactement 2ⁿ⁺¹ − 1, soit un facteur 2. Les cas de base fixent cette constante et la récurrence fixe la racine, et seule la racine décide qui l'emporte à la fin.

    6. Cette fin arrive vite ici. À n = 20, Hanoï fait 16,3 fois plus d'appels que les marches, et à n = 30 il en fait 37,8 fois plus. Le rapport double tous les 8,3 pas, puisque ln 2 divisé par ln(2 ÷ 1,8393) vaut 8,27.

    Réponse

    Hanoï croît plus vite, 2 contre 1,8393, et la troisième branche ne coûte aux marches rien qu'elles ne puissent payer. Le facteur de branchement est une borne supérieure du taux de croissance, et il n'est atteint que lorsque chaque enfant est plus petit d'un seul pas. Les marches dépensent deux de leurs trois appels sur des arguments diminués de 2 et de 3, et ces sous-arbres sont assez bon marché pour ne jamais totaliser la branche qu'elles semblaient avoir gagnée.

    La recette est ce qu'il faut garder, car elle fonctionne sur une définition que personne n'a résolue pour vous. Comptez les enfants, notez de combien chacun abaisse l'argument, écrivez x à la plus grande baisse comme somme des x aux baisses restantes, et prenez la plus grande racine réelle. Une mise en garde l'accompagne : c'est un fait sur l'arbre naïf, et l'arbre naïf n'est pas ce que quiconque écrirait. La carte des sous-problèmes distincts affiche 21 à n = 20 pour les marches et 21 pour Hanoï : une mémoïsation supprime donc l'exposant entièrement, et le taux de croissance cesse de compter.

Parcours

Compter le travail, pas les secondes

Mène à Recherche de chemin

Exemples de problèmes