Académique Documents
Professionnel Documents
Culture Documents
STPI - 2A
2020-2021
Séance 5: Récursivité
1 / 26
Récursivité
• Dans la résolution d’un problème, il est courant d’avoir à résoudre des
instances plus petites du même problème. Exemple mathématique :
calcul du déterminant d’une matrice.
• C’est ce principe qui est appelé récursivité.
• Stratégie qui permet de simplifier l’écriture, les preuves de correction
et le calcul de complexité des algorithmes.
• Utilisation des preuves par récurrence !
• Les premiers langages de programmation qui ont autorisé l’emploi de
la récursivité sont LISP et Algol 60.
• Tous les langages de programmation (ou presque) proposent une mise
en œuvre de la récursivité
• Les algorithmes récursifs sont souvent opposés aux algorithmes dits
itératifs.
2 / 26
Première approche incomplète
Définition de la fonction factorielle
• n! = n ∗ (n − 1)!
Condition d’arrêt
Condition d’arrêt
• Puisqu’une fonction récursive s’appelle elle-même, il est impératif de
prévoir le cas où on ne doit pas continuer le calcul.
• Cette condition doit être testée en premier, avant de lancer l’appel
récursif.
factorielle
Fonction factorielle(In Variable n : Entier) : Entier
Si n=0 Alors
Retourne 1
Sinon
Retourne n * factorielle(n-1)
FinFonction 4 / 26
Calcul de factorielle(3)
5 / 26
Pile d’appel
• Lors d’un appel de fonction, il faut se placer dans un contexte
spécifique qui isole les variables locales et les calculs locaux
• A la sortie de cet appel, il faut se souvenir d’où venait l’appel
• Une fonction f peut en appeler une fonction g qui peut appeler une
fonction h. À chaque fois, il faut se rappeler où on va et d’où l’on
vient !
• Utilisation d’une structure de données de type pile
6 / 26
Exemple simplifié
7 / 26
• Les appels récursifs de fonctions sont basés sur le même principe. Les
zones dans la piles identifient clairement les différents appels.
• Prévoir à l’avance la profondeur d’appels récursifs d’une fonction est
souvent complexe, voire impossible. La pile permet d’avoir une
allocation dynamique.
• Les langages de programmation acceptent pour la plupart les
fonctions récursives, mais ce n’est pas le cas de certains langages
anciens (COBOL, FORTRAN 77, BASIC, . . .).
• En programmation fonctionnelle (LISP, CAML, . . .) ou en
programmation logique (PROLOG), les programmes sont
majoritairement récursifs.
8 / 26
Limite de la pile d’exécution
Trop d’appels récursif ?
• Débordement possible de la pile
• Problème pratique d’allocation d’espace mémoire
• Pas un souci en algorithmique
• Pas un souci avec les machines actuelles
#include <stdio.h>
Appel recursif 0
Appel recursif 1
// Fonction trop recursive
Appel recursif 2
void recurs(int n){
Appel recursif 3
printf("Appel recursif %i\n",n
Appel recursif 4
);
Appel recursif 5
recurs(n+1);
...
}
Appel recursif 261848
Appel recursif 261849
// Utilisation minimale
Appel recursif 261850
int main(){
Appel recursif 261851
recurs(0);
Appel recursif 261852
return 0;
Erreur de segmentation (core dumped)
}
9 / 26
Fibonacci récursif
Fonction Fibo(In Variable n : Entier) : Entier
Si i<2 Alors
Retourne 1
Sinon
Retourne Fibo(n-1) + Fibo(n-2)
FinFonction
Exercices
1. Exécuter Fibo(4)
2. Evaluer la complexité
11 / 26
Exercices
1. Exécuter Fibo(4)
2. Evaluer la complexité
12 / 26
Un tri récursif
Tri fusion
• Idée principale :
1. Diviser le tableau en deux parties égales
2. Trier chaque partie
3. Fusionner les deux parties
Algorithme
Fonction TriFusion(In-Out Variable tab : tableau d’entiers,
In Variable i,j : Entier) : Vide
Variable mil : Entier
Si i=j Alors
Retourne // Une case est triée!
Sinon
TriFusion(tab, i, mil)
TriFusion(tab, mil+1, j)
Fusion(tab, i, mil, j)
FinFonction 13 / 26
Étape 3
Fonction Fusion(In-Out Variable tab : tableau d’entiers,
In Variable i,j,k : Entier) : Vide
// On suppose que 1 ≤ i ≤ j ≤ k ≤ taille(tab), que tab[i..j] et tab[(j+1)..k] sont
triés
Variable i0, j0, l0 : Entiers
Variable tab2 : tableau d’Entiers
alloue(tab2, k-i+1) // Pour avoir le bon nombre d’éléments
i0 ← i, j0 ← j+1, l0 ← 1
Tant Que i0 ≤ j et j0 ≤ k
Si tab[i0]<tab[j0] Alors
tab2[l0] ← tab[i0], i0 ← i0 +1
Sinon
tab2[l0] ← tab[j0], j0 ← j0 +1
l0 ← l0 +1
Tant Que i0 ≤ j
tab2[l0] ← tab[i0], i0 ← i0 +1, l0 ← l0 +1
Tant Que j0 ≤ k
tab2[l0] ← tab[j0], j0 ← j0 +1, l0 ← l0 +1
Pour l0 allant de 1 à k-i+1 par pas de 1
tab[i-1+l0] ← tab2[l0] 14 / 26
// à la fin, tab[i..k] est trié
FinFonction
Etude de la complexité du tri fusion
Fusion des deux listes triées
• Principe de la fermeture éclair
• Pour la création du tableau tab2, à chaque étape du Tant-que, on
ajoute un élément
• Exactement (k − i + 1) recopies
• La recopie de tab2 dans tab prend le même temps.
• Au total O(k − i) étapes, soit la longueur du tableau fusionné.
Tri Fusion
O(1) si n = 1
Fusion(n) =
2 * Fusion(n/2)+ O(n) sinon
Fusion(n) = O(n lg n)
15 / 26
Correction
La correction de l’algorithme est directe si la formule est juste
mathématiquement !
16 / 26
Terminaison des algorithmes récursifs
Potentiel
• Partie complexe dans le cas général
• Il faut montrer qu’il existe une fonction positive entière, dépendant
des paramètres qui décroit à chaque appel : un potentiel !
factorielle : On prend comme fonction potentiel n. Cette fonction
est toujours positive ou nulle, entière, l’appel suivant est
n − 1 donc strictement décroissant
pgcd : a + b
Conjecture de Syracuse
• Suite de Syracuse pour un entier N > 0 : U0 = N et
Un /2 si Un est pair
Un+1 =
3Un + 1 sinon
Récursivité croisée
• Cas de deux fonctions s’interdéfinissant !
19 / 26
Quelques éléments
• A(O, n) = n + 1,A(1, n) = n + 2, A(2, n) = 2n + 3
• A(3, n) = 2n+3 − 3
• A(4, 0) = 13, A(4, 1) = 65533,
65536
A(4, 2) = 265536 − 3,A(4, 3) = 22 − 3 ...
Ackermann(3, 0) = 5
Ackermann(3, 1) = 13
Ackermann(0, 0) = 1 Ackermann(3, 2) = 29
Ackermann(1, 0) = 2
Ackermann(1, 1) = 3 ...
Ackermann(2, 1) = 5
Ackermann(2, 2) = 7 Ackermann(3, 12) = 32765
Ackermann(3, 2) = 29
Ackermann(3, 3) = 61 Ackermann(3, 13) = 65533
Erreur de segmentation (
Erreur de segmentation ( Ackermann(3, 14) =
core dumped)
core dumped) 131069
Erreur de segmentation (
core dumped)
20 / 26
Structures de données et récursivité
Tableaux
• Séparation d’un tableau en sous-tableaux
• Utilisation des indices pour faire la récursivité
• Exemple du tri fusion
Listes
• Vision récursive d’une liste :
• soit vide (∅)
• soit une cellule suivie d’une liste
• Structure le traitement naturellement de manière récursive
• On peut assimiler alors l.tête et l
• l.suivant est de type liste.
21 / 26
22 / 26
Exercice avec une liste
Exercice
Écrire une fonction récursive qui détermine si une valeur est présente dans
une liste. La fonction renverra une référence sur la cellule où se trouve
l’élément, ∅ si l’élément n’est pas présent.
23 / 26
Tapis de Sierpiński
• Ensemble fractal
• Défini récursivement à l’infini
24 / 26
Se prendre les pieds dans le tapis
Exercice
Sachant que le côté du carré initial est 1 et qu’on le divise en 9 parties
identiques, écrire une fonction qui prend en entrée un point du carré (x, y )
et indique s’il est noir.
Et se relever
26 / 26