Lycée Victor Hugo MPSI-PCSI 2016-2017
Récursivité
La récursivité est un concept très général qui s’applique à divers objets et diverses démarches
et selon lequel dans la définition de cet objet ou la réalisation de cette démarche, il est fait appel à
l’objet lui-même ou à la démarche elle-même (récurrence, auto-référence, ...).
Par exemple, les cas suivants constituent des cas concrets de récursivité :
Processus dépendant de paramètres et faisant appel à ce même processus sur d’autres para-
mètres plus simples (par exemple : suites récurrentes)
Image contenant en elle-même des images similaires (voir figures ?? et ??)
Concept défini en invoquant le même concept
Algorithme qui s’appelle lui-même.
(a) Vache (b) Arbre récursif
Figure 1 – Exemples de dessins récursifs
I Définitions récursives
1 Définition
Une définition récursive d’un objet a pour caractéristique d’y voir figurer l’objet lui-même :
objet == (une définition qui utilise le mot objet)
2 Exemples
1. Fonctions mathématiques définies récursivement
(a) Multiplication de deux entiers naturels à l’école primaire (reformulation) :aN
n>1, n ×a= (n1) ×a+a
(b) Puissance d’un nombre décimal a(ou dans un groupe G) : n>1, an=an1×a
(c) suite définie par récurrence : nN, un+1 =f(un)
1
Lycée Victor Hugo MPSI-PCSI 2016-2017
2. Arbre généalogique : concaténation de l’individu et des arbres généalogiques de son père et de
sa mère
3. Palindrome : palindrome entouré de deux lettres identiques
Question 1 Ces définitions sont-elles pertinentes ? Que manque-t-il ?
II Algorithme récursif
1 Définition
Un algorithme récursif se caractérise par :
1. une base de récursivité correspondant à un ou plusieurs cas particuliers à partir desquels la
chaîne de récurrence (on dira plutôt d’induction) s’enclenche
2. le traitement des autres éléments faisant appel à un ou plusieurs appels récursifs
Le processus est semblable à la récurrence utilisée en mathématiques qui comporte de même :
1. une phase d’initialisation
2. la propriété dite d’hérédité
2 Structure générale en langage Python
A Cas particulier
Ici le paramètre est un entier n, prend pour valeur initiale 0 et l’appel porte sur l’argument
précédent n1du paramètre
def maFonction (n ) :
if n == 0 :
return le r é sultat pour n = 0
else :
return le r é sult at cal cu l é à l ’ aid e d ’ un appe l à m aF onction ( n -1)
Ainsi pour l’exemple 1.(b), on définira :
def puissance ( a ,n ) :
if n == 0 :
return 1
else :
return a * pui ssance ( a ,n -1)
B Cas plus général
Le codage de manière générale d’une fonction récursive s’écrira selon le canevas :
maFonction (x ) :
if x == ( base ):
return (r é sultat pour ma Fo nction appli qu é e à la base )
else :
...
return (ré sultat calcul é à partir d un ou p lusieurs appels r é cursi fs à
maFonction )
Question 2 Quelle condition est souhaitable sur les arguments avec lesquels on appelle récursivement
la fonction ?
2
Lycée Victor Hugo MPSI-PCSI 2016-2017
3 Déroulement : création et exécution d’une pile
Pour comprendre le processus de calcul, reprenons la fonction puissance et voyons ce qui se passe
lors de l’appel puissance(5,2) permettant de calculer 52; tout se passe à la manière d’une pile.
Au départ la pile est V ide.
Juste après l’appel puissance(5,2), une place est réservée en mémoire à une adresse, notons-la
A0, pour stocker puissance(5,2) : l’état de la pile est donc après une action push
Adresse Valeur
A0?
Dans l’exécution de puissance(5,2), on appelle puissance(5,1) que l’on multiplie par 5 : une
place est cette fois réservée en mémoire en haut de pile (nouveau push) à une adresse que nous
noterons A1pour stocker puissance(5,1) ; et on laisse en attente en A0le calcul 5×V aleur1
où la V aleur1est la valeur « pointée » en adresse A1; l’état de la mémoire est à présent :
Adresse Valeur
A1?
A0 5 ×V aleur1
Dans l’exécution de puissance(5,1) : même démarche qu’à l’étape précédente avec appel à
puissance(5,0) et un nouveau push :
Adresse Valeur
A2?
A1 5 ×V aleur2
A0 5 ×V aleur1
Dans l’exécution de puissance(5,0), on rend directement comme résultat 1:
Adresse Valeur
A21
A1 5 ×V aleur2
A0 5 ×V aleur1
Dans la pile ainsi constituée, on a une valeur en haut de pile : ceci a pour effet de supprimer
(action pop) la tête de pile et d’envoyer sa valeur comme argument à l’adresse A1:
Adresse Valeur
A15
A0 5 ×V aleur1
puis par le même procédé (nouvelle action pop)
Adresse Valeur
A025
La pile est de hauteur 1et contient une valeur, nouvelle action pop qui rend la pile V ide : la
valeur finale est renvoyée comme résultat : 25
4 Exercice : jeu de briques
Coder une fonction Python qui rend le nombre de façons d’obtenir une ligne de briques de longueur
nen n’utilisant que des briques de longueur 2 ou 3 :
73
84
10 7
3
Lycée Victor Hugo MPSI-PCSI 2016-2017
III Intérêt de la récursivité
De nombreux problèmes sont résolus efficacement par l’ordinateur en ayant recours à des algo-
rithmes récursifs. Citons par exemple :
la résolution du problème des tours de Hanoï
l’algorithme hautement efficace de tri par fusion d’une liste
Les algorithmes récursifs sont souvent élégants et plus faciles à mettre en place, cependant ils sont
souvent plus délicats à prouver !
IV Problèmes soulevés par la récursivité
1 Terminaison des algorithmes récursifs
Considérons la fonction Python définie par :
def u(n) :
if n == 0 :
return 1
else :
return n - u (u (n -1) )
Question 3 Que va-t-il se passer ? D’où vient exactement le problème ?
Considérons à présent la célèbre suite de Syracuse (dite aussi de Collatz) définie par la récur-
rence (où cNest une constante d’initialisation donnée) :
a0=cet nN, an+1 =
an
2si anest pair
3an+ 1 si anest impair
On conjecture que pour toute valeur de la constante c, il existe un entier ntel que an= 1.
Le plus petit entier nainsi obtenu s’appelle la longueur du vol (on imagine le vol d’un oiseau,
anreprésentant son altitude à l’instant n).
A l’instar de cette suite, on définit alors la fonction Python suivante :
def longueurD u V ol (c) :
ass ert c >0 ," le para m è tre doit ê tre un nat urel non nul "
if c == 1:
return 0
elif c % 2 == 0:
return 1+ l ongueurDuVol ( c // 2)
else:
return 1+ l on gu eu rD uV ol (3* c +1)
Question 5 Quel problème pose cette fonction ?
Ces deux exemples nous fournissent une illustration du théorème suivant :
Théorème
Soit une fonction récursive dont le paramètre est un entier naturel.
Pour qu’elle termine, il suffit que les appels récursifs portent sur des arguments stricte-
ment inférieurs à l’argument initial (et que le cas n= 0 soit un cas de base)
4
Lycée Victor Hugo MPSI-PCSI 2016-2017
Lorsque la fonction ne porte pas sur un paramètre entier naturel, il est suffisant, pour que cette
fonction termine, que :
1. l’ensemble des paramètres soit muni d’un ordre qui permet de dire qu’un argument est
"avant" un autre,
2. les appels récursifs portent sur des arguments qui sont strictement "avant" l’argument initial.
3. et les premiers arguments soient traités dans les cas des bases
On parle alors d’induction (au lieu de récurrence dans le cas de N)
2 Preuve des algorithmes récursifs
Il s’agit de démontrer que la fonction programmée rend bien pour le résultat escompté. En général
on réglera ce problème en même temps que celui de la terminaison.
Cas particulier : le paramètre est un entier naturel
On utilisera un raisonnement par récurrence (simple, double, forte selon le type de récursivité
utilisée dans la fonction) en posant :
P(n) : l’algorithme termine et rend le résultat escompté pour la valeur ndu paramètre
Cas général : le paramètre fait partie d’un ensemble ordonné
On effectue ici un raisonnement par induction en posant :
P(x) : l’algorithme termine et rend le résultat escompté pour la valeur xdu paramètre
Le raisonnement suit d’assez près le principe du raisonnement par récurrence, plus précisément
celui de récurrence forte.
La phase dite de base (similaire à l’initialisation d’un raisonnement par récurrence) sera :
P(base)est vraie
La phase dite d’induction (similaire à l’hérédité pour un raisonnement par récurrence) sera :
Supposons que P(y)est vraie pour tout ysitué avant x, alors [...] P(x)est vraie
3 Complexité des algorithmes récursifs
Évaluation théorique de la complexité
La complexité d’un algorithme récursif peut (sous quelques réserves) s’évaluer à l’aide du nombre
d’opérations effectuées : il va dépendre de deux éléments :
le nombre d’appels récursifs effectués
le nombre d’opérations effectuées lors de l’appel de la fonction (ligne d’appel, souvent celle qui
suit else:, et qui traite le cas général)
Pour l’évaluer, on peut poser que C(n)est le nombre d’opérations effectuées, déterminer la formule de
récurrence vérifiée par C(n)par relecture de l’algorithme puis utiliser ses connaissances en mathématiques pour
obtenir sinon la formule explicite de C(n), du moins une comparaison O(...)avec une suite connue.
Évaluation de la complexité
Pour donner quelques idées, lorsque le paramètre dépend d’un entier naturel n:
S’il s’agit d’une récursivité simple usuelle et que la ligne d’appel ne nécessite qu’un nombre
borné d’opérations, la complexité de l’algorithme sera O(n)
S’il s’agit d’une récursivité simple et que la ligne d’appel avec la valeur nnécessite un nombre
d’opérations proportionnel à n, la complexité de l’algorithme sera O(n2)
S’il s’agit d’une récursivité double et que la ligne d’appel ne nécessite qu’un nombre borné
d’opérations, la complexité de l’algorithme sera O(2n)
S’il s’agit d’une récursivité simple pour laquelle l’appel est fait sur un paramètre voisin de n/2
(méthode appelée “Diviser pour régner”) et que la ligne d’appel ne nécessite qu’un nombre
borné d’opérations, la complexité de l’algorithme sera O(log2(n))
5
1 / 7 100%