Correction et terminaison (Bac 🎯)⚓︎
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.
Sources et crédits pour ce cours
Pour préparer ce cours, j'ai utilisé :
- le cours d'Olivier Lécluse
- le manuel NSI chez Ellipses de Balabonski, Conchon, Filliâtre, Nguyen
- le manuel NSI chez Hachette sous la direction de Michel Beaudouin Lafon
- la ressource Eduscol sur la récursivité
- le cour e-nsi sur la récursivité
- le cours de mon collègue Pierre Duclosson
🔖 Synthèse de ce qu'il faut retenir pour le bac
Étude de cas⚓︎
Exercice 3
🎯 Analyser le fonctionnement d'un programme récursif
-
On veut compléter la fonction récursive
puissance_rec
dans l'IDE ci-dessous pour quepuissance_rec(x, n)
renvoie la valeur \(x^{n}\) sans utiliser l'opérateur d'exponentiation**
de Python.a. Déterminer un cas de base.
b. Comment peut-on déterminer
puissance_rec(x, n)
à partir depuissance_rec(x, n - 1)
(réduction) ?c. Compléter le code.
-
On va démontrer la correction de la fonction
puissance_rec
, c'est-à-dire que la propriété \(P(n): puissance\_rec(x, n) = x^{n}\) est vraie pour tout entier naturel \(n\) positif.Méthode 2 : preuve de correction par récurrence
La correction d'une fonction récursive se démontre à l'aide d'un raisonnement par récurrence comme en Mathématiques :
- Initialisation : on démontre que la propriété est vraie pour le ou les cas de base (sans en oublier), ici pour \(n=0\).
- Hérédité : on démontre que pour tout entier naturel \(n\), si on suppose que \(P(n)\) est vraie alors \(P(n+1)\) est encore vraie.
Appliquez le raisonnement par récurrence pour démontrer que \(P(n)\) est vraie pour tout entier naturel \(n\) et donc que la fonction
puissance_rec
est correcte. -
On a vu dans l'exercice 1 que comme pour un algorithme itératif avec une boucle non bornée, il est possible qu'un algorithme récursif ne se termine pas.
Déplier et lire le paragraphe suivant puis justifier que
puissance_rec
se termine.Récursivité et terminaison
Une fonction récursive ne se termine pas si les étapes de réduction ne font pas converger les appels récursifs vers un cas de base.
Dans l'exercice 1, on a vu que celà peut se produire :
- Si la fonction ne propose pas de cas de base :
🐍 Script Pythondef factorielle(n): """Fausse""" # réduction mais pas de cas de base return n * factorielle(n-1)
- Si la fonction implémentée est appelée sur une valeur en dehors du domaine de définition de l'algorithme récursif :
🐍 Script Pythondef factorielle(n): """Renvoie n! = 1 *2 * ... (n-1) * n pour tout entier n >= 0""" # cas de base if n == 0: return 1 # réduction return n * factorielle(n-1) # Appel sur un nombre négatif => descente infinie factorielle(-1)
On peut prévenir ces problèmes en vérifiant des préconditions (programmation défensive).
🐍 Script Pythondef factorielle(n): """Renvoie n! = 1 *2 * ... (n-1) * n pour tout entier n >= 0""" assert isinstance(n, int) and n >= 0 # cas de base if n == 0: return 1 # réduction return n * factorielle(n-1) # Appel sur un nombre négatif => descente infinie factorielle(-1)
- Si dans la fonction implémentée, la réduction ne fait pas converger les appels récursifs vers un cas de base. Par exemple dans le cas ci-dessous l'appel
factorielle(1)
ne se termine pas carfactorielle(1)
appellefactorielle(-1)
qui appellefactorielle(-3)
etc ... Donc le cas de base 0 a été sauté et ne sera jamais atteint.
🐍 Script Pythondef factorielle(n): """Fausse""" # cas de base if n == 0: return 1 # réduction fausse return n * factorielle(n - 2)
💡 Comme pour une boucle, on peut démontrer que fonction récursive se termine, à l'aide d'un variant, une quantité entière positive dont on démontre qu'elle décroît lors de chaque appel récursif jusqu'à ce que le cas de base soit atteint.
Au tableau ...
# Tests
(insensible à la casse)(Ctrl+I)