Académique Documents
Professionnel Documents
Culture Documents
LSI1
Plan
• Intérêt de la récursion
• Propriétés des algorithmes récursifs
Démarche à suivre
Exemples (Factorielle et Fibonacci)
Utilisation de l’itération et de la récursion
• Invocation d’un sous programme
Activités
Pile d’exécution
• Processus engendré par un sous programme
▫ Récursion linéaire (factorielle)
Processus récursif : fact()
Trace d’exécution (décomposition et contraction)
Processus itératif : fact_iter()
Points de convergence et points de divergence
Récursivité terminale vs récursivité non terminale
▫ Récursion en arbre binaire (suite de fibonacci)
Trace d’exécution (décomposition et contraction)
• Analyse des algorithmes récursifs
▫ Factorielle
▫ Tour de Hanoi
3
LSI1
Intérêt de la récursion
• La notion de récursivité en informatique est très proche de la notion de
récurrence en mathématique
▫ De nombreuses définitions mathématiques sont récursives
• La récursion ou récursivité intervient fréquemment en programmation
Un objet (pas au sens de la programmation objet) et dit récursif s’il fait appel à lui-
même (d’une façon directe ou indirecte) lors de sa définition ou composition
LSI1
Exemples
5
LSI1
Intérêt de la récursion
Entiers naturels (IN) Factorielle (!) Structure de données
•0 est un entier naturel 0! =1 struct element{
•Tout entier naturel à un n>0 , n!=n*(n-1)! int cle;
successeur unique struct element * suivant;
•Tout entier naturel sauf 0 };
est le successeur d’un
unique entier
LSI1
Intérêt de la récursion
• La récursivité directe correspond au fait qu’un programme fait
appel à lui-même
▫ Factorielle
• La récursivité indirecte correspond au cas où la suite d’appels
qui part de A peut passer par plusieurs autres sous programmes
avant d’arriver de nouveau à A
▫ La définition récursive des nombres pairs et impairs
Un nombre positif n est pair si n-1 est impair et un nombre positif n est
impair si n-1 est pair
7
LSI1
Intérêt de la récursion
Récursion directe
void recursive(void)
{
<instructions de pré-traitement>
if (test de continuation)
recursive()
<instructions de post-traitement>
}
LSI1
Intérêt de la récursion
void rec(void)
void rec(void)
1 { <inst pré>
if (cont) 2 { <inst pré> void rec(void)
rec() if (cont)
3 { <inst pré>
<inst post> 6 rec() if (cont)
} <inst post> 5 rec()
} <inst post> 4
}
appel récursif : branchement
LSI1
Intérêt de la récursion
• La fonction rec() s'appelle elle même 3 fois
▫ chaque nouvel appel conserve l'adresse de retour
• Les instructions de pré-traitement sont exécutées avant les appels
récursifs
• Les instructions de post-traitement sont exécutées après les
appels récursifs
▫ dans l'ordre de dépilement, c'est à dire inverse
10
LSI1
Intérêt de la récursion
Récursion indirecte
Une fonction A appelle une fonction B
Une fonction B appelle la fonction A
LSI1
5!=5*4!
12
LSI1
LSI1
LSI1
Exemples
• Le problème a été réduit à lui-même mais pour un cas plus simple
• fact est une fonction récursive
▫ Chaque appel à fact est susceptible d’introduire un appel à elle-même
• fibo est une fonction récursive
▫ Chaque appel à fibo est susceptible d’introduire deux appels à elle
même
15
LSI1
LSI1
Exemples
Sous programme non récursif Sous programme récursif
fact(0) = 1 , n = 0 ; fact(0) = 1, n = 0;
fact(n) = 1*2*3…*n, n > 0 ; fact(n) = fact(n-1)*n, n > 0;
LSI1
LSI1
LSI1
LSI1
LSI1
LSI1
LSI1
LSI1
LSI1
LSI1
LSI1
LSI1
unsigned fact(unsigned 2)
{
unsigned res;
if (n<= 1)
res = 1;
else
res = fact(1) * 2;
return res;
}
31
unsigned fact(unsigned 3)
{
unsigned res; LSI1
if (n <= 1)
res = 1;
else
res = fact(2) * 3;
return res;
}
unsigned fact(unsigned 2)
{
unsigned res;
if (n <= 1)
res = 1;
else
res = fact(1) * 2;
return res;
}
unsigned fact(unsigned 1)
{
unsigned res;
Une fois fact(1) est atteint les appels if (n <= 1)
récursifs s’arrêtent et la récursion res = 1;
else
commence à retourner des résultats res = fact(n - 1) * n;
return res;
}
32
unsigned fact(unsigned 3)
{
unsigned res; LSI1
if (n <= 1)
res = 1;
else
res = fact(2) * 3;
return res;
}
unsigned fact(unsigned 2)
{
unsigned res;
if (n <= 1)
res = 1;
else
res = fact(1) * 2;
return res;
}
unsigned fact(unsigned 1)
{
unsigned res;
if (n <= 1)
res = 1;
else
res = fact(n - 1) * n;
return 1;
}
33
unsigned fact(unsigned 3)
{
unsigned res; LSI1
if (n <= 1)
res = 1;
else
res = fact(2) * 3;
return res;
}
unsigned fact(unsigned 2)
{
unsigned res;
if (n <= 1)
res = 1;
else
res = 1 * 2;
return res;
}
unsigned fact(unsigned 1)
{
unsigned res;
if (n <= 1)
res = 1;
else
res = fact(n - 1) * n;
return 1;
}
34
unsigned fact(unsigned 3)
{
unsigned res; LSI1
if (n <= 1)
res = 1;
else
res = fact(2) * 3;
return res;
}
unsigned fact(unsigned 2)
{
unsigned res;
if (n <= 1)
res = 1;
else
res = 1 * 2;
return 2;
}
35
unsigned fact(unsigned 3)
{
unsigned res; LSI1
if (n <= 1)
res = 1;
else
res = 2 * 3;
return res;
}
unsigned fact(unsigned 2)
{
unsigned res;
if (n <= 1)
res = 1;
else
res = 1 * 2;
return 2;
}
36
unsigned fact(unsigned 3)
{
unsigned res; LSI1
if (n <= 1)
res = 1;
else
res = 2 * 3;
return 6;
}
37
LSI1
fact(3) 4
LSI1
*
fact(3) 4
*
fact(2) 3
*
unsigned fact(unsigned n)
fact(1)->1 2 {
unsigned res;
if(n<=1) //cas trivial
res=1;
else //cas récursif (decomposition)
res = fact(n-1) * n; (composition)
return res;
}
39
LSI1
*
fact(3) 4
*
fact(2)->2 3
unsigned fact(unsigned n)
{
unsigned res;
if(n<=1) //cas trivial
res=1;
else //cas récursif (decomposition)
res = fact(n-1) * n; (composition)
return res;
}
40
LSI1
fact(4)
*
fact(3)->6 4
unsigned fact(unsigned n)
{
unsigned res;
if(n<=1) //cas trivial
res=1;
else //cas récursif (decomposition)
res = fact(n-1) * n; (composition)
return res;
}
41
LSI1
fact(4)->24
unsigned fact(unsigned n)
{
unsigned res;
if(n<=1) //cas trivial
res=1;
else //cas récursif (decomposition)
res = fact(n-1) * n; (composition)
return res;
}
42
LSI1
Pile d’exécution
dépilement
…..
fact(4)=24
empilement
…..
res = 1
n=1
3
res = fact(1)*2 ? fact(1)
res =1*2
n=2 n=2
…..
2
res = fact(2)*3 ? fact(2) res = fact(2)*3 ? res = 2*3
…..
n=3 n=3 n=3
1
res =fact(3)*4 ? fact(3) res =fact(3)*4 ? res =fact(3)*4 ? res =6*4
n=4 n=4 n=4 n=4
fact(4)
43
LSI1
LSI1
dépilement
…..
fact_iter(3)=6
return 6
empilement
…..
max_compteur = 3
compteur = 4
produit = 6 3
return ? fact_iter(6,4,3)
return 6
max_compteur = 3 max_compteur = 3
compteur = 3
…..
compteur = 3
produit = 2 produit = 2
2
return ?
max_compteur = 3 fact_iter(2,3,3) return ? return 6
max_compteur = 3 max_compteur = 3
…..
compteur = 2 compteur = 2 compteur = 2
produit = 1 produit = 1 produit = 1
return ?
1
return ? return ? return 6
max_compteur = 3 fact_iter(1,2,3)
max_compteur = 3 max_compteur = 3 max_compteur = 3
compteur = 1 compteur = 1 compteur = 1 compteur = 1
produit = 1 produit = 1 produit = 1 produit = 1
fact_iter(1,1,3) fact_iterative(3)
46
LSI1
Points de convergence
• Les deux fonctions fact et fact_iter
▫ Offrent le même service permettant de calculer n!
Par rapport à la partie utilisation
▫ Produisent un nombre d’étapes de calcul proportionnel à n
T(n) = T(n-1) + C
T(n-1) = T(n-2) + C
T(n-2) = T(n-3) + C
……………………..
T(2) = T(1) + C
En additionnant membre à membre, on arrive à: T(n) = T(1) + C(n-1) = O(n)
LSI1
Points de divergence
• Le processus engendré par fact est caractérisé par une activité de
développement suivie d’une activité de contraction
▫ Des opérations sont retardées
▫ L’activité de contraction effectue progressivement l’opération de
multiplication retardée
• Le processus engendré par fact_iter
▫ N’implique ni développement, ni contraction
48
LSI1
Points de divergence
• La reprise du processus de calcul engendré par fact_iter suite à un
arrêt (c-à-d à une étape donnée) est facile
• La reprise suite à un arrêt pour le sous programme fact est loin
d’être facile
LSI1
Solution itérative
Inspirée de celle basée sur la récursivité terminale
50
LSI1
Récursivité terminale
• Une fonction récursive terminale est en théorie plus efficace que
son équivalent non terminale
• En récursivité terminale les appels récursifs n’ont pas besoin
d’être empilés dans la pile d’exécution
▫ L’appel suivant remplace simplement l’appel précédent dans la pile
d’exécution
• Il est facile de transformer de façon simple une fonction récursive
terminale en une version itérative
▫ C’est la dérécusivation
51
LSI1
LSI1
LSI1
T : type de retour
P : liste des paramètres et f(P) fonction de transformation des paramètres
C : condition d’arrêt
I0, I1, I2, I3 : instructions
54
LSI1
LSI1
• Question
▫ Suppose we start with a single newborn male-female pair in the first month
▫ What will be the number rabbit pairs in month n?
56
LSI1
LSI1
fibo(4) fibo(3)
LSI1
fibo(3) fibo(2)
+ +
fibo(2) fibo(1)->1 fibo(1)->1 fibo(0)->0
+
fibo(1)->1 fibo(0)->0
59
LSI1
fibo(3) fibo(2)
+ +
LSI1
fibo(3)->2 fibo(2)
+
fibo(1)->1 fibo(0)->0
61
LSI1
fibo(3)->2 fibo(2)->1
62
LSI1
LSI1
LSI1
LSI1
LSI1
LSI1
T(n) = T(n-1) + 5
T(n-1) = T(n-2) + 5
T(n-2) = T(n-3) + 5
……………………..
T(2) = T(1) + 5
En additionnant membre à membre on a T(n) = T(1) + 5(n-1) = O(n)
68
LSI1
LSI1
LSI1
LSI1
LSI1
LSI1
Déplacer 2 de B à C
74
LSI1
Hanoi (3,A,B,C)
Déplacer (B,A)
Hanoi (1,A,B,C) Déplacer (A,B) Hanoi (1,C,A,B)
Déplacer (B,C)
Déplacer (A,C) Déplacer (C,B)
Déplacer (A,C)
75
LSI1
A -> C
A -> B
C -> B
A -> C
B -> A
B -> C
A -> C
76
LSI1
Analyse de Hanoi
• Pour déterminer la complexité de cette fonction
▫ déterminer combien de fois elle fait appel à elle-même
▫ dans le corps de cette fonction, il y a:
Un test
Deux appels à elle même
Deux soustractions
Une opération de sortie
LSI1
Analyse de Hanoi
• Soit t(n) la complexité de la fonction hanoi(n,i,j,k)
• La relation entre t(n) et t(n-1) est comme suit
t(n) = t(n-1)+ t(n-1) + 6, si n > 0
t(0) = 1 (un seul test)
t(n) = 2 t(n-1) + 6
2 t(n-1) = 2 (2t(n-2) + 6) =4 t(n-2) + 2.6
4t(n-2) = 8 t(n-3) + 4.6
...................................
...................................
2n-1 t(1) = 2nt(0) + 6.2n-1
En additionnant membre à membre, on obtient
t(n) = 2nt(0) +6(1+2+4+...... +2n-1)
= 2n + 6. 2n
= O(2n).
78
LSI1
En conclusion
• Vis-à-vis de l’utilisateur la même interface est proposée
▫ indépendamment de la nature de la solution (itérative ou récursive)
• Toute solution récursive peut être transformée en une solution
itérative
▫ le coût de transformation est un critère important à envisager
LSI1
En conclusion
• Occasionnellement, une solution récursive s'exécute de façon beaucoup plus
lente que son équivalent itératif
▫ les nombres de fibonacci
• Par contre, dans la majorité des cas, la solution récursive est légèrement plus
lente
• Dans la majorité des cas, la solution récursive est plus facile à comprendre et
à implanter correctement que la solution itérative correspondante
▫ ce qui est un atout
• Dans les langages (comme Java) qui offrent à a fois l’itération et la récursion on va
préférer la récursivité surtout dans les situations où la solution itérative
est difficile à obtenir
▫ Si les structures de données manipulées sont récursives (les arbres)
▫ Si le raisonnement lui-même est récursif
80
LSI1
Exemple
• Ecrire un sous-programme récursif qui calcule la somme des
éléments positifs d’un tableau
o Définir les paramètres du problème.
o Identifier les cas triviaux et les cas élaborés.
o Expliquer et illustrer par un exemple s’il s’agit d’une récursivité terminale ou
non terminale
o Ecrire un programme qui permet de tester le sous-programme proposé.
81
LSI1
Exemple 1 : Corrigé
Les paramètres du problème sont :
Le tableau T
Les indices de début (deb) et de fin (fin) de la partie du tableau
à traiter
Cas trivial :
Deb>fin : somme = 0
Cas élaborés :
Deb ≤ fin , ça nécessite un appel récursif.
82
LSI1
#include <stdio.h>
void main ()
{
int T[]={2,-5,3,8};
printf("%d\n", somme(T,0,3));
}
83
LSI1