Analyse d’algorithmes Structures de données IFT-2000 Abder Alikacem Analyse d’algorithmes Édition Septembre 2009 Département d’informatique et de génie logiciel
Plan Notions d’analyses d’algorithmes Introduction Efficacité des algorithmes Analyse d’algorithmes Notation Ω (big-omega) Notation Θ Notation o (little-oh) Notation O (big-oh) Baromètres Comparaison entre les classes de complexité Règles de simplification Espace mémoire Compromis espace/temps
Algorithmique Conception de méthodes pour la résolution de problèmes Description des données Description des méthodes Preuve de bon fonctionnement Complexité des méthodes Efficacité : temps de calcul, espace nécessaire, ... Complexité intrinsèque, optimalité Réalisation - implémentation Organisation des objets Opérations élémentaires
Efficacité des algorithmes Habituellement on mesure l’efficacité d’un algorithme (correct) par sa vitesse. C’est-à-dire la durée que met l’algorithme à produire ses résultats. On veut savoir combien de temps cela prend de résoudre une instance I du problème P: données(I) + Algorithme(P) ==> résultat(I). temps mis ?
Efficacité des algorithmes Ici, on s’intéresse au temps de calcul pour résoudre I: en microsecondes, cycles d’horloge, nombre d’instructions exécutées, etc.. On prend en compte la taille des données(I): en bits, entiers, nombres d’enregistrements... Parfois, on considère aussi la mémoire nécessaire.
Efficacité des algorithmes Approche empirique: mesure de performances. essayer l’algorithme sur différents jeux de données bien choisis. Avantages: résultats réalistes. Inconvénients: coûteux et long dépend du processeur, de l’OS et de la charge dépend de l’implémentation pas toujours généralisable.
Efficacité des algorithmes Approche algorithmique: estimer le nombre de pas de l’algorithme en fonction de la taille des données. Avantages: résultats généraux: on ne dépend pas du processeur, de l’OS ou de l’implémentation Estimation rapide et peu coûteux évite de se fourvoyer. Inconvénients: prédictions approximatives.
Efficacité des algorithmes Les approches empiriques et algorithmique sont complémentaires. Utiliser l’approche algorithmique pour déterminer comment le temps de calcul varie avec la taille des données. Utiliser l’approche empirique afin d’obtenir un temps d’exécution précis. (associé à une implantation, à un système d'exploitation , et à un type de compilateur particuliers) Avantage: estimer le temps que prend l’algorithme pour des tailles de données plus grands que ceux des tests effectifs.
Efficacité des algorithmes Comment choisir parmi les différentes approches pour résoudre un problème? Exemple: Liste chaînée ou tableau? 2 objectifs à atteindre: Concevoir un algorithme facile à comprendre, coder et déboguer. Concevoir un algorithme qui utilise de façon efficace les ressources de l’ordinateur.
Efficacité des algorithmes Objectif (1): concerne le génie logiciel Objectif (2): Algorithmes et structures de données. Lorsque l’objectif (2) est important, comment peut-on mesurer l’efficacité d’un algorithme?
Analyse d’algorithme Analyse empirique: exécuter le programme Analyse assymptotique d’algorithmes Ressources critiques: temps, espace mémoire,... Facteurs affectant le temps d’exécution: machine, language, programmeur, compilateur, algorithme et structure de données. Le temps d’exécution dépend du volume de l’entrée.
Analyse d’un algorithme Analyse détaillée : Avantage :Très précise Désavantage : Long à calculer Solution: Analyse asymptotique du comportement de l’algorithme lorsque le(s) paramètre(s) de l’algorithme tendent vers l’infini: Avantage: Parfois facile à calculer Désavantage: Parfois moins précis Mesure indépendante de l'implémentation : Temps d'exécution d'un algorithme = fonction(taille du problème)
Notation W (big-omega) Détermine une borne inférieure Définition formelle : f(n) = W (g(n)) s’il existe deux constantes positives n0 et c tel que cg(n) <= f(n) pour tout n>n0
Notation W (big-omega) Étant donné deux fonctions f(n) et g(n), f(n) = W(g(n)) ssi: b > 0, n, f(n) / g(n) >= b Limn®¥ f(n) / g(n) Î Â+ È ¥ Exemple f(n) = 3n2+6n+4 et g(n) = n+3 limn®¥ f(n) / g(n) = ¥ \ 3n2+6n+4 est W(n+3) 3n2+6n+4 est W(n) On dit « f(g) est en oméga g(n) »
Notation Q Relation d’équivalence, correspond à la complexité exacte Définition formelle : f(n) = Q (g(n)) s’il existe trois constantes positives n0,c1 et c2 tel que c1g(n) <= f(n) <= c2g(n) pour tout n>n0
Notation Q Étant donné deux fonctions f(n) et g(n), f(n) = Q (g(n)) ssi: a,b > 0, n, a <= f(n) / g(n) <= b Limn®¥ f(n) / g(n) Î Â+ Exemple: f(n) = 3n2+6n+4 et g(n) = n2+3 limn®¥ f(n) / g(n) = ¥ \ 3n2+6n+4 est Q (n2+3) 3n2+6n+4 est Q (n2) Autre propriété: f(n) = Q(g(n)) ssi f(n) = O(g(n)) et f(n) = W(g(n))
Notation o (little-oh) Détermine une borne supérieure stricte Définition formelle : f(n) = O(g(n)) s’il existe deux constantes positives n0 et c tel que f(n) < cg(n) pour tout n>n0
Notation O (big-oh) Détermine une borne supérieure Définition formelle : f(n) = O(g(n)) s’il existe deux constantes positives n0 et c tel que f(n) <= cg(n) pour tout n>n0
Notation O (big-oh) Cherche une fonction simple qui décrit le comportement de l’algorithme dans le pire cas. Exemples : O(n), O(n2),O(log n) L’analyse est simplifié selon les règles suivantes: Les constantes sont éliminées Seul le monôme avec le degré le plus élevé est conservé Exemple: f(n) = 3n2+5n+4 est O(n2)
Exemple void triSelection(int tab[MAXTAB], int n) { int PositionMin, temp, i, j; for (i = n-1; i > 0; i--) PositionMin = i; for (j = 0; j < i; j++) if (tab[j] < tab[PositionMin]) PositionMin = j; } temp = tab[i]; tab[i] = tab[PositionMin]; tab[PositionMin] = temp; Exemple
Baromètres // i * a //b2 void triSelection(int tab[MAXTAB], int n) { int PositionMin, temp, i, j; for (i = n-1; i > 0; i--) PositionMin = i; //b1 for (j = 0; j < i; j++) if (tab[j] < tab[PositionMin]) PositionMin = j; } temp = tab[i]; tab[i] = tab[PositionMin]; tab[PositionMin] = temp; Qu’est-ce qu’un baromètre? Comment ça fonctionne? Comment choisir le choisir ? // i * a //b2
Baromètres Qu’est-ce qu’un baromètre? C’est une instruction qui s’exécute au moins autant de fois que chacune des autres. Comment ça fonctionne?: Sélectionne un ensemble d’instructions significatives B={b1…bk} On détermine le nombre d’exécution de chacun des baromètres dans B Temps total est la somme du nombres de d’exécution de chaque baromètre Comment choisir l’ensemble B? Mettre systématiquement certains types d’instructions(boucles,conditions,etc.) Estimer leurs nombres d’exécution maximal et minimal selon que l’on calcul le meilleur ou le pire cas. Simplifier B en gardant seulement les instructions qui s’exécutent le plus souvent.
Baromètres Boucle tant que (pour): chaque fois qu’on passe dessus, il s’exécute le nombre de fois que la condition est vraie+1 (quand la condition est fausse). Bloc interne d’un while: chaque fois qu’on passe sur le while, on passe sur ses instructions autant de fois que la condition est vraie. Bloc interne d’un si: chaque fois qu’on passe sur le si et que la condition est vraie, on passe sur ses instructions Autres instructions: chaque fois qu’on passe dessus, elles s’exécutent une fois.
Exemple (suite) for (i = n-1; i>0; i--) { Coût b1 /* PositionMin = i */ Coût i*a; /* pour exécuter la boucle interne*/ Coût b2; /* pour l'échange de tab[i] et tab[PositionMin] */ }
Exemple (suite) Exemple (suite) b = b1 + b2
f(n) = an2 + bn + c a = 0.0001724, b = 0.0004, c = 0.1 n f(n) an2 % du n2 125 2.8 2.7 94.7 250 11.0 10.8 98.2 500 43.4 43.1 99.3 1000 172.9 172.4 99.7 2000 690.5 689.6 99.9
Retour sur la notation O( ) (big-oh) Évaluer le coût d'un algorithme Rechercher la fonction g(n) qui est la plus proche au dessus ou égale à f(n). Nous dirons alors f(n) est O(g(n)).
Notation O(g(n)) (big-oh) Pour un nombre n assez grand f(n) an2 Temps d'exécution d'un algorithme A est f(n) = an2 + bn + c g(n) = n2 On écrit : A est O(n2) On lit : l'algorithme A tourne en O(n2) Classes : O(1), O(n), O(n2), O(n3), O(log n), …
Notation O(g(n)) (big-oh) Classes : O(1), O(n), O(n2), O(n3), O(log n), … 2n n 2 n f(n) log n 1 n
Comparaison entre les classes x VS log(x)
Comparaison entre les classes 50 45 40 35 30 x 25 x log( x ) 20 15 10 5 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 x VS x log(x)
Comparaison entre les classes x2 VS x log(x)
Comparaison entre les classes x VS x2 VS x3
Comparaison entre les classes O(1) < O(log(n)) < O(n) < O(nlog(n)) < O(n2) < O(n3) < O(2n) < O(10n)
Comparaison entres les classes O(1). Constant ou borné parfait. O(log(n)). Logarithmique excellent O(n). Linéaire très bon O(n log(n) ) bon. O(na) polynomial acceptable O(an) exponentiel inacceptable O(n!) factoriel beurk.
Augmenter la puissance du PC ?
Augmenter la puissance du PC
Meilleur algorithme ou ordinateur? On suppose que l’ordinateur utilisé peut effectuer 106 opérations à la seconde
Exemple (suite et fin) b = b1 + b2 L’algorithme est en O(n2)
Exemple: chercher le minimum dans un tableau d’entiers int minimumOn2(int tableau[],int taille) { int i,j; for(i=0;i<taille;i++) j=0; while(j<taille && tableau[i]<=tableau[j] ) j++; } if(j == taille) return tableau[i]; return tableau[i]; int minimumOn(int tableau[],int taille) { int i,min = tableau[0]; for(i=0;i<taille;i++) if( tableau[i]<min) min = tableau[i]; } return min; Algorithme #1 Algorithme #2
Autres exemples O(n) Fusionner(x,y) n = taille(x)+taille(y) pour i=1:n …. v(i) = ….. ….. fin Ab=c O(n2) pour i=1:n s = 0; pour j=i:n s =s+A(i,j)*b(i) Fin c(i)=s fin Résoudre(P(n)) Si n = 1 ,...c’est fini Sinon A=Résoudre(P(1:n/2)) B=Résoudre(P(n/2+1:n)) Fusionner(A,B) O(nlog2n)
Simplifier les séries Sommes ou produits avec un nombre variable de termes. Série Formule Résultat Exemple O() constante k=1..N a aN 3+3+...+3(10 fois) N somme de logs k=1..N log(k) log(N!) log(2)+log(3)+..log(100) N log(N) arithmétique k=1..N k N(N+1)/2 1+2+...+10 N2 somme de carrés k=1..N k2 N(N+1)(2N+1)/6 1+22+32+..102 N3 somme de puissances k=1..N ka (a entier) 1+23+33+..103 (a=3) Na+1 de polynômes de degré a k=1..N Pa(k) k=1..N k(k+1)..(k+a) géométrique binaire k=0..N 2k 2N+1-1 1+2+4+..+128 2N géométrique entière k=0..N ak (a entier) (aN+1-1)/(a-1) 1+3+9+..+81 (a=3) factorielle k=1..N k N!2N (N/e)N 1*2*..*100 NN+0.5
Pire cas, meilleur cas et cas moyen Toutes les entrées d’une longueur donnée ne nécessitent pas le même temps d’exécution. Exemple. Recherche séquentielle dans un tableau de taille n: Commencer au début du tableau et considérer chaque élément jusqu’à ce que l’élément cherché soit trouvé. Meilleur cas: O(1) Pire cas: O(n) Cas moyen: O(n/2)
Règles de simplification 1. Toujours négliger les constantes multiplicatives: 2n2 = O(n2) 2. Négliger les termes d’ordre inférieur: 5n2 + 3n = O(n2) + O(n) = O(n2) 3. O(n2) x O(n3) = O(n5)
Règles de simplification f(n) O(g(n)) et g(n) O(h(n)), alors f(n) O(h(n)).
Règles de simplification f(n) O(kg(n)) où k >0 est une constante alors f(n) O(g(n)).
Règles de simplification f1(n) O(g1(n)) et f2(n) O(g2(n)), alors f1(n) + f2(n) O(max(g1(n), g2(n))). Exemple: n + nlog n O(nlog n)
Règles de simplification f1(n) O(g1(n)) et f2(n) O(g2(n)) alors f1(n)*f2(n) O(g1(n) * g2(n)) Exemple: (3n2 +3)(n2+5nlog n) O(n4)
Règles de simplification Si p(n) est un polynôme de degré k alors p(n) (nk)
pour toute constante k>0 Règles de simplification logk (n) O(n) pour toute constante k>0
logk(n) k logk−1(n) logk(n) logk(n) = O(n) cn ≤ 1 cn lim lim = cn n→∞ k(k − 1)logk−2(n) = n→∞ cn (règle de l’Hôpital) ... = k! lim = = cn n→∞
Exemples Exemple 1: a = b; Temps constant: (1). Exemple 2: somme = 0; for (i=1; i<=n; i++) somme += n; Temps: (n)
Exemples Exemple 3: somme = 0; for (i=1; i<=n; i++) for (j=1; j<=i; j++) somme++; Temps: (1) + O(n2) = O(n2) On peut montrer : (n2)
Exemples Exemple 4: somme = 0; for (j=1; j<=n; j++) for (i=1; i<=n; i++) somme++; for (k=0; k<n; k++) A[k] = k; Temps: (1) + (n2) + (n) = (n2)
Exemples Exemple 5: somme = 0; for (k=1; k<=n; k*=2) for (j=1; j<=n; j++) somme++; Temps: (nlog n)
Exemples lSum = 0; for( int i = 0; i < n; ++i ) lSum++; lSum = 0; for( int j = 0; j < n; ++j) lSum++;
Exemples lSum = 0; for( int i = 0; i < n; ++i ) lSum++; for( int j = 0; j < n; ++j) lSum++; lSum = 0; for( int i = 0; i < n; ++i ) for( int j = 0; j < n*n; ++j) lSum++;
Exemples lSum = 0; for( int i = 0; i < n; ++i ) for( int j = 0; j < i; ++j) lSum++; lSum = 0; for( int i = 0; i < n; ++i ) for( int j = 0; j < n*n; ++j) for( int k = 0; k < j; ++k) lSum++;
Exemples lSum = 0; for( int i = 1; i <= n; ++i ) for( int j = 1; j < i*i; ++j) if( j % i == 0) for( int k = 0; k < j; ++k) lSum++;
Analyse de problèmes Borne supérieure: Borne supérieur sur le meilleur algorithme connu. Borne inférieure: Borne inférieure sur tous les algorithmes possibles. Exemple: Trier un tableau de taille n nécessite O(n) étapes et peut se faire en O(nlog n) étapes.
Temps de calcul Exemple T(n) = O(n4). De combien le temps de calcul augmente-t-il si n est multiplié par 10 ? T(n) = O(n4) = cn4 T(10n) = c(10n)4 = 104 cn4 = 10 000 T(n)
Logarithmes logb(n) = k ⇔ bk = n logb(n) = loga(n) / loga(b) logb(n) = O(log(n)) (la base ne compte pas) Le log apparaît lorsque systématiquement, on découpe le problème en deux morceaux de même taille
Logarithmes et exposants log(xy) = log(x) + log(y) log(xa) = alog(x) a(b+c) = abac abc = (ab)c
Temps de calcul Exemple T(n) = O(n logn). De combien le temps de calcul augmente-t-il si n est multiplié par 10 ? T(n) = cn log(n) T(10n) = c(10n) log(10n) T(10n) = 10cn log(n) + 10cn log(10) T(10n) = 10T(n) + dn
Recherche binaire données initialement triées point milieu
Analyse par télescopage T(n) = T(n/2) + O(1) T(n/2) = T(n/4) + O(1) T(n/4) = T(n/8) + O(1) …. = …. T(2) = T(1) + O(1)
Analyse par télescopage T(n) = T(n/2) + O(1) T(n/2) = T(n/4) + O(1) T(n/4) = T(n/8) + O(1) …. = …. T(2) = T(1) + O(1)
Analyse par télescopage T(n) = T(n/2) + O(1) T(n/2) = T(n/4) + O(1) T(n/4) = T(n/8) + O(1) …. = …. T(2) = T(1) + O(1)
Analyse par télescopage T(n) = T(n/2) + O(1) T(n/2) = T(n/4) + O(1) T(n/4) = T(n/8) + O(1) …. = …. T(2) = T(1) + O(1)
Analyse par télescopage T(n) = T(n/2) + O(1) T(n/2) = T(n/4) + O(1) T(n/4) = T(n/8) + O(1) …. = …. T(2) = T(1) + O(1)
Analyse par télescopage T(n) = T(n/2) + O(1) T(n/2) = T(n/4) + O(1) T(n/4) = T(n/8) + O(1) …. = …. T(2) = T(1) + O(1) log n ⇒ T (n)= T (1) + O(1) = O(log n) i=1
Temps de calcul Exemple T(n) = O( log n). De combien le temps de calcul augmente-t-il s i n est multiplié par 1000 ? T(n) = c log(n) T(1000n) = c log(1000n) T(1000n) = c log(n) + c log(1000) T(1000n) = T(n) + 10c
Code source template <typename Comparable> int binarySearch( const vector<Comparable> & a, const Comparable & x ) { int low = 0; int high = a.size( ) - 1; int mid; while( low < high ) mid = ( low + high ) / 2; if( a[ mid ] < x ) low = mid + 1; else high = mid; } return ( low == high && a[ low ] == x ) ? low : NOT_FOUND;
Limitation de l’analyse Si on pense que T(n) = Θ (f(n)), on peut valider expérimentalement T(n)/f(n) = c Considérez des n suffisamment grands Lors de l’analyse, on choisit souvent le pire des cas, mais celui-ci peut être significativement pire que le cas moyen !
Limitation de l’analyse T(n)/f(n)
Espace mémoire La quantité d’espace mémoire utilisé peut aussi être étudiée à l’aide de l’analyse asymptotique. En général: Temps: Algorithme manipulant les structures de données. Espace: Structure de données
Compromis espace/temps Il est souvent possible de réduire le temps d’exécution au prix d’augmenter l’espace mémoire et vice versa. Fichiers compressés Table des valeurs d’une fonctions Insérer un nœud dans une liste chaînée Espace disque: Un programme sera d’autant plus rapide qu’il requiert peu d’accès disque.