Algorithmique et complexité de calcul Objectifs : • Étude des techniques de conception et d'analyse des algorithmes. • comparaison et classification des algorithmes. • Ce n’est pas un catalogue d'algorithmes pour la résolution de problèmes spécifiques. Plan : I II III IV V VI VII VIII Préliminaires Analyse de l'efficacité des algorithmes Diviser pour régner Algorithmes voraces Programmation dynamique Transformation du domaine Algorithmes probabilistes Pré conditionnement Chapitre 1 : Préliminaires 1 Notion d’algorithme 2 Efficacité des algorithmes 3 Nature de l’analyse 4 Pourquoi des algorithmes efficaces 5 Calcul des nombres de Fibonacci 1 Notion d’algorithme Origine : le mot "algorithme" est associé au célèbre auteur Perse Abou Jaafar Mohammed Ibn Moussa Al Khawarizmi connu pour son livre "Al Jabr oua El Mokabala" écrit en l'an 825. Un algorithme est une méthode systématique pour résoudre un problème donné. L'exécution ne doit pas laisser la place à l'interprétation, l’intuition ni à la créativité. L’Algorithmique est l’étude des techniques de conception et d’analyse des algorithmes. Intérêt de l’étude de l’efficacité d’un algorithme ou de sa complexité : Un problème peut être résolu par plusieurs algorithmes. L’étude de la complexité permet de choisir le meilleur = le plus efficace. L’efficacité est caractérisée par : Les temps d’exécution (on recherchera le plus rapide) L’occupation de la mémoire Consommation optimale des ressources de l’ordinateur Cette étude utilise La trace Chronomètre Etude théorique Exemples : • Multiplication des nombres entiers • Tri • Fibonacci Multiplication des nombres : 8*9 et 54*17 Travail : Proposer des algorithmes pour faire ces multiplications et des outils pour les comparer. Algorithmes classique et russe Pré requis pour les multiplications: Méthode classique : tables de multiplication et d’addition Méthode russe : multiplication par 2 et division par 2 (décalages à droite et à gauche dans une représentation en base 2) + addition Travail demandé : Compter le nombre de divisions et opérations Ecrire l’opérande qui est divisé en binaire Conclusion ? Algorithme de multiplication russe fonction russe (a,b) tableau X , Y X[1] a , Y[1] b, i 1 { initialisation } tant que X[i] > 1 faire {former les 2 colonnes} X[i + 1] X[i] div 2 Y[i + 1] Y[i] * 2 ii+1 p 0 {addition des entrées appropriées} tant que i > 0 faire si (X[i] mod 2 = 1) alors p p + Y[i] i i-1 retourner p Exercice : Faire la trace pour l'exemplaire (17,53). Modifier cet algorithme pour avoir une seule boucle et utiliser des variables scalaires. Autre algorithme de multiplication russe fonction autre_russe (a , b) entier x , y x a, y b, p 0 {initialisation} tant que x ≥ 1 faire si (x mod 2 = 1) alors p p + y {ajouter la valeur appropriée} x x div 2 yy*2 retourner p Algorithme de la multiplication pas russe fonction pas_russe (A,B) tableau X,Y X[1] A, Y[1] B, i 1 { initialisation } tant que X[i] > 1 faire { former les 2 colonnes } X[i + 1] X[i] - 1 Y[i + 1] B i i+1 P 0 { additionner les entrées appropriées } tant que i > 0 faire si X[i] > 0 alors P P + Y[i] i i-1 retourner P 2 Efficacité des algorithmes Définition : Un exemplaire x est l’entrée d’un algorithme, |x| = n = taille de l'exemplaire x Exemples : - Tri : |x| est le nombre d'entiers à ordonner. - Multiplication : |x| est le nombre de chiffres (ou bits) des facteurs Approches d’analyse : - Empirique : programmation + exécution avec plusieurs exemplaires - Théorique : déterminer la quantité de ressources (temps, mémoire,...) en fonction de la taille des exemplaires Avantages de l'approche théorique : - Indépendance de l'ordinateur et langage de programmation - Gain dans l'exécution des algorithmes inefficaces - Taille des exemplaires n'est pas une contrainte 3 Nature de l’analyse Efficacité d’un algorithme dépend - taille de l’exemplaire - espace mémoire -… Comparaison des algorithmes selon différentes analyses - meilleur cas (optimiste) - moyenne - pire cas (pessimiste) Cf. Notebook pour Tri et Fibonacci Tri par insertion Procédure insert (T[1..n]) pour i2 jusqu'à n faire xT[i] j i - 1 tant que j > 0 et T[j] > x faire T[j + 1] T[j] j j - 1 T[j + 1] x Exercice : Faire la trace pour T = [3,1,4,0,5], U = [0,3,4,6,9] et V = [9,6,4,3,0] Et W = [7,4,3,1], Tri par sélection Procédure select (T[1..n]) pour i1 jusqu'à n-1 faire minj i, minxT[i] pour j i + 1 jusqu'à n faire si T[j] < minx alors minjj minxT[j] T[minj] T[i] T[i] minx Exercice : Faire la trace pour W = [7,4,3,1], Travail demandé : Programmer ces algorithmes avec des chronos en déduire le plus rapide Faire la trace en déduire la fonction de récurrence qui donne le temps d’exécution en fonction de la taille Classer ces temps d’exécution du + rapide au plus lent Question : Faut-il concevoir des algorithmes performants ou des machines de plus en plus puissantes ? Supposons : Un algorithme A et une machine M Un algorithme A’ plus efficace que A Une machine M’ plus puissante que M t(n) = temps d’exécution, sur un exemplaire de taille n, de A sur M t’(n): temps d’exécution dans un ordinateur plus rapide, de A sur M’ t’’(n): temps d’exécution d’un algorithme plus efficace : de A’ sur M t(n)= 10-4 x 2ns t’(n)= 10-2 x 10-4 x 2n = 10-6 x 2n s t’’(n)= 10-4 x n3 x 102 = 10-2 x n3 s Relevé des temps d’exécution n t(n) t’(n) t’’(n) 10 1/1O s 2 ms 10 s 20 2 mn 1s 1 mn 30 10 jours 3 heures 5 mn 38 1 année 4 mois 10 mn 45 - 1 année 20 mn 200 - - 1 jour 1500 - - 1 année Graphes des temps d’exécution en log, log L’amélioration des algorithmes est plus intéressante que celles des machines Voilà pourquoi il faut des algorithmes efficaces Définition de ‘’Ordre de f(n)‘’ = (majoration) Soit f : IN IR* O( f(n) ) = { t : IN IR+ / ( c IR+ )( n0 IN ) n n0 t(n) c f(n) } O ( f(n) ) est appelé l’ordre de f(n). t(n) est dans l’ordre de f(n) ssi t(n) O ( f(n) ) Travail demandé : a) Quel est l’ordre d’un algorithme dont le temps d’exécution vérifie la récurrence suivante: T (n) = 27 n2 ms - 18 n ms + 3 s b) Prouver que: si f(n) O ( g(n) ) et g(n) O ( h(n) ) alors f(n) O ( h(n) ) c) Déduire alors que si g(n) O ( h(n) ) alors O ( g(n) ) O ( h(n) ) d) Soient f et g : IN IR+, montrez que : O ( f(n) + g(n) ) = O (max(f(n),g(n) ) e) Soient f et g : IN IR+, et c prouvez que : lim n f(n) / g(n) = c IR+ O ( f(n) ) = O ( g(n) ) lim n f(n) / g(n) = 0 O ( f(n) ) O ( g(n) ) f) Prouver que: log n O ( n ) et n O (log n) g) Soit 0< x <1 , utilisez pour classer les ordres des fonctions suivantes : nLog n, 1, n , Log n , n , n2 , n2 / Log n , n (1+x) , (1+x) n , n8 Définition de ‘’Oméga de f(n)‘’ = (minoration) W( f(n) ) = { t : IN IR+ / ( c IR+ )( n0 IN ) n n0 t(n) c f(n) } Définition de ‘’Ordre exacte de f(n)‘’ : Q ( f(n) ) Q ( f(n) ) = O (f(n)) W (g(n)) Ou encore t(n) est borné par : c1 f(n) t(n) c2 f(n) Travail demandé : Programmer ces 3 algorithmes avec des chronos, remplir alors le tableau et en déduire le plus rapide Suites de Fibonacci Faire la trace de fib1, fib2 et fib3 pour n = 5 Déterminez la classe (complexité) du temps d’exécution pour chaque algorithme Exécutez (Dev) en ayant implémenté des compteurs pour les 3 algorithmes pour les valeurs de n proposées et présentez les temps d’exécution dans le tableau suivant : Conclusions n 10 20 30 40 50 102 104 106 XXX XXX XXX fib3 fib2 fib1 fonction fib1(n) si n < 2 alors retourner n sinon retourner fib1(n-1) + fib1(n-2) fonction fib2 (n) j 0 ; i1 pour k1 jusqu'à n faire ji + j i j - i retourner j fonction fib3(n) i 1 ; j 0 ; k 0 ; h 1 tant que n > 0 faire si n est impair alors t jh j ih + jk + t i ik + t t h2 h 2kh + t k k2 + t n n div 2 retourner j