Vous êtes sur la page 1sur 22

La récursivité et le paradigme

‘’diviser pour régner’’


Plan
- La récursivité
-Le paradigme ‘’diviser pour régner’’

A. EL ALLAOUI
QU’EST-CE QUE LA RÉCURSIVITÉ?

Une fonction C est dite récursive si elle s’appelle elle-même.

L’appel d’une fonction à l’intérieur d’elle-même est nommé appel


récursif. Un appel récursif doit obligatoirement être dans une
instruction conditionnelle (sinon la récursivité est sans fin).

Exemple 1
La fonction suivante calcule la factorielle n! d’un nombre n. On se base sur la
relation de récurrence n! =(n−1)!∗n

Implémentation itérative
int FactorielleRecursive(int n)
{ int FactorielleIterative(int n)
if (n <= 0)
{
return 1;
int i, resultat=1;
else
return n * FactorielleRecursive(n-1); for (i=1 ; i<=n ; i++)
/* appel récursif */ resultat *= i;
} return resultat;
}
RÉCURSIVITÉ DES PROCÉDURES

Dans une procédure récursive, il y a deux notions à retenir :

la procédure s’appelle elle-même : on recommence avec de


nouvelles données.
il y a un test de fin : dans ce cas, il n’y a pas d’appel récursif. Il est
souvent préférable d’indiquer le test de fin des appels récursifs en
début de procédure.
Vvoid proc(…){
if (fin){
… //pas d’appel récursif
} else {
…..
proc(…); // la procédure proc s’appelle elle-même une ou
plusieurs fois
……
}
}
COMMENT PROGRAMMER UNE FONCTION RÉCURSIVE?

Pour créer une fonction récursive, il faut :


1. décomposer un problème en un ou plusieurs sous-problèmes du
même type. On résoud les sous-problèmes par des appels
récursifs.
2. les sous-problèmes doivent être de taille plus petite que le
problème initial.
3. enfin, la décomposition doit en fin de compte conduire à un cas
élémentaire, qui, lui, n’est pas décomposé en sous-problème.
(condition d’arrêt).

Exemple

Problème : calculer n!.


Sous-problème : calculer (n−1)! . On multipliera le résultat de la
résolution du sous-problème par n.
Le calcul de (n−1)! passe par le calcul de (n−2)! , etc...
À la fin, le problème devient le calcul de 0! . C’est un problème
élémentaire : le résultat vaut 1.
Structure d’une fonction récursive

FONCTION FonctionRécursive(type1 p1, type2 p2,..., typek pk) : typeretour


début
si condition faire /* condition d’arrêt */
retourner calcul; /* cas élémentaire */
sinon faire
FonctionRécursive(...); /* appel récursif */
...
FonctionRécursive(...); /* appel récursif */
retourner quelque-chose;
fin faire
fin
Exemple 2 : nombres de Fibonacci
La suite des nombres de Fibonacci se définit comme suit :

Valeur de fn pour n de 0 a 8

Exercice: implémenter Le programme C récursif de la fonction de calcul des


nombres Fibonacci
Programme des nombres de Fibonacci

Le programme C récursif correspondant est donné ci-dessous.


/* fibonacci.c calcul de fibonacci d'un entier n >= 0 */

#include <stdio.h>
// version 1 pour explication
long fibonacci(int n) {
if (n <= 1) {
return n;
} else {
long x = fibonacci(n-1);
long y = fibonacci(n-2);
return x + y;
}
}
void main() {
printf ("Fibonacci de n ? ");
int n; scanf ("%d", &n);
printf ("Fibonacci de %d est %ld\n", n, fibonacci(n) );
}
Analyse: Nombres de Fibonacci

Une fonction récursive peut s’appeler elle-même plusieurs fois avec des
paramètres différents. La fonction fibonacci(n) s’appelle récursivement 2
fois en fibonacci (n-1) et fibonacci (n-2).
Exemple d’algorithme récursif : Le jeu des tours de Hanoï

