Récursivité☘
Un objet récursif est un objet qui, dans sa définition, se sert de lui-même.
Vocabulaire
Le mot récursif provient du latin recurrere qui signifie littéralement « revenir en arrière ».
Fonction récursive☘
Une fonction sera dite récursive lorsqu'elle s’appelle elle-même.
L'utilité d'une telle fonction sera souvent de se ramener à un problème similaire mais de taille plus petite :
- la fonction va s'appeler elle-même avec un paramètre « plus petit » ;
- cet appel va en générer un autre, puis un autre, etc... ;
- la taille du problème va ainsi diminuer.
Un risque est que ces appels successifs soient sans fin. Il est donc nécessaire d'identifier un problème élémentaire que l'on sait résoudre et indiquer que ce problème élémentaire est la condition d'arrêt des appels récursifs.
Méthode
Pour définir une fonction récursive, il faut :
- qu’elle s’appelle elle-même ;
- qu’elle possède une condition d’arrêt.
Exemple☘
On considère la fonction spirale()
définie ci-dessous, dont le paramètre est n
(entier).
On admet que les vérifications sur le type de n
ont été réalisées avant l'appel à la fonction spirale()
.
1 2 3 4 5 6 7 8 9 10 11 |
|
- La condition d'arrêt est présente.
Seuls les paramètres entiers positifs non nuls permettent d'exécuter les instructions de la fonction. - Cette fonction s'appelle elle-même en ligne 11.
- Le paramètre d'appel diminue.
Toujours en ligne 11, ce paramètre est à présentn-1
. - Cette condition d'arrêt se réalisera forcément.
Seuls les entiersn
strictement positifs permettent d'exécuter les instructions de la fonction. Dans ce cas, à chaque appel récursif, la valeur du paramètre va diminuer de 1 et deviendra forcément strictement inférieur à 1 à un moment.
Terminaison d'une fonction récursive☘
Problématique
Est-on certain que, pour n'importe quelle entrée, la fonction récursive se terminera ?
Cette question se ramènera souvent à prouver que les appels successifs ne soient pas sans fin.
Nous allons, comme en classe de première, utiliser la notion de variant de boucle pour justifier qu'une fonction récursive se termine.
Rappel - variant de boucle
En classe de Première et Terminale, on peut par exemple utiliser la définition suivante :
On appelle variant d'une boucle toute quantité v telle que :
- v \in \mathbb{N}
- v décroit strictement à chaque passage dans la boucle.
Exemple☘
On considère les fonctions définies ci-dessous, dont les paramètres sont des entiers.
On admet que les vérifications
sur le type de ces paramètres ont été réalisés avant l'appel à la fonction carres_emboites()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
- Copiez/collez tout ce code puis exécutez l'appel
carres_emboites(100)
. - Justifiez que la fonction
carres_emboites()
est récursive. - Justifiez que cette fonction ne termine pas nécessairement
(on pourra donner un exemple d'appel à
carres_emboites()
qui conduit à un comportement non souhaité). - Améliorez la condition d'arrêt pour qu'elle prenne en compte tous les cas possibles.
- Justifiez qu'alors la fonction
carres_emboites()
termine en utilisant la technique du variant de boucle.
Des réponses
-
On obtient :
-
La fonction
carres_emboites()
:- possède un test d'arrêt ;
- s'appelle elle-même ;
- avec un paramètre entier dont la valeur diminue strictement.
-
L'appel
carres_emboites(103)
par exemple...
Testez-le si vous n'aviez pas trouvé d'exemple de comportement non souhaité puis essayez de justifier ce comportement.Attention
Avoir une variable dont les valeurs décroissent strictement ne suffit pas à définir un variant et à justifier que les appels récursifs cesseront.
En effet, si la condition d'arrêt des appels récursifs n'est jamais mise en défaut par les valeurs de cette variable, la pile d'appels (cf. cours suivant) se remplira indéfiniment. -
Par exemple :
1 2 3 4 5
def carres_emboites(n): if n >= 5: carre(n) deplacer(5) carres_emboites(n-5)
-
Justifions que
n
est un variant de boucle :n
est un entier positif dans l'appel récursif decarres_emboites()
car on n'entre dans cet appel que sin >= 5
.n
diminue strictement à chaque appel récursif puisque l'appel suivant s'effectue en remplaçantn
parn-5
.
Par conséquent, au bout d'un certain nombre d'appels récursifs, n
deviendra strictement inférieur à 5 et le test n >= 5
deviendra faux.
On a trouvé un variant de boucle donc les appels récursifs se terminent.
Calcul et renvoi de valeurs☘
Une fonction récursive peut aussi renvoyer une valeur. Pour cela elle doit :
- Renvoyer la (ou les) valeurs du (des) cas de base ;
- Renvoyer l'appel récursif suivant de cette fonction.
Exemple☘
On appelle factorielle de l'entier n le calcul 1 \times 2 \times 3 \times \dots \times (n-1) \times n. Le but de cet exemple est de programmer une version récursive du calcul de la factorielle d'un entier n
.
- Déterminez le (ou les) cas de base.
- Exprimer
factorielle(n)
en fonction defactorielle(n-1)
. - En déduire la définition récursive de la fonction
factorielle()
:1 2 3 4 5
def factorielle_rec(n): """ n - entier positif ou nul Sortie: entier - le produit des entiers naturels de 1 à n """
Des réponses
-
n
doit être un entier strictement positif (car la multiplication par 0 renvoie 0). Par conséquent, le cas de base est factorielle(1) = 1. -
factorielle(n)
=n
×factorielle(n-1)
. -
Version récursive de la fonction
factorielle()
:1 2 3 4 5 6 7 8 9
def factorielle_rec(n): """ n - entier positif ou nul Sortie: entier - le produit des entiers naturels de 1 à n """ if n == 1 : return n else: return n * factorielle_rec(n-1)