Récursivité Définition récursive d’un problème. Critère d’arrêt et convergence. Performance des algorithmes récursifs. Résolution d’un problème à l’aide de la récursivité.
Bourgeois gentilhomme (Acte II Scène IV) de Molière Récursivité Bourgeois gentilhomme (Acte II Scène IV) de Molière Le héros, Monsieur Jourdain, veut connaître toutes les manières « galantes » d'écrire un billet. De la phrase Belle Marquise, vos beaux yeux, me font mourir d'amour, il pourrait tirer Vos beaux yeux, belle Marquise, d'amour me font mourir, puis Vos beaux yeux, me font mourir, belle Marquise, d'amour, puis Vos beaux yeux, me font mourir d'amour, belle Marquise etc. Comment Monsieur Jourdain devrait-il procéder pour engendrer toutes ces permutations? Le mieux pour lui pour être sûr d'y arriver est d'utiliser un procédé récursif. On construit toutes les permutations de la phrase vos beaux yeux -- me font mourir -- d'amour; puis, dans ces permutations, on insère en première, deuxième, troisième et quatrième position le morceau de phrase belle Marquise.
Récursivité : générateur de courbes fractales générateur forme initiale arbre fractale
Récursivité Une fonction ou une procédure récursive s’appelle elle-même. Exemple I : Calcul de n! #include <iostream.h> #include <cassert> int Factorielle(int n) // Calcule la factorielle de n. // // Paramètre d'entrée : // n : un entier positif ou nul. // Valeur de renvoi. // la valeur de n! { assert(n >= 0); if (n == 0 || n == 1) return 1; return n * Factorielle(n - 1); }
Récursivité void main() { int n; cout << "Entrez un nombre entier positif ou nul : "; cin >> n; cout << "La factorielle de " << n << " est : " << Factorielle(n) << endl; } Exemple II : Calcul du nième nombre de Fibonacci (Fn). 1 si n = 1 ou 2, Fn = Fn-1 + Fn-2 si n > 2.
Récursivité #include <iostream.h> #include <cassert> int Fibonacci(int n) // Calcule le n ième nombre de Fibonacci. // // Paramètre d'entrée : // n : un entier positif. // Valeur de renvoi. // la valeur du n ième nombre de Fibonacci. { assert(n > 0); if (n == 1 || n == 2) return 1; return Fibonacci(n - 1) + Fibonacci(n - 2); } void main() int n; cout << "Entrez un nombre entier positif : "; cin >> n; cout << "Le " << n << " ieme nombre de Fibonacci est : " << Fibonacci(n) << endl;
Efficacité Dans ces exemples, les fonctions récursives ne sont pas efficaces. n = 47 Mauvais résultat int Fibonacci(int n) { assert(n > 0); int F1 = 1; int F2 = 1; int resultat; if (n == 1 || n == 2) return 1; for (int i = 3; i <= n; i++) resultat = F1 + F2; F1 = F2; F2 = resultat; } return resultat; n = 50 Je n’ai pas réussi à obtenir une réponse avec l’approche récursive. Évite de recalculer plusieurs fois le même nombre. Les appels récursifs sont coûteux.
Efficacité Fibonacci(5) Fibonacci(4) Fibonacci(3) Fibonacci(3) Beaucoup de redondance.
Coûteux en espace mémoire Les fonctions récursives exigent aussi beaucoup d’espaces mémoires. int Essai(int n) // Calcule la sommation suivante : 1 + 2 + ... + n. // // Paramètre d'entrée : // n : un entier positif. // Valeur de renvoi. // la valeur de la sommation. { assert(n > 0); int i; if (n == 1) return 1; i = n + Essai(n - 1); return i; }
Coûteux en espace mémoire Appels successifs Retours d’appels pile pile Essai(1) 1 ? Essai(2) 2 3 Essai(2) 2 ? Essai(3) 3 ? 4 ? 3 ? Essai(4) Essai(3) 4 ? Essai(4) n i Essai(3) 3 6 4 ? Essai(4) 4 10 Essai(4)
Usage d’une pile #include <iostream.h> void Lecture() { char ch; cin >> ch; if (ch != '#') Lecture(); cout << ch; } void main() cout << endl; Affichage d’une chaîne inversée. abc# cba
Stratégie pour résoudre un problème La récursivité peut être utile pour résoudre un problème pouvant être décomposé en sous-problèmes de même type mais de plus petite taille. 3 étapes : Décomposez le problème original en sous-problèmes ou envisagez différentes façons pour simplifier les entrées. Combinaison des solutions des sous-problèmes pour obtenir la solution du problème original. Identification d’un cas trivial et de sa solution (sans appel récursif).
Exemple : la tour de Hanoï Énoncé : n disques de diamètres différents sont posés les uns sur les autres, par ordre de taille décroissante, sur un socle A, il s’agit de les transférer sur le socle B en utilisant le socle C. Déplacements autorisés : prendre un disque au sommet d’une des piles, le poser sur un disque plus grand ou sur un socle vide. A B C État initial
Exemple : la tour de Hanoï Stratégie : Cas trivial : si n est égale à 1 alors déplacer le disque de A dans B. Cas général : Transférer n – 1 disques de A à C en utilisant B comme socle intermédiaire. Déplacer le disque qui reste en A sur B. Transférer les n – 1 disques de C en B en utilisant A comme socle intermédiaire. On dit que le jeu des Tours de Hanoï a pour origine un rituel des prêtres de Brahman pour prédire la fin du monde. Les prêtres de Brahman utilisaient n = 64 disques.
Exemple : la tour de Hanoï #include <iostream.h> void Hanoi(int n, char A, char B, char C) { if (n == 1) cout << "Deplacer le disque " << A << " dans " << B << ".\n"; else Hanoi(n - 1, A, C, B); cout << "Deplacer un disque " << A Hanoi(n - 1, C, B, A); } void main() Hanoi(5, 'A', 'B', 'C'); cout << endl;
Algorithme d’Euclide : PGCD de 2 entiers PGCD(n, m) si n > m PGCD(m, n) = m si n ≤ m, n = 0 PGCD(n, m % n) si n ≤ m, n > 0 #include <iostream.h> #include <cassert> int PGCD( int m, int n) { assert(m != 0 || n != 0); if(n != 0) return PGCD(n, m % n); return m; } void main() int m, n; cin >> m >> n; cout << PGCD(m, n); cout << endl;
Comment déterminer si une chaîne est un palindrome ? Une chaîne égale à elle-même en inversant tous ses caractères. Ex. : rotor #include <iostream.h> #include <string.h> bool est_palindrome(char * s) { int i = strlen(s); if(i <= 1) return true; if(s[0] != s[i-1]) return false; char * raccourci = new char[i-1]; strncpy(raccourci, s+1, i-2); raccourci[i-2] = '\0'; return est_palindrome(raccourci); } void main() char * chaine = "rotor"; cout << est_palindrome(chaine) << endl;
Comment déterminer si une chaîne est un palindrome ? Approche plus élégante. #include <iostream.h> #include <string.h> bool souschaine_est_palindrome(char * s, int debut, int fin) { if (debut >= fin) return true; if(s[debut] == s[fin]) return souschaine_est_palindrome(s, debut+1, fin-1); else return false; } bool est_palindrome(char * s) return souschaine_est_palindrome(s, 0, strlen(s) - 1); void main() char * chaine = "rotor"; cout << est_palindrome(chaine) << endl;