Télécharger la présentation
Publié parPauline Berthet Modifié depuis plus de 10 années
1
INF1101 Algorithmes et structures de données
Cours 5 La récursivité INF1101 Algorithmes et structures de données
2
INF1101 Algorithmes et structures de données
Plan du cours I. Notions de base II. Exemple simple (imp. de nombres) III. Limitations (suite de Fibonacci) Arbres (introduction) Autres exemples VI. Sous-séquence de somme maximale VII. Backtracking INF1101 Algorithmes et structures de données
3
INF1101 Algorithmes et structures de données
I - Notions de base Définition: Un objet est récursif s'il est défini à partir de lui-même. Une fonction est récursive si elle peut s'appeler elle-même de façon directe ou indirecte Idée générale: Pour bien comprendre la notion de récursivité, il faut connaître le concept des piles. Les piles sont des structures qui peuvent contenir plusieurs éléments. Toutefois, seul le dernier élément ajouté à la pile est accessible. Une analogie peut être faite avec les piles d'assiettes. Seule l'assiette du dessus est immédiatement accessible. De la même façon, lorsqu'une assiette est ajoutée, celle-ci est nécessairement déposée sur le dessus de la pile. INF1101 Algorithmes et structures de données
4
INF1101 Algorithmes et structures de données
La pile d’assiette Assiette disponible Lorsqu'une fonction est appelée, ses paramètres formels, l'adresse de retour et les variables locales (automatiques) de la fonction appelée sont déposés sur la pile. Toutes ces données sont retirées de la pile à la fin de l'exécution de la fonction. Conséquemment, lors d'appels récursifs, les variables de la fonction sont empilées en cascade. INF1101 Algorithmes et structures de données
5
INF1101 Algorithmes et structures de données
Récursivité (suite) Idée générale : La réalisation d'une tâche par un algorithme récursif repose sur deux éléments: La résolution partielle du problème d'une façon simple. La réalisation du reste de la tâche étant déléguée aux appels récursifs successifs. La détermination d'une condition d'arrêt qui permet d'arrêter la cascade d'appels récursifs. Résoudre le problème au moment où la condition d'arrêt est détectée correspond en général à résoudre un cas trivial de celui-ci. INF1101 Algorithmes et structures de données
6
Champs d'application des algorithmes récursifs
Structures de données définies récursivement (listes, arbres, graphes, etc.) Équations de récurrence (algébriques, vectorielles, booléennes, formelles, ensemblistes, etc.); Rétroaction ("backtracking"). … INF1101 Algorithmes et structures de données
7
Avantages et inconvénients
Formulation compacte, claire et élégante. Maîtrise des problèmes dont la nature même est récursive. Désavantages: Possibilité de grande occupation de la mémoire. Temps d'exécution peut être plus long. Estimation difficile de la profondeur maximale de la récursivité. INF1101 Algorithmes et structures de données
8
Fonction récursive factorielle
Soit la fonction Factorielle, effectuant l’opération n! long Factorielle(long N) { if (N==0) return 1; else return (Factorielle(N-1)*N); } Voir main1.cpp INF1101 Algorithmes et structures de données
9
Exécution de Factorielle
int Reponse = Factorielle (4); Factorielle (1) Factorielle (0) Factorielle (2) Factorielle (3) Factorielle (4) INF1101 Algorithmes et structures de données
10
INF1101 Algorithmes et structures de données
Récursivité (suite) Quatre règles régissent la récursivité: Présence de cas de base pouvant être résolu sans récursivité Toujours progresser vers le cas de base à chaque appel récursif « Ayez la Foi »: avoir confiance que les appels récursifs progressent réellement vers le cas de base. Intérêt composé: Ne jamais dédoubler du travail dans deux appels récursifs différents. INF1101 Algorithmes et structures de données
11
II – Exemple simple : impression de nombres
Idée générale: Fonction qui donne l’impression d’un nombre non-négatif N sous forme décimale. Par exemple pour le nombre 248, il est nécessaire d’imprimer 2, puis 4 et finalement 8. La récursivité offre une méthode efficace et simple de programmer une tel méthode. En utilisant l’opérateur %, le dernier chiffre peut être imprimer, suivi des autres par n/10. INF1101 Algorithmes et structures de données
12
Fonction récursive pour N en forme décimale
void printDecimal( int n ) { if ( n >= 10 ) printDecimal( n/10 ); cout.put( ‘0’ + n%10 ); } INF1101 Algorithmes et structures de données
13
Fonction récursive pour N pour n’importe quelle base
void printInt( int n , int base) { static string DIGIT_TABLE = “ abcdef”; if ( n >= base ) printInt( n/base, base ); cout << DIGIT_TABLE[ n%base ]; } INF1101 Algorithmes et structures de données
14
Exemple simple : impression de nombres
Note : Une erreur se produit lors de l’appel à la fonction récursive lorsque en base 1, puisque les deux paramètres sont identiques. Ceci entraîne une série infinie d’appel à la fonction récursive. En testant et en validant la base avant de faire appel à la fonction récursive, ceci éviterait de faire appel à la fonction récursive en base 1. Le test n’est effectué qu’une seule fois puisque ce test sera valide pour tous les appels subséquents. Ceci permettra ainsi de posséder une fonction plus robuste. INF1101 Algorithmes et structures de données
15
Fonction récursive pour N pour n’importe quelle base
void printIntRec( int n , int base) { static string DIGIT_TABLE = “ abcdef”; if ( n >= base ) printIntRec( n/base, base ); cout << DIGIT_TABLE[ n%base ]; } void printInt(int n, int base) if(base <=1 || base > MAX_BASE) cerr << “Impression impossible en base “ << base <<endl; else if(n < 0) cout << ”-”; n = -n; printIntRec(n,base) INF1101 Algorithmes et structures de données
16
III – Limitations: Fonction récursive Fibonnacci
Soit la fonction Fibo (Fibonacci), effectuant l’opération f(n) = f(n-1) + f(n-2) long fibo (long valeur) { if (valeur <= 1) return valeur; else return(fibo(valeur-1) + fibo(valeur-2)); } // voir main1.cpp INF1101 Algorithmes et structures de données
17
Exécution de Fibonnacci
Soit les appels effecutés pour fibo(4) : fibo(4) fibo(3) fibo(2) fibo(1) fibo(0) fibo(2) fibo(1) fibo(1) fibo(0) INF1101 Algorithmes et structures de données
18
INF1101 Algorithmes et structures de données
Danger de Fibonnacci Note : Cette fonction récursive effectue plusieurs appels au même calcul. Par exemple pour déterminer f(n), on calcule d’abord f(n-1), puis au retour de l’appel, f(n-2) est calculé. Or, dans le calcul de f(n-1), f(n-2) est déjà calculé! Ce problème s’aggrave en descendant l’arborescence, puisque f(n-3) sera appelé à 3 reprises : chaque appel récursif entraînera de plus en plus d’appel redondant. Règle 4: Ne jamais dupliquer le travail par la résolution d’une même instance d’un problème dans plusieurs appels récursifs. INF1101 Algorithmes et structures de données
19
IV – Arbres: introduction
Définition : Un arbre est une structure fondamentale et omniprésente en informatique. La plupart des systèmes d’exploitation organisent leur fichiers dans une structure arborescente. INF1101 Algorithmes et structures de données
20
INF1101 Algorithmes et structures de données
Définitions Arbre : Un arbre est une structure composée de noeuds et d'arêtes pour laquelle il n'existe qu'un seul chemin pour passer d'un noeud à un autre. Racine : Premier noeud d'un arbre (noeud sans père). Feuille : Noeud n'ayant pas de fils. INF1101 Algorithmes et structures de données
21
Représentation d’un arbre
C D E F G INF1101 Algorithmes et structures de données
22
INF1101 Algorithmes et structures de données
V – Autres exemples Exemples démontrant des méthodes qui peuvent être implémentés non récursivement et des avantages de la récursivité. Ex : recherche dichotomique (binaire) Ex : Dessin d’une règle INF1101 Algorithmes et structures de données
23
Recherche binaire (dichotomique)
Idée générale : La recherche d’une valeur particulière dans un tableau ordonné se fait par divisions successives du tableau en deux parties. Le fait que le tableau soit ordonné permet de déterminer rapidement la moitié dans laquelle se trouve l’élément recherché. i = début –1 j = fin +1 Tant que i et j ne se rencontrent pas Trouver le milieu du sous-tableau courant Si la valeur cherchée < tableau [milieu] j = milieu Si la valeur cherchée = tableau [milieu] Retourne milieu Si la valeur cherchée > tableau [milieu] i = milieu Retourne -1 INF1101 Algorithmes et structures de données
24
Recherche dichotomique : Exemple d’exécution
Valeur Cherchée = 17; Début = 0, Fin = 10 j milieu i -1 11 1 2 3 4 5 6 7 8 9 10 -2 11 15 17 20 21 30 39 Valeur cherchée < tableau[milieu] Valeur cherchée = tableau[milieu] Valeur cherchée > tableau[milieu] La valeur cherchée est dans la partie gauche du tableau La valeur cherchée est dans la partie droite du tableau On retourne milieu INF1101 Algorithmes et structures de données
25
Recherche binaire : programmation non-récursive
int RechercheBinaire( int * Tableau, int Debut, int Fin, int ValeurCherchee) { int i = Debut - 1; int j = Fin + 1; while ( i+1 != j) int milieu = (i+j)/2; if ( ValeurCherchee < Tableau[milieu]) j = milieu; if (ValeurCherchee == Tableau[milieu]) return milieu; if (ValeurCherchee > Tableau[milieu]) i = milieu; } return -1; INF1101 Algorithmes et structures de données
26
Fonction récursive pour la recherche binaire
template <class Comparable> int binarySearch(const vector<Comparable>& a, const Comparable& x) { return binarySearch(a, x, 0, a.size()-1); } int binarySearch(const vector<Comparable>& a, const Comparable& x, int low, int high) { if(low > high) return NOT_FOUND; int mid = (low+ high)/2; if( a[mid] < x) return binarySearch(a, x, mid+1, high) else if( x < a[mid]) return binarySearch(a, x, low, mid-1); else return mid; } INF1101 Algorithmes et structures de données
27
VI – Sous-séquence de somme maximale
Définition: La technique de diviser et conquérir (divide-and-conquer) est une méthode de résolution de problèmes basée sur la récursivité: Diviser en plusieurs sous problèmes résolus récursivement. Conquérir, où la solution du problème original est composé de la solution des sous-problèmes. INF1101 Algorithmes et structures de données
28
Sous-séquence de somme maximale
Définition: Étant donnée des entiers (possiblement négatifs) A1, A2,. . . , AN, trouver et identifier la séquence correspondante à la valeur maximale de La somme de la sous-séquence est nulle si tous les entiers sont négatifs. INF1101 Algorithmes et structures de données
29
Sous-séquence de somme maximale
Exemple: Soit les entiers suivants: {4, -3, 5, -2, -1, 2, 6, -2}. On divise cet ensemble en deux: Première moitié Deuxième moitié Valeurs Sommes 4* * 5 Sommes à partir du centre (* indique la somme maximale pour chaque moitié) INF1101 Algorithmes et structures de données
30
Sous-séquence de somme maximale
Cas possible: Cas 1: La séquence est situé dans la première moitié. Cas 2: La séquence est situé dans la deuxième moitié. Cas 3: La séquence commence dans la première moitié et se termine dans la deuxième moitié. INF1101 Algorithmes et structures de données
31
Algorithme de la sous-séquence de somme maximale
Sommaire de l’algorithme: Déterminer récursivement la sous-séquence de somme maximale uniquement dans la première moitié. Déterminer récursivement la sous-séquence de somme maximale uniquement dans la deuxième moitié. Avec deux boucles consécutives, déterminer la sous-séquence de somme maximale qui débute dans la première moitié et qui termine dans la deuxième moitié. Choisir la somme la plus élevée des trois. INF1101 Algorithmes et structures de données
32
Fonction récursive pour la sous-séquence de somme maximale
template<class Comparable> Comparable maxSubSum( const vector<Comparable> & a, int left, int right) { Comparable maxLeftBorderSum = 0, maxRightBorderSum = 0; Comparable leftBorderSum = 0, rightBorderSum = 0; int center = ( left + right ) / 2; if(left == right) return a[left] > 0 ? a[left] : 0; Comparable maxLeftSum = maxSubSum(a,left,center); Comparable maxRightSum = maxSubSum(a,center+1,right); for(int i=center; i>=left ;i--) leftBorderSum += a[i]; if( leftBorderSum > maxLeftBorderSum ) maxLeftBorderSum = leftBorderSum; } . . . INF1101 Algorithmes et structures de données
33
Fonction récursive pour la sous-séquence de somme maximale
for(int j=center+1; j<=right; j++) { rightBorderSum += a[j]; if( rightBorderSum > maxRightBorderSum ) maxRightBorderSum = rightBorderSum; } return max3( maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum ); template <class Comparable> Comparable maxSubsequenceSum( const vector<Comparable> & a) return a.size() > 0 ? maxSubSum( a, 0, a.size() – 1) : 0; INF1101 Algorithmes et structures de données
34
Sous-séquence de somme maximale (Exemple)
3 2 -5 -1 max(0,2,0+1) max(3,2,3+2) 3 2 1 3 2 -5 -1 2 max(0,2,0+2) max(0,0,0+0) 3 2 max(2,0,2+0) 2 3 2 -5 -1 -1 2 2 2 2 -5 -1 -1 2 INF1101 Algorithmes et structures de données dstj
35
Sous-séquence de somme maximale (Exemple)
3 2 -5 -1 max(0,2,0+1) max(3,2,3+2) 3 2 1 3 2 -5 -1 2 max(0,2,0+2) max(0,0,0+0) 3 2 max(2,0,2+0) 2 3 2 -5 -1 -1 2 2 2 2 -5 -1 -1 2 INF1101 Algorithmes et structures de données dstj
36
INF1101 Algorithmes et structures de données
VII – Backtracking Problématique : Pour résoudre certains types de problèmes, il peut être souvent utile de procéder à l'exploration systématique des différentes solutions possibles. Il est donc essentiel de développer des techniques algorithmiques qui permettent une telle exploration. Idée générale pour le calcul du trajet du robot: Il s'agit de concevoir un algorithme qui permet à un robot de trouver un chemin menant à la sortie d'un labyrinthe. Pour ce faire le robot explore systématiquement les quatre directions vers lesquelles il peut se déplacer. De plus, afin d'éviter que le robot ne se retrouve sur des cases déjà explorées chacune de celles-ci seront marquées à mesure qu'elles sont visitées. C'est de façon récursive que les cases faisant partie du chemin seront identifiées. INF1101 Algorithmes et structures de données
37
Algorithme du retour-arrière
INF1101 Algorithmes et structures de données
38
INF1101 Algorithmes et structures de données
Plan du site INF1101 Algorithmes et structures de données
39
Programmation du trajet d’un robot mobile
#define Y 9 /* Les dimensions du labyrinthe */ #define X 7 char laby[X][Y] = { {'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'}, {'X', ' ', ' ', 'X', ' ', ' ', ' ', ' ', 'X'}, {'X', ' ', 'X', 'X', ' ', 'X', 'X', ' ', 'X'}, {'X', ' ', ' ', ' ', ' ', ' ', 'X', 'X', 'X'}, {'X', 'X', 'X', ' ', 'X', ' ', 'X', ' ', 'X'}, {'X', 's', ' ', ' ', ' ', ' ', ' ', ' ', 'X'}, {'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'}}; main() { int i, j; bool Parcours(int x, int y); if (Parcours(1,1)) { // Imprimer le résultat de la recherche for (i = 0; i < X; i++, cout<<endl) for (j = 0; j < Y; j++) cout<<laby[i][j]; } else cout<<"Il n'existe aucun chemin.\n"; } INF1101 Algorithmes et structures de données
40
Programmation du trajet d’un robot mobile (suite)
bool Parcours(int x, int y) { if (laby[x][y] == 's') { /* on a trouvé la sortie */ laby[x][y] = 'S'; return true; } else if (laby[x][y] == ' ') { /* la case est libre */ laby[x][y] = 'o'; /* marquer la case visitée */ if (Parcours(x-1, y)) /* Parcours nord */ return(true); else if (Parcours(x, y+1)) /* Parcours est */ else if (Parcours(x+1, y)) /* Parcours sud */ else if (Parcours(x, y-1)) /* Parcours ouest */ else laby[x][y] = '-'; /* on laisse une marque */ } return false; INF1101 Algorithmes et structures de données
41
Retour en arrière - exercice
1 2 3 4 5 6 7 8 9 A B S C D E F G H I J Quel est le chemin parcouru? Combien de retours en arrière? Utiliser des appels récursifs et l’ordre d’appel suivant: Nord Sud Est Ouest INF1101 Algorithmes et structures de données
42
Retour en arrière - réponse
1 2 3 4 5 6 7 8 9 A B C D E F G H I J o X X o S o o X X o o o X o o o o o o o o o E o o o X o o o o o o o o o o o o INF1101 Algorithmes et structures de données
Présentations similaires
© 2024 SlidePlayer.fr Inc.
All rights reserved.