Next: 2 Listes
Up: Algorithmique
Previous: Algorithmique
n ! = n * n-1 * ... 2 * 1
n ! = n * (n-1) ! et 0 ! = 1
On remarquera que pour calculer n ! il nous faut pouvoir calculer $(n-1) !$.\begin{rawhtml} (n-1) ! Si l'on connait le factoriel d'un nombre p , alors en utilisant cette définition récursive, on pourra calculer le factoriel de des nombres p +1, p +2, etc ... Ainsi, une défintion récursive est composée de deux partie: une partie strictement récursive et une partie non récursive (base) servant de point de départ à l'utilisation de la définiton récursive.Comme pour les définition récursives, les fonctions récursives sont de fonctions qui sont appelées depuis leur propre corps de fonction soit directement soit indirectement à travers une ou plusieurs fonctions relais. Si la fonction P appelle directement P , on dit que la récursivité est directe. Si P appelle une fonction P1 , qui appelle une fonction P2 , ... , qui appelle une fonction Pn et qui enfin appelle P , on dit qu'il s'agit d'une récursivité indirecte.
La grande question qui se pase dans une fonction récursive est celle
de l'arrêt de la récursivité. Prenons le cas de la récusivité directe
pour simplifier la présentation: si la fonction P appelle dans son
propre corps la fonction P , il est tout à fait logique de penser que
cette ne s'arrêtera jamais et fonctionnera à << l'infini >> Il est donc primordial qu'une des
branches de la fonction P permette de stopper la récursivité.
Pernons un exemple pour clarifier tout ça. Suppons que l'on veuille calculer la somme des n premiers entiers positifs. Une défintion récursive de cette somme serait:
somme(n) = n * somme(n-1) et somme(1) = 1
Ce qui se traduit en C par la fonction suivante:int somme(int n) { int m; if (n==1) return 1; else { m = somme(n-1); return n + m; } }
Il est clair que sans le test if (n==1), cette fonction ne s'arrêterait jamais !
L'appel somme(3) suivra le même processus et appelera somme(2) qui lui-même fera appel à somme(1).
somme(4) | |||||
somme(3) | |||||
somme(2) | |||||
somme(1) | |||||
retourne 1 | |||||
retourne 2 + 1 | |||||
retourne 3 + 3 | |||||
retourne 4 + 6 |
Il est essentiel de comprendre que lorsqu'une fonction récursive définit des varibales locales, un exemplaire de chacune d'entre elles est créée à chaque appel récursif de la fonction. Dans notre exemple somme(4), la variable locale m est créée 4 fois. Ces variables sont restituées au fur et à mesure que l'on quitte la fonction comme toute variable locale d'une fonction.
somme(4) | |||||
création du premier m | |||||
somme(3) | |||||
création du deuxième m | |||||
somme(2) | |||||
création du troisième m | |||||
somme(1) | |||||
création du quatrième m | |||||
destruction de m | |||||
retourne 1 | |||||
destruction de m | |||||
retourne 2 + 1 | |||||
destruction de m | |||||
retourne 3 + 3 | |||||
destruction de m | |||||
retourne 4 + 6 |
Il en est de même des arguments des fonctions.
Considérons une fonction void miroir() récursive qui lit caractère par caractère une cahîne terminée par '?' et l'affiche dans l'ordre inverse de celui de la lecture. Exemple d'exécution:
Cette fonction pourrait ressembler à ceci:
void miroir() { int c = getchar(); if (c != '?') { miroir(); printf("%c", c); } }
miroir() | |||||
c = '1' | |||||
miroir() | |||||
c = '2' | |||||
miroir() | |||||
c = '3' | |||||
miroir() | |||||
c = '?' | |||||
destruction de c | |||||
retour | |||||
affichage de '3' | |||||
destruction de c | |||||
retour | |||||
affichage de '2' | |||||
destruction de c | |||||
retour | |||||
affichage de '1' | |||||
destruction de c | |||||
retour |
Cette même fonction sans vraibale locale aurait nécessité un tableau pour stocker tous les caractères jusqu'au caractère '?' pour ensuite les afficher dans l'ordre inverse.
Une structure de données récursives, tout comme une définition récursive, est une structure qui fait référence à elle-même. Les structures de données naturellement récursives sont les listes chaînées et les arbres que nous étudierons en détail dans les chapitres 3 et 2.
Touraivane