Aller au contenu principal

La récursivité

Ce que ce chapitre apporte 5 points
  • Reconnaître un problème qui se décrit naturellement par lui-même.
  • Écrire une définition récursive en posant son cas de base puis son cas récursif.
  • Lire la pile des appels pendant qu'elle se remplit, atteint son fond, puis se vide.
  • Diagnostiquer une récursion qui ne s'arrête pas, et nommer la cause exacte.
  • Convertir un algorithme récursif en version itérative, dire ce que chacun coûte, et reconnaître les cas où la boucle ne suffit plus.

Le total d'un relevé, c'est la première mesure ajoutée au total du reste du relevé. Cette phrase n'est pas une devinette : c'est une définition complète, et elle se traduit en trois lignes de pseudo-code parce qu'une fonction a le droit de s'appeler elle-même. Ce chapitre montre ce que la machine fait pendant ce temps. Ses figures affichent la pile des appels, comme celles des chapitres sur les fonctions et la modularité, mais la pile y devient le sujet : les appels s'empilent, touchent le fond, puis remontent un par un en rendant leur résultat.

Le chapitre précédent s'est achevé sur une promesse, celle d'une fonction qui s'appelle elle-même et de la pile d'appels qui la rend possible. Cette promesse est tenue ici, et elle se tient à l'écran plutôt que sur le papier : chaque figure de ce chapitre affiche, sous l'algorithme, les cadres d'appel côte à côte, du programme principal à l'appel en cours, avec les variables que chacun possède en propre. La récursivité est l'un des rares sujets d'algorithmique où la description écrite reste opaque tant qu'on n'a pas vu le mécanisme fonctionner. Autant commencer par le voir.

Un problème qui se décrit par lui-même

Soit un relevé de cinq mesures, et la question la plus banale qui soit : combien font-elles au total ? La réponse itérative est connue depuis le chapitre sur les tableaux : un accumulateur et un parcours. Mais il existe une autre façon de répondre, et elle tient en une phrase :

Le total d'un relevé vide vaut 0. Le total d'un relevé non vide vaut sa première mesure, plus le total de tout ce qui suit.

Cette phrase a deux propriétés remarquables. D'abord elle est complète : elle couvre tous les cas, le relevé vide et les autres. Ensuite elle se sert d'elle-même, puisque « le total de tout ce qui suit » est un total de relevé, exactement la chose que la phrase est en train de définir. Un tel énoncé s'appelle une définition récursive, et il se recopie presque mot pour mot en pseudo-code.

Algorithme
pas 1 / 20
Fonction Total(t, i)
Si i > longueur(t) Alors
Retourner 0
FinSi
Retourner t[i] + Total(t, i + 1)
FinFonction
Début
releves ← [12, 7, 25, 4, 18]
Écrire "Total : ", Total(releves, 1)
Fin

programme principal

releves[12, 7, 25, 4, 18]
À manipuler
Avancer d'un pas à la fois avec « suivant ▶ », en regardant sous l'algorithme la rangée des encadrés plutôt que la sortie. Chaque encadré est un cadre d'appel : le programme principal à gauche, puis un encadré de plus à chaque appel, le plus récent au bout de la rangée, seul entouré de couleur, les autres estompés parce qu'ils attendent. Noter le i de chaque nouveau cadre : six cadres de Total apparaissent, avec 1, puis 2, 3, 4, 5, enfin 6, le dernier au pas 13. Au pas 14, ce sixième cadre a trouvé i > longueur(t) et la ligne surlignée est Retourner 0. Constater qu'à partir de là plus aucun appel n'est lancé : du pas 15 au pas 20, un encadré disparaît à chaque pas, toujours celui du bout, chacun ajoutant sa mesure à ce que le cadre suivant vient de lui rendre, et « Total : 66 » ne s'affiche qu'au pas 20, quand il ne reste que le programme principal.

Le paramètre i désigne l'endroit où commence « le reste ». Total(t, 1) demande le total du relevé entier, Total(t, 2) celui de tout ce qui suit la première mesure, et ainsi de suite. Quand i dépasse la dernière case, il ne reste rien : le total vaut 0, et la fonction répond sans rien appeler.

Le même déroulé tient dans un tableau de trace, une ligne par cadre. La troisième colonne se remplit de haut en bas, pendant la descente ; la dernière se remplit de bas en haut, pendant la remontée.

CadreiCe que le cadre met en attenteCe qu'il rend
Total(t, 1)112 + Total(t, 2)66
Total(t, 2)27 + Total(t, 3)54
Total(t, 3)325 + Total(t, 4)47
Total(t, 4)44 + Total(t, 5)22
Total(t, 5)518 + Total(t, 6)18
Total(t, 6)6rien : c'est le cas de base0

Le calcul ne se fait pas à la descente. À la descente, la fonction ne calcule rien : chaque cadre pose son addition en attente et relance un appel, et la troisième colonne se remplit sans qu'un seul total soit connu. Le premier nombre réel apparaît au fond, avec le 0 du cas de base. Tout le reste est produit à la remontée, dans l'ordre inverse de la descente : 0, puis 18, 22, 47, 54, enfin 66. C'est le point qui explique le plus grand nombre de malentendus sur la récursivité, et la figure le rend visible en vingt pas.

Vérification rapideon peut se reprendre

Dans Retourner t[i] + Total(t, i + 1), quand la première addition est-elle réellement effectuée ?

Ce que compte le compteur de pas

Chaque figure affiche au-dessus de l'algorithme un compteur, « pas 1 / 20 » pour celle-ci, et les chapitres qui suivent s'en servent pour comparer des algorithmes. Un pas est une ligne exécutée. Une affectation, un Écrire, un Lire ou un Retourner comptent un pas. Un test en compte un chaque fois qu'il est évalué : la ligne Si, chaque SinonSi examiné, et l'en-tête d'un TantQue ou d'un Pour à chaque tour, plus une fois à la sortie, pour le test qui arrête la boucle. Un Sinon ne compte que si sa branche est prise ; Début, Fin, l'en-tête d'une fonction et les lignes de fermeture comme FinSi ne comptent jamais. Une ligne qui appelle une fonction est franchie deux fois : au départ de l'appel, puis au retour, quand la valeur rendue a servi. Les vingt pas de Total se décomposent ainsi : la ligne qui range le relevé, six tests Si, un Retourner 0, et six lignes d'appel franchies deux fois chacune.

Ces nombres mesurent le travail du déroulement, pas des secondes. Ils servent à comparer deux algorithmes sur les mêmes données, ce qui est exactement ce qu'on demande à une mesure de coût. La notation qui exprime un coût indépendamment de la machine, les classes usuelles et la distinction entre cas moyen et pire cas sont traitées dans le chapitre consacré à la complexité. La page arrête enfin tout déroulement au-delà de 500 pas, et au-delà de trente appels imbriqués.

Les deux questions qui font une définition récursive

Définition

Un algorithme est récursif lorsqu'il s'appelle lui-même sur un cas plus petit du même problème. Une définition récursive comporte toujours deux parties : un cas de base, traité directement et sans aucun appel, et un cas récursif, qui se ramène à un cas strictement plus proche du cas de base.

Écrire un algorithme récursif revient à répondre à deux questions, et à rien d'autre.

Quel est le cas que l'on sait traiter sans rien appeler ? Pour le total, c'est le relevé vide, dont le total vaut 0. Pour une recherche, c'est la portion vide, où l'on ne trouve rien. Pour une nomenclature, c'est la pièce élémentaire, qui ne se décompose plus. Ce cas doit être traité en premier dans la fonction, avant tout appel, sans quoi il ne sera jamais atteint.

Comment se ramener à un cas plus petit ? Il ne suffit pas d'appeler la fonction avec des arguments différents : il faut qu'ils soient plus proches du cas de base, et que le rapprochement soit strict. i + 1 rapproche de longueur(t) + 1. n - 1 rapproche de 0. n DIV 2, le quotient entier de n par 2 (7 DIV 2 vaut 3), rapproche aussi de 0, et bien plus vite. Un appel qui ne rapproche de rien donne une récursion sans fin, et la suite du chapitre montre exactement ce que la machine en fait.

La forme générale est toujours la même :

Algorithme
Fonction Traiter(donnee)
Si donnee est le cas de base Alors
Retourner la réponse immédiate
FinSi
Retourner combiner(donnee, Traiter(donnee réduite))
FinFonction
Faire confiance à l'appel
La difficulté n'est pas technique, elle est mentale : en écrivant le cas récursif, il faut admettre que Traiter(donnee réduite) rend déjà la bonne réponse, sans chercher à imaginer comment. C'est exactement ce qu'on fait avec n'importe quelle fonction déjà écrite par quelqu'un d'autre. Le seul contrôle qui reste à faire est celui des deux questions ci-dessus : le cas de base existe, et l'appel réduit vraiment le problème.

La remontée fait apparaître ce que la descente n'a pas fait

Un second exemple rend la chose encore plus nette, parce qu'il n'y a plus de calcul du tout pour brouiller la lecture. Il s'agit d'afficher une liste d'étapes dans l'ordre inverse, sans la modifier et sans compter à rebours.

Algorithme
pas 1 / 21
Fonction Renverser(t, i)
Si i > longueur(t) Alors
Retourner
FinSi
Renverser(t, i + 1)
Écrire t[i]
FinFonction
Début
etapes ← ["poser", "visser", "serrer", "contrôler"]
Renverser(etapes, 1)
Fin

programme principal

etapes["poser", "visser", "serrer", "contrôler"]

Retourner seul, sans valeur, quitte la fonction sans rien rendre : Renverser affiche, elle ne calcule rien, et c'est pourquoi son appel s'écrit seul sur sa ligne, sans flèche pour ranger un résultat.

L'appel récursif est placé avant l'affichage, et c'est tout l'algorithme. Pendant la descente, cinq cadres de Renverser s'empilent sans produire une seule ligne de sortie : quatre d'entre eux ont atteint la ligne Renverser(t, i + 1) et attendent, le cinquième est le cas de base, qui ne renvoie rien du tout. Puis la remontée commence, et chaque cadre reprend là où il s'était arrêté, sur son Écrire t[i]. La sortie n'apparaît qu'à la remontée, une ligne à chaque retour, et à l'envers : « contrôler », « serrer », « visser », « poser ».

Aucune variable ne compte à rebours, aucune boucle ne descend. L'ordre inverse est produit par la seule mécanique de la pile, qui est un rangement où le dernier arrivé est le premier servi. Il suffit de déplacer le Écrire d'une ligne, avant l'appel au lieu d'après, pour rétablir l'ordre normal :

Algorithme
pas 1 / 21
Fonction Lister(t, i)
Si i > longueur(t) Alors
Retourner
FinSi
Écrire t[i]
Lister(t, i + 1)
FinFonction
Début
etapes ← ["poser", "visser", "serrer", "contrôler"]
Lister(etapes, 1)
Fin

programme principal

etapes["poser", "visser", "serrer", "contrôler"]

Chaque cadre affiche maintenant son étape avant d'appeler le suivant. La sortie se remplit pendant la descente, dans l'ordre du montage, de « poser » à « contrôler », et la remontée ne produit plus rien. Les deux figures demandent le même nombre de pas, 21, et leur pile monte aussi haut : seule la place de l'affichage par rapport à l'appel a changé, et elle décide de tout.

C'est la même pile que celle du processeur

Le mot « pile » employé ici n'est pas une image pédagogique. Le parcours d'architecture montre la même pile côté machine : la pile y réserve un cadre à chaque fragment de code qui en a besoin, et l'appel de fonction montre qu'une fonction qui en appelle une autre, ce que fait toute fonction récursive, range dans son cadre l'adresse à laquelle elle devra revenir et les valeurs qu'elle veut retrouver après l'appel.

Définition

Un cadre d'appel est l'espace réservé à une exécution d'une fonction : ses paramètres, ses variables locales, et le point de retour. Chaque appel crée un cadre neuf ; chaque retour le détruit. Les cadres empilés au même instant forment la pile des appels.

C'est ce qui explique pourquoi la récursivité fonctionne sans aucune précaution particulière : les cinq i des figures précédentes ne se gênent pas, parce que chacun vit dans un cadre distinct. La portée, vue au chapitre sur les fonctions, n'est pas une règle qui s'ajoute à la récursivité, c'est ce qui la rend possible. Une fonction récursive n'est rien d'autre qu'une fonction ordinaire dont plusieurs exécutions se trouvent en cours en même temps.

Cette même pile explique aussi la limite. Chaque cadre occupe de la place, et cette place n'est pas infinie.

Algorithme
pas 1 / 64
Fonction Profondeur(n)
Si n = 0 Alors
Retourner 0
FinSi
Retourner 1 + Profondeur(n - 1)
FinFonction
Début
Écrire "Profondeur atteinte : ", Profondeur(20)
Fin

programme principal

aucune variable

Cette fonction ne sert à rien d'autre qu'à creuser : elle descend de 20 à 0 et recompte les étages en remontant. Le compteur de pas affiche 64, et la pile atteint 22 cadres au plus profond, le programme principal compris. La figure la montre entièrement, ce qui donne une idée assez juste de ce qu'un algorithme récursif consomme en mémoire : une hauteur de pile proportionnelle au nombre d'appels imbriqués.

Quand la récursion ne s'arrête pas

Le défaut le plus courant est aussi le plus facile à produire : oublier le cas de base. L'algorithme est alors parfaitement lisible, il se traduirait sans une erreur dans n'importe quel langage, et il ne se termine jamais.

Algorithme
pas 1 / 61
Fonction Compte(n)
Écrire n
Retourner Compte(n - 1)
FinFonction
Début
Compte(5)
Fin

programme principal

aucune variable

Rien dans cette fonction ne dit quand s'arrêter. Compte(5) appelle Compte(4), qui appelle Compte(3), et la descente continue dans les négatifs sans que quoi que ce soit s'y oppose. Le moteur de la page interrompt le déroulement à trente appels imbriqués et l'annonce, ce qui permet de voir la trace s'arrêter plutôt que de lire qu'elle le ferait : la sortie s'est rendue jusqu'à −24, et la pile est pleine.

Ce que fait une vraie machine

Sur une machine réelle, il n'y a pas d'avertissement à trente appels. La pile grandit jusqu'à épuiser la zone qui lui est réservée, et le programme s'arrête brutalement sur un débordement de pile ; Python, lui, s'interrompt de lui-même vers mille appels imbriqués. Le message, quand il y en a un, désigne la ligne de l'appel récursif, répétée des centaines de fois : or cette ligne n'a rien de faux. La vraie faute est une absence, le cas de base manquant ou inatteignable, et une absence ne se montre pas du doigt. C'est une panne qui se diagnostique par relecture, et la relecture porte toujours sur les deux mêmes questions.

Le second défaut est plus sournois, parce que le cas de base est bien là. Il est simplement inatteignable.

Algorithme
pas 1 / 91
Fonction Descendre(n)
Si n = 0 Alors
Écrire "arrivé"
Retourner 0
FinSi
Écrire "descente ", n
Retourner Descendre(n - 2)
FinFonction
Début
Descendre(7)
Fin

programme principal

aucune variable

Le test n = 0 existe, il est écrit en premier, et il ne servira jamais : en partant de 7 et en retirant 2 à chaque fois, les valeurs prises sont 7, 5, 3, 1, puis −1, et la suite passe à côté de zéro sans jamais l'atteindre. La trace le montre sans ambiguïté, elle descend jusqu'à −51 avant que la pile ne sature.

Tester une borne plutôt qu'une égalité
Écrire Si n = 0 suppose que la suite des valeurs passe exactement par 0. Écrire Si n <= 0 ne le suppose pas, et attrape aussi bien 0 que −1. La règle est générale : un cas de base doit être formulé comme une borne, pas comme une valeur précise, sauf à avoir prouvé que cette valeur sera atteinte. La même prudence s'applique à i > longueur(t), qui attrape tout dépassement plutôt que le seul dépassement d'une case.
Vérification rapideon peut se reprendre

1.Que se passe-t-il si une fonction récursive n'a pas de cas de base ?

2.Si n = 0 comme cas de base, avec un appel Descendre(n - 2) depuis 7 : que se passe-t-il ?

Récursif ou itératif : ce que chacun coûte

Tout algorithme récursif peut s'écrire avec une boucle, et réciproquement. Le choix n'est donc jamais une question de possibilité, seulement de lisibilité et de coût. Le total du relevé s'écrit aussi comme au chapitre sur les tableaux.

Algorithme
pas 1 / 14
Début
releves ← [12, 7, 25, 4, 18]
somme ← 0
Pour i de 1 à longueur(releves)
somme ← somme + releves[i]
FinPour
Écrire "Total : ", somme
Fin

programme principal

releves[12, 7, 25, 4, 18]

Les deux versions donnent 66, la récursive en 20 pas et l'itérative en 14 : l'écart existe, mais il reste modeste, et le temps de calcul n'est pas ce qui les sépare vraiment. Ce qui les sépare se lit dans la rangée des cadres, sous l'algorithme : la version itérative tient dans un seul cadre du début à la fin, la version récursive en occupe sept au plus profond. Sur cinq mesures la différence est anecdotique ; sur cinquante mille, la version récursive épuise la pile et la version itérative ne bronche pas.

Le vrai arbitrage. La récursivité coûte de la mémoire de pile, proportionnelle à la profondeur des appels. Elle se paie en échange d'un algorithme qui ressemble à l'énoncé du problème. Le calcul est gagnant quand la description récursive est nettement plus simple que la version itérative, et perdant quand la version itérative tient en trois lignes de boucle. Un parcours linéaire de tableau appartient à la seconde catégorie : la récursivité y est un exercice, pas une solution.

Il existe toutefois une famille de problèmes où la version itérative est un cauchemar et la version récursive une évidence : ceux où le problème se coupe en plusieurs sous-problèmes indépendants, et non en un seul. Un parcours d'arborescence, un découpage en deux moitiés, une exploration de possibilités : dans ces cas, écrire la boucle équivalente oblige à gérer soi-même une pile, c'est-à-dire à refaire à la main ce que la machine fait déjà. La section suivante en donne un exemple, et le tri fusion, deux chapitres plus loin, en sera l'exemple majeur.

Vérification rapideon peut se reprendre

1.Pourquoi les différents i d'une fonction récursive ne se mélangent-ils pas ?

2.Sur le même relevé de cinq mesures, la version récursive demande 20 pas et la version itérative 14. Où passe l'écart ?

Là où la boucle ne suffit plus

Une nomenclature décrit un produit par ses composants : un vélo se compose d'un cadre et de deux roues ; une roue, d'une jante, de trente-deux rayons et d'un moyeu ; un moyeu, d'un axe et de deux roulements. Combien de pièces élémentaires faut-il approvisionner pour un vélo ? La question se décrit par elle-même, comme le total d'un relevé. Une pièce sans composant compte pour une ; un ensemble compte, pour chacun de ses composants, la quantité multipliée par le nombre de pièces de ce composant.

Pour la ranger, chaque pièce reçoit un numéro, et deux tableaux se lisent en parallèle. La case p de composants contient elle-même un tableau, la liste des composants directs de la pièce p ; la case p de quantites contient leurs nombres, dans le même ordre. Un tableau vide, [], signale une pièce élémentaire : c'est le cas de base.

Algorithme
pas 1 / 58
Fonction Pieces(p, composants, quantites)
c ← composants[p]
Si longueur(c) = 0 Alors
Retourner 1
FinSi
q ← quantites[p]
total ← 0
Pour k de 1 à longueur(c)
total ← total + q[k] * Pieces(c[k], composants, quantites)
FinPour
Retourner total
FinFonction
Début
// pièces : 1 vélo, 2 cadre, 3 roue, 4 jante, 5 rayon, 6 moyeu, 7 axe, 8 roulement
composants ← [[2, 3], [], [4, 5, 6], [], [], [7, 8], [], []]
quantites ← [[1, 2], [], [1, 32, 1], [], [], [1, 2], [], []]
Écrire "Pièces élémentaires d'un vélo : ", Pieces(1, composants, quantites)
Fin

programme principal

composants[[2, 3], [], [4, 5, 6], [], [], [7, 8], [], []]

Le résultat est 73 : un cadre, et deux roues de trente-six pièces chacune, puisqu'un moyeu en compte trois. Chaque cadre reçoit sa propre copie des deux tableaux, que la figure affiche avec ses variables ; seuls p, c, q, total et k changent d'un cadre à l'autre. La différence avec le total d'un relevé est dans la boucle : un même appel en lance plusieurs, un par composant, et chacun peut en lancer d'autres à son tour. La rangée des cadres s'allonge et se raccourcit au fil des composants, et atteint cinq cadres au plus profond : le programme principal, le vélo, la roue, le moyeu et l'une de ses pièces.

Aucune boucle seule ne ferait ce travail. Des boucles imbriquées supposeraient de savoir combien de niveaux compte la nomenclature, or un moyeu peut contenir un sous-ensemble, qui en contient un autre, sans limite fixée d'avance. Et une boucle qui descendrait d'un niveau à la fois ne saurait pas où reprendre après avoir fini une roue : il lui faudrait tenir elle-même la liste des ensembles commencés et des composants qui leur restent, c'est-à-dire refaire à la main la pile des appels que la figure affiche. La récursivité s'impose ici, parce que le problème se coupe en plusieurs sous-problèmes du même genre et que leur profondeur n'est pas connue d'avance.

Mettre en pratique

Ce que coûte un algorithme récursif se compte : les appels, la hauteur de la pile, les pas du déroulement. Tout se déduit des figures du chapitre, sans en dérouler de nouvelle.

Compter les appels, mesurer la pile

  • 1.

    Total(releves, 1) sur un relevé de 12 mesures : combien d'appels de Total en tout ?

  • 2.

    Pour ce même relevé, combien de cadres la pile compte-t-elle au plus profond, programme principal compris ?

  • 3.

    Combien de pas le compteur affiche-t-il pour le même algorithme sur un relevé de 8 mesures ?

  • 4.

    Dans la nomenclature, une roue passe à 36 rayons, et rien d'autre ne change. Combien de pièces élémentaires faut-il pour un vélo ?

  • 5.

    Combien de cadres la pile compte-t-elle au plus profond pour SommeChiffres(907315), programme principal compris ?

Écrire une fonction récursive SommeChiffres(n) qui rend la somme des chiffres d'un entier positif, et la vérifier sur 48127.
Algorithme
pas 1 / 16
Fonction SommeChiffres(n)
Si n < 10 Alors
Retourner n
FinSi
Retourner (n MOD 10) + SommeChiffres(n DIV 10)
FinFonction
Début
Écrire "Somme des chiffres de 48127 : ", SommeChiffres(48127)
Fin

programme principal

aucune variable

Le cas de base est un nombre à un seul chiffre, qui est sa propre somme. Le cas récursif détache le dernier chiffre avec n MOD 10, retire ce chiffre avec n DIV 10, et se ramène à un nombre qui a un chiffre de moins. La réduction est stricte, donc la descente se termine. Le résultat affiché est 22, et la pile atteint six cadres pour un nombre de cinq chiffres.

La version itérative existe aussi, et elle tient dans un seul cadre :

Algorithme
pas 1 / 19
Début
n ← 48127
somme ← 0
TantQue n > 0
somme ← somme + (n MOD 10)
n ← n DIV 10
FinTantQue
Écrire "Somme des chiffres de 48127 : ", somme
Fin

programme principal

n48127

Ici la version itérative est aussi lisible que la récursive, et moins coûteuse en mémoire, un seul cadre contre six, même si elle demande trois pas de plus, 19 contre 16 : c'est un cas où la récursivité ne se justifie pas, sinon comme exercice.

Écrire le calcul du plus grand diviseur commun de deux entiers par la méthode d'Euclide, qui s'énonce déjà de façon récursive : le pgcd de a et b vaut a quand b est nul, et vaut sinon le pgcd de b et du reste de a par b.
Algorithme
pas 1 / 13
Fonction Pgcd(a, b)
Si b = 0 Alors
Retourner a
FinSi
Retourner Pgcd(b, a MOD b)
FinFonction
Début
Écrire "Pgcd(1071, 462) = ", Pgcd(1071, 462)
Fin

programme principal

aucune variable

Le cas de base s'écrit b = 0, une égalité, et c'est permis ici parce que la valeur 0 sera forcément atteinte. Un reste n'est jamais négatif, et il est strictement plus petit que le diviseur : le second argument reste donc positif ou nul, et il diminue d'au moins 1 à chaque appel. Une suite d'entiers qui diminue ainsi sans pouvoir passer sous zéro ne peut pas durer indéfiniment, et elle ne peut s'arrêter qu'en tombant exactement sur 0. Décroître ne suffisait pas à Descendre, qui passait sous zéro : c'est de ne jamais pouvoir descendre sous zéro qui garantit l'arrêt. Le résultat est 21, obtenu en 13 pas et cinq cadres, là où une recherche du plus grand diviseur commun par essais successifs en aurait demandé plusieurs centaines.

Écrire une fonction récursive Puissance(x, n) qui calcule x à la puissance n, pour un n entier positif ou nul.
Algorithme
pas 1 / 34
Fonction Puissance(x, n)
Si n = 0 Alors
Retourner 1
FinSi
Retourner x * Puissance(x, n - 1)
FinFonction
Début
Écrire "2 puissance 10 = ", Puissance(2, 10)
Fin

programme principal

aucune variable

Le cas de base est n = 0, qui rend 1 : c'est la convention mathématique, et c'est aussi ce qui fait fonctionner la remontée, puisque multiplier par 1 ne change rien. Le test n = 0 est sûr pour la même raison que dans l'exercice précédent : l'énoncé garantit un exposant entier positif ou nul, et n - 1 ne peut pas enjamber 0. Le déroulement prend 34 pas et empile 12 cadres pour un exposant de 10 : un cadre par unité d'exposant, plus celui du cas de base et celui du programme principal. Le chapitre suivant obtient le même résultat avec six cadres, en coupant l'exposant en deux au lieu de le décrémenter : c'est l'objet de son troisième exercice.

Chapitre révisé en octobre 2026 · Une erreur dans ce chapitre ?

Dans la bibliothèque d'exercices