Cours 2 Algorithmique IN102-02 Michel Mauny ENSTA Prénom.Nom@ensta.fr 1 Tris Retour sur fusion et quicksort Complexité minimale Efficacité 2 Récursivité 3 Structures de données Tableaux Listes Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 1 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 2 / 38 Complexité d un algorithme Problème du tri But : mesurer l efficacité intrinsèque en fonction de la taille des données à traiter dénombrement d opérations élémentaires représentatives mesure asymptotique complexité dans le cas le pire ou en moyenne complexité temporelle ou spatiale Algorithme Cas le pire En moyenne Tri par insertion Tri à bulle Θ(n 2 ) Θ(n 2 ) Tri rapide Θ(n 2 ) Θ(n log(n)) Tri par fusion Θ(n log(n)) Θ(n log(n)) Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 3 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 4 / 38 Tri fusion Calcul de la complexité T (n) = D(n) + 2 T (n/2) + R(n) 1 D(n) : division du problème en sous-problèmes. Fusion : D(n) = Θ(1). 2 C(n) : conquête, c est-à-dire le traitement des sous-problèmes, chacun de taille n/2. Fusion : C(n) = 2 T (n/2). 3 R(n) : recombinaison des résultats du traitement des sous-problèmes. Fusion : R(n) = Θ(n). T (n) = { Θ(1) si n = 1 2 T (n/2) + Θ(n) sinon Tri rapide (quicksort) Tri récursif basé sur un partitionnement Tri rapide void trirapide(int tab, int p,r) { if (p<r) { int q = partitionner(tab, p, r); trirapide(tab, p, q 1); trirapide(tab, q+1, r); Complexité en moyenne : C n = Θ(n log(n)) (voir poly) Complexité dans le cas le pire : C n = Θ(n 2 ) Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 5 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 6 / 38 Tri rapide (quicksort) Tri : peut-on mieux faire? Partitionnement int partitionner(int tab, int p, int r) { int pivot = tab[p]; int q = p; for (int i = p+1; i r; i = i+1) if (tab[i] < pivot) { q = q+1; échanger tab[q] et tab[i]; échanger tab[q] et tab[p]; return q; Tout algorithme de tri nécessite au moins Θ(n log(n)) comparaisons. Si on ne fait aucune hypothèse supplémentaire sur les données à trier Représentation de tout algorithme par un arbre de décision Complexité en moyenne : C n = Θ(n log(n)) (voir poly) Complexité dans le cas le pire : C n = Θ(n 2 ) Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 7 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 8 / 38
Arbre de décision pour le tri de 3 éléments Nombre de «feuilles» nombre de permutations à n éléments = n! Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 9 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 10 / 38 Nombre de comparaisons pour un tri : longeur de la «branche» correspondante Hauteur de l arbre : nombre maximum de comparaisons Arbre de hauteur h nombre maximum de feuilles=2 h 2 h nombre de feuilles n! h log(n!) > log (( n e ) n ) = n log(n) n log(e) Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 11 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 12 / 38 Qu est-ce qu un algorithme efficace? Sans hypothèse sur les données à trier, tout algorithme de tri nécessite au moins Θ(n log(n)) comparaisons. Et avec des hypothèses supplémentaires? Voir TD. Complexité en moyenne Complexité dans le pire des cas Facilité de mise en œuvre Efficacité en pratique Algorithmes hybrides Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 13 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 14 / 38 La légende dit : Lorsque le Dieu hindou Brahma créa le monde, il construisit en son centre le grand temple de Benares avec une tour de 64 disques d or pur. Ensuite il donna aux moines du temple l instruction de transférer les disques de cette tour selon certaines règles et expliqua qu un coup de tonnerre ferait disparaître le monde sitôt que ce devoir serait accompli. Migration de n anneaux de la tour 1 vers la tour 3 Interdiction de poser un anneau sur un plus petit Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 15 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 16 / 38
Migration de n anneaux de la tour 1 vers la tour 3 Déplacer récursivement n 1 anneaux de T 1 vers T 2...... puis déplacer 1 anneau de T 1 vers T 3 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 17 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 18 / 38... puis déplacer récursivement n 1 anneaux T 2 vers T 3... et voilà! Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 19 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 20 / 38 Animation (adaptée de http://www.cise.ufl.edu/ sahni/dsaaj/webpage/ towers of hanoi.html) Déplacer n anneaux de la tour i vers la tour j Version récursive void hanoi(int n, int i, int j) { int tourintermediaire = 6 (i+j); if (n > 0) { hanoi(n 1, i, tourintermediaire); cout «"Déplacer de " «i «" vers " «j «endl; hanoi(n 1, tourintermediaire, j); Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 21 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 22 / 38 Hanoi : complexité Nombre de déplacements nécessaires : C 0 = 0 C n = 1 + 2 C n 1 Donc C n = 2 n 1 = Θ(2 n ) Complexité exponentielle inhérente au problème Complexité spatiale : Θ(n) 50 disques, 1 disque par seconde, jour et nuit : 35 millions d années!! Structures de données Tableaux Listes chaînées ou variantes piles files listes circulaires listes doublement chaînées Structures de données complexes arbres graphes... Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 23 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 24 / 38
Stockage de données : tableaux Tableaux statiques : simples, efficaces, limités int t [10]; t[5] = 17; // t [12] = 4; // int n=4; int t [n ]; Stockage de données : tableaux Tableaux dynamiques : moins simples, aussi efficaces, moins limités int t; int n; cout «"Valeur de n? "; cin» n; t = new int[n]; t[0] = 17;... delete [] t; Note : new + delete en C++ malloc + free en C Mélange risqué! Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 25 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 26 / 38 Listes chaînées Les listes chaînées : plus délicates à programmer efficacité différente de celle des tableaux grande flexibilité Définition récursive : une liste est ou bien la liste vide Nil ou alors un premier élément x mis devant une liste xs : Cons(x, xs) Manipulation de listes Opérations de base : Création : Cons(1, Cons(2, Cons(3, Nil))) Cons(1, [1,2,3]) = [1,2,3,4] Premier élément : Tête([1,2,3,4]) = 1 Tout sauf le premier élément : Queue([1,2,3,4]) = [2,3,4] Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 27 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 28 / 38 Listes : implémentation C Listes : implémentation C Listes C typedef struct cell { int val; struct cell suiv; cellule; typedef cellule liste; Construction liste cons(int v, liste vs) { liste nouv = new liste; nouv val = v; nouv suiv = vs; return nouv; Graphiquement : Extraction du premier élément Extraction du reste Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 29 / 38 int tete(liste vs) { erreur("tete(null)"); return vs val; liste queue(liste vs) { erreur("queue(null)"); return vs suiv; Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 30 / 38 Parcours Recherche d un élément Itératif void printlisteiter(liste vs) { liste p = vs; while (p NULL) { cout «p val «" "; p = p suiv; cout «endl; Récursif void printlisterec(liste vs) { cout «endl; { cout «vs val «" "; printlisterec(queue(vs)); Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 31 / 38 Recherche de v dans vs, itérativement bool chercheiter(int v, liste vs) { liste p = vs; while (p NULL) { if (p val == v) return true; p = p suiv; return false; Recherche de v dans vs, récursivement bool chercherec(int v, liste vs) { return false; return ( (v == tete(vs)) chercherec(v, queue(vs)) ); Complexité : Θ(n) Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 32 / 38
Insertion dans une liste Insertion dans une liste Insertion à la position k, récursivement, avec recopie liste ajoute(int v, int k, liste vs) { if (k == 0) return cons(v, vs); return cons(tete(vs), ajoute(v, k 1, queue(vs))); Ici, on recopie un segment initial de la liste : fabrication d une nouvelle liste partage d un segment terminal avec la liste originale Risque d échec? Insertion à la position k, itérativement, sans recopie void insere(int v, int k, liste pl) { // NB: la liste est passée par référence liste pc = pl; liste nouv; if (k == 0) pl = cons(v, pl); { while (k > 1 ) if (pc == NULL) erreur("insere: liste trop courte"); { pc = pc suiv; k = k 1; nouv = new cellule; nouv val = v; nouv suiv = pc suiv; pc suiv = nouv; Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 33 / 38 Ici, on partage la liste. Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 34 / 38 Suppression d une liste Libération de la mémoire occuppée par une liste void libere(liste vs) { if (vs NULL) { libere(vs suiv); delete [] vs; Inversion d une liste Inversion avec recopie : Inversion naïve liste inversionnaive(liste vs) { if (vs = NULL) return vs; return concatene(inversionnaive(queue(vs)), cons(tete(vs), NULL)); Inversion avec accumulateur liste inversion(liste vs, liste accu) { return accu; return inversion(queue(vs), cons(tete(vs), accu)); Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 35 / 38 Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 36 / 38 Variantes Listes doublement chaînées Listes circulaires Piles Files Michel Mauny (ENSTA) Algorithmique IN102-02 Prénom.Nom@ensta.fr 37 / 38