Le jeu est constitué d’une plaquette de bois où sont plantées trois tiges. Sur ces
tiges sont enfilés des disques de diamètres tous différents.

Les seules règles du jeu sont que l’on ne peut déplacer qu’un seul disque à la fois,
et qu’il est interdit de poser un disque sur un disque plus petit.

Au début tous les disques sont sur la tige de gauche, et à la fin sur celle de droite.

tige départ tige intermédiaire tige destination


Pour déplacer N anneaux d'une colonne de départ à une
colonne d'arrivée en utilisant une colonne intermédiaire, on
suppose que l'on peut déplacer N – 1 anneaux du départ à
l'intermédiaire, déplacer l'anneau restant du départ à
l'arrivée et déplacer les N – 1 anneaux de l'intermédiaire à
l'arrivée.
Exercice: donner l’ Exécution avec trois disques
Simulation
Diviser pour régner
Dans l’algorithme récursif pour le calcul de la suite de Fibonacci, on a vu que le
problème de calcul de Fibo(n) a été séparé en deux "sous-problèmes" semblables au
problème initial mais de taille inférieure : le calcul de Fibo(n - 1) et le calcul de
Fibo(n - 2).
La résolution des sous-problèmes s’effectue de façon récursive et la solution du
problème original est obtenue en combinant toutes les solutions partielles :
Fibo(n - 1) + Fibo(n - 2).
Cette méthode de conception d’algorithmes s’appelle la technique « Diviser pour
Régner ». Elle donne lieu à trois étapes à chaque niveau de récursivité :

Diviser : le problème en un certain nombre de sous-problèmes ;


Régner : sur les sous-problèmes en les résolvant récursivement ou, si
la taille d’un sous-problème est assez réduite, le résoudre directement ;
Combiner : les solutions des sous-problèmes en une solution complète
du problème initial.
RQ: Les algorithmes récursifs utilisent naturellement cette technique
Recherche du maximum et du
minimum d’un tableau
• Problème. Trouver respectivement le maximum et le
minimum dans un tableau T[1..N].
– Entrée : un entier N et un tableau T[1..N] de N éléments entiers.
– Sortie : deux entiers min et max tels que min = Min(T) et max =
Max(T).

• Un algorithme « Diviser pour Régner » :


– Diviser : diviser le tableau en deux sous-tableaux.
– Régner : trouver récursivement le maximum et le minimum dans
chaque demitableau.
– Combiner : retourner le maximum des deux maximums trouvés et le
minimum des deux minimums trouvés.
Recherche du maximum et du
minimum d’un tableau
// Calcul le minimum et le maximum dans le tableau A[deb…fin]

Fonction DivRegMinMax(A : Tableau d’Entier, deb, fin : Entier) : Couple d’Entier


Variables mid, min1, max1, min2, max2 : Entier;
Début
// Cas de Base
Si (fin - deb <= 1) Alors Retourner(Min(A[deb], A[fin]), Max(A[deb], A[fin]));
Sinon
mid := (deb + fin) / 2; // Diviser
(min1, max1) := DivRegMinMax(A, deb, mid); // Régner
(min2, max2) := DivRegMinMax(A, mid + 1, fin); // Régner
Retourner(Min(min1, min2), Max(max1, max2)); // Combiner
FinSi
Fin
Problème de recherche
binaire(dichotomique)
• le problème de la recherche dichotomique consiste à rechercher un
élément dans un tableau de n éléments, procédant de la manière suivante :
1. on partitionne le tableau en deux parties égales
2. si l‘élément du milieu est celui qu’on cherche, on arrête la recherche
3. sinon, si l‘élément du milieu est supérieur à celui qu’on cherche, alors,
comme le tableau est déjà trié, ce dernier ne peut être que dans la partie
gauche de l’élément du milieu
4. autrement l‘élément du milieu est inférieur supérieur à celui qu’on cherche.
Dans ce cas, ce dernier ne peut être que dans la partie droite de l’élément
du milieu.

• Le cas de base est celui où l’espace de recherche est nul.


Diviser pour régner
Algorithme de recherche par dichotomie

• Recherche un nombre entier dans un tableau préalablement trié par


ordre croissant (et sans doublons).
Principe
• savoir où se trouve le nombre 9 dans le tableau suivant :

indice 1 2 3 4 5 6 7 8 9
valeur 0 2 4 5 6 9 14 20 30

• Une méthode de bourrin consiste à passer tous les nombres du


tableau en revue, de gauche à droite. Dans cet exemple, il faudra donc
tester les 6 premiers nombres pour savoir que le 6ème nombre est un
9 ! Comme je vous l'ai dit, 6 tests, c'est une méthode de bourrin
Diviser pour régner
• On propose de le faire en 3 étapes seulement avec la méthode par
dichotomie, signifie «couper en deux».
1. le nombre du «milieu», c'est-à-dire le numéro 5 (opération : (1+9)/2=5
2. Comme 6 est plus petit que 9, on recommence mais seulement avec la partie
supérieure du tableau car comme le tableau est trié, il ne peut y avoir de 9
avant ! indice 6 7 8 9
valeur 9 14 20 30

3. On prend à nouveau le nombre du «milieu». L'opération (6+9)/2 donne 7,5 !


Donc nous allons considérer que le nombre du «milieu» est le n°7 (je sais, ce
n'est pas mathématiquement rigoureux, mais c'est bien pour cela que j'utilise
des guillemets depuis le début).

4. Le nombre obtenu (15) est plus grand que 9, on recommence donc avec la
partie inférieure du tableau. 6 7
9 14

5. Le nombre du «milieu» est le n°6 (logique) et il vaut 9. C'est gagné ! Notre


résultat est que «le 9 est à la 6ème case».
Question: Quel est l'avantage de la
recherche dichotomique?

• Avec cet algorithme, je n'ai testé que la 5ème, la 7ème


et la 6ème valeur du tableau, soit seulement 3 tests au
lieu de 6 pour la méthode «bourrin» (on parle de force
brute). Et cet écart peut très vite s'accroître notamment
pour rechercher de grandes valeurs dans un grand
tableau.
Question: Quel est l'avantage de la
recherche dichotomique?
• Lors de la recherche dans un tableau de 1024 éléments:
- le pire des cas pour la recherche séquentielle peut
entraîner 1024 exécutions de la boucle.
- le pire des cas pour la recherche dichotomique peut
entraîner 10 exécutions de la boucle.

• recherche tableau de 1 048 576 éléments:


- le pire des cas pour recherche séquentielle
1 048 576 exécutions de la boucle.
- le pire des cas : recherche dichotomique 20
exécutions de la boucle.
Dicho.c
#include <stdio.h>
main() { /* Déclarations */
int A[50]; /* tableau donné */ int VAL; /* valeur à rechercher */
int POS; /* position de la valeur */ int N; /* dimension */ int I; /* indice courant */

int INF, MIL, SUP; /* limites du champ de recherche */


/* Saisie des données */ printf("Dimension du tableau (max.50) : "); scanf("%d", &N );
for (I=0; I<N; I++) { printf("Elément %d : ", I); scanf("%d", &A[I]); } printf("Elément à
rechercher : "); scanf("%d", &VAL );
/* Affichage du tableau */ printf("Tableau donné : \n");
for (I=0; I<N; I++) printf("%d ", A[I]); printf("\n");
/* Initialisation des limites du domaine de recherche */
INF=0; SUP=N-1; /* Recherche de la position de la valeur */
POS=-1; while ((INF<=SUP) && (POS==-1)) {
MIL=(SUP+INF)/2;
if (VAL < A[MIL]) SUP=MIL-1;
else if (VAL > A[MIL]) INF=MIL+1;
else POS=MIL; } /* Edition du résultat */
if (POS==-1) printf("La valeur recherchée ne se trouve pas " "dans le tableau.\n");
else printf("La valeur %d se trouve à la position %d. \n", VAL, POS); return 0; }

Vous aimerez peut-être aussi