Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 16

Fonctions récursives : Factorielle et Fibonacci

Recommended resources

Bonjour,

Bienvenue dans ce nouveau module dédié aux fondements de l'analyse d'algorithmes. La leçon précédente sur l'espérance et la variance a clôturé notre étude des outils mathématiques fondamentaux. Nous allons désormais appliquer cette rigueur à notre discipline principale : l'informatique. Vous allez voir que les concepts mathématiques étudiés, notamment les suites et les récurrences, sont le langage naturel pour décrire et analyser les algorithmes.

Cette première leçon du module porte sur un concept central en algorithmique et en programmation : la récursivité. L'objectif est double : vous devez être capable d'écrire une fonction récursive simple (comme la factorielle ou la suite de Fibonacci) et de tracer son arbre d'appels pour en comprendre l'exécution. La maîtrise de la récursivité est une compétence absolument essentielle, non seulement pour la conception d'algorithmes élégants (diviser pour régner, parcours de structures arborescentes), mais aussi pour leur analyse formelle, un attendu fondamental à l'Agrégation.

1. La pensée récursive : cas de base et cas récursif

Un algorithme est dit récursif s'il résout un problème en se ramenant à la résolution d'un ou plusieurs sous-problèmes de même nature, mais de taille plus petite. Pour qu'une définition récursive soit valide, elle doit impérativement comporter deux éléments :

  1. Un ou plusieurs cas de base : Ce sont des cas "triviaux" pour lesquels la solution est connue immédiatement, sans avoir besoin de faire un nouvel appel récursif. Le cas de base garantit la terminaison de l'algorithme.
  2. Un cas récursif (ou général) : Il exprime la solution du problème en fonction de la solution d'un ou plusieurs sous-problèmes. L'étape récursive doit systématiquement "réduire la taille" du problème pour converger vers un cas de base.

L'exemple canonique est celui de la fonction factorielle, notée . Sa définition mathématique est intrinsèquement récursive :

Ici, est le cas de base. L'expression est le cas récursif.

Pour approfondir cette définition et voir comment elle se traduit en algorithme, je vous invite à regarder la vidéo suivante. Elle compare l'approche itérative, que vous connaissez certainement, à l'approche récursive.

Algorithme 21: Récursivité - Calcul récursif de factorielle et des termes de la suite de Fibonacci

Cette vidéo de Hassan EL BAHI introduit clairement la notion de récursivité en s'appuyant sur l'exemple de la factorielle. Elle distingue bien le cas de base du cas récursif, qui sont les deux piliers de toute fonction récursive.

Regardez la portion de 04:10 à 08:46. Concentrez-vous sur la manière dont la définition mathématique de la factorielle est transformée en une structure algorithmique avec une condition si... alors... sinon... qui sépare le cas de base du cas récursif.


2. Comment s'exécute une fonction récursive ? La pile d'appels

Comprendre la définition est une chose, mais visualiser l'exécution en est une autre. Lorsqu'une fonction f(n) appelle récursivement f(n-1), l'exécution de f(n) est suspendue et mise en attente. Un nouvel environnement est créé pour l'appel f(n-1). Ce mécanisme est géré par une structure de données fondamentale du système d'exécution : la pile d'appels (ou call stack).

Chaque appel de fonction "empile" un nouveau contexte (ses paramètres, ses variables locales, l'endroit où reprendre l'exécution). Lorsqu'une fonction se termine, son contexte est "dépilé" et la valeur de retour est transmise à la fonction appelante, qui peut alors reprendre son exécution.

La vidéo suivante illustre parfaitement ce mécanisme pour la fonction factorielle.

Algorithme 21: Récursivité - Calcul récursif de factorielle et des termes de la suite de Fibonacci

Nous continuons avec la même vidéo, qui explique maintenant la gestion de la pile d'appels lors du calcul de factorielle(5).

Regardez la section de 08:46 à 11:22. Observez comment la mémoire est allouée pour chaque appel successif, de factorielle(5) jusqu'à factorielle(1), puis comment les résultats sont calculés lors de la phase de "dépilement".

Pour visualiser cette séquence d'appels, on utilise un arbre d'appels. Pour la factorielle, cet "arbre" est en fait une simple branche linéaire.

Exécution d'une fonction factorielle récursive
Cette image illustre le calcul de `factorielle(3)`. À gauche, la cascade d'appels jusqu'au cas de base `factorielle(0)`. À droite, la remontée des résultats : `factorielle(0)` retourne 1, puis `factorielle(1)` calcule `1*1`, et ainsi de suite.

En Python, la fonction s'écrirait ainsi :

def factorial(n: int) -> int:



    # Cas de base
    if n == 0:
        return 1



    # Cas récursif
    else:
        return n * factorial(n - 1)




# Exemple d'appel
print(factorial(4)) # Affiche 24

3. Une récursivité arborescente : la suite de Fibonacci

La récursivité n'est pas toujours linéaire. Considérons la suite de Fibonacci, définie par :

Ici, nous avons deux cas de base ( et ) et un cas récursif qui fait deux appels à lui-même. L'arbre d'appels ne sera plus une ligne, mais bien un arbre binaire.

La vidéo suivante détaille la définition et le tracé de l'arbre d'appels pour Fibonacci.

Algorithme 21: Récursivité - Calcul récursif de factorielle et des termes de la suite de Fibonacci

La dernière partie de la vidéo de Hassan EL BAHI aborde la suite de Fibonacci. Elle est parfaite pour comprendre un cas de récursivité non linéaire.

Regardez les deux dernières sections : De 11:49 à 14:37 : pour la définition récursive de la suite. De 14:37 à 17:53 : pour une illustration du déroulement des appels pour fibonacci(5).

Pour une visualisation encore plus claire et une explication très rigoureuse du lien entre l'arbre d'appels et la pile d'appels, la vidéo ci-dessous (en anglais) est une référence.

Fibonacci Sequence - Anatomy of recursion and space complexity analysis

Cette vidéo de 'mycodeschool' est une excellente ressource complémentaire. Elle décompose méticuleusement comment la pile d'appels grandit et rétrécit en synchronisation avec le parcours de l'arbre d'appels pour Fib(5).

Regardez de 00:56 à 07:13. Ne vous souciez pas de l'audio si l'anglais est un obstacle ; concentrez-vous sur l'animation à gauche (la pile d'appels) et son lien avec le dessin de l'arbre récursif à droite. C'est une visualisation très éclairante.

En Python, l'implémentation est une traduction directe de la définition mathématique :

def fibonacci(n: int) -> int:



    # Cas de base
    if n <= 1:
        return n



    # Cas récursif
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)




# Exemple d'appel
print(fibonacci(5)) # Affiche 5

4. Exercices : à vous de tracer

La meilleure manière de consolider votre compréhension est de tracer vous-même les arbres d'appels. Prenez une feuille de papier et un crayon.

Exercice 1 : factorial(4)

  1. Dessinez le premier appel : un nœud étiqueté factorial(4).
  2. Cet appel génère un appel à factorial(3). Dessinez un nœud pour factorial(3) sous le premier et reliez-les.
  3. Continuez jusqu'à atteindre le cas de base factorial(0).
  4. Maintenant, "remontez" l'arbre. Inscrivez la valeur de retour à côté de chaque nœud. factorial(0) retourne 1. factorial(1) reçoit 1 et calcule 1 * 1 = 1, qu'il retourne. Continuez jusqu'à la racine.

Exercice 2 : fibonacci(4)
Procédez de la même manière pour fibonacci(4). Vous verrez l'arbre se diviser en deux branches à chaque étape. Notez les valeurs de retour et calculez les sommes lors de la remontée.

  • Que remarquez-vous concernant l'appel à fibonacci(2) ? Combien de fois est-il calculé ? Cette observation sera cruciale lorsque nous aborderons la programmation dynamique.
Structure des appels et remontée des résultats - Fibonacci de 4
Cette image résume l'exécution de `fibonacci(4)`. La partie gauche montre la cascade d'appels, formant un arbre. La partie droite montre la remontée des calculs, où les résultats des sous-problèmes sont combinés.

Pour aller plus loin et voir à quoi ressemblent ces arbres dans un contexte plus formel, vous pouvez consulter le document de cours suivant.

[PDF] Functional Algorithmic and Programming - Lecture 4: Recursion

Ce polycopié de cours de l'INP-ENSEEIHT présente de manière très propre les arbres d'appels pour la factorielle et Fibonacci. Cela vous donnera un aperçu de la rigueur de présentation attendue.

Consultez la diapositive 6, intitulée 'Calling a recursive function'. Observez les deux schémas pour fact(3) et fib(4). Notez la distinction entre la flèche descendante (appel) et la flèche montante (retour de valeur).


Conclusion

Cette leçon vous a introduit au paradigme de la récursivité, une pierre angulaire de l'informatique théorique et pratique.

Points clés à retenir :

  • Une fonction récursive est définie par un cas de base (condition d'arrêt) et un cas récursif (appel à soi-même sur un problème plus petit).
  • L'exécution est gérée par la pile d'appels : chaque appel est empilé, puis dépilé lorsque son résultat est calculé.
  • L'arbre d'appels est une représentation visuelle de la séquence d'appels. Sa forme (linéaire, arborescente) dépend du nombre d'appels récursifs dans le corps de la fonction.
  • Savoir tracer cet arbre est la première étape pour comprendre la complexité d'un algorithme récursif. Le nombre total de nœuds dans l'arbre est directement lié au volume de travail effectué.

Dans notre prochaine leçon, nous allons justement formaliser cette notion de "volume de travail". Maintenant que nous savons visualiser l'exécution d'un algorithme, nous allons apprendre le langage pour en mesurer l'efficacité : les notations asymptotiques O, Ω, et Θ. Cela nous permettra de comparer les algorithmes de manière rigoureuse.