Algorithmique et Complexité 4. Stratégie I : Diviser pour Régner et Tri 4.1 Diviser pour Régner Nicole Bidoit Université Paris XI, Orsay Année Universitaire 2008–2009 1 Diviser pour régner De l’adage politique divide ut imperes à une stratégie fondamentale de l’algorithmique Résoudre le problème P par un algorithme A qui, pour chaque instance I de taille n 1. coupe l’instance I en 2 (ou plus!) instances I1 et I2 2.résoud le problème P pour I1 et I2 avec l’algorithme A 3. compose les solutions P(I1 ) et P(I2 ) pour produire la solution P(I). ”Diviser pour régner” versus ”algorithme récursif” complexité : Diviser pour gagner en temps ֒→ Exploiter les propriétés des sous-instances ֒→ Bien ”couper” (sans que ce soit compliqué) ֒→ Composition facile De nombreux exemples : la recherche, le tri, exponentiation (calcul de an ) la multiplication d’entiers, de matrices, de polynômes enveloppe convexe de points, diagramme de Voronoı̈ ... 2 Stratégies algorithmiques Diviser pour régner Recherche dichotomique Entrée : un tableau T trié Binary Search et un élément e 1. Couper le tableau T [1..n] en 2: T [1..⌊ n2 ⌋] et T [⌊ n2 ⌋+1..n] 2. Chercher e dans les 2 sous instances (-: la recherche est triviale pour une des deux sous-instances ! 3. ←− le gain est là e est trouvé dans T si il est trouvé dans l’une des deux sous-instances. Recherche dans une liste contiguë non triée ←− aucun gain même stratégie que ci-dessus Recherche d’un élément majoritaire (Exercice) Soit T [1..n] un tableau représentant une liste d’éléments de taille n. Un élément e de T est majoritaire si l’ensemble {i | T [i]=e} est de cardinalité strictement supérieure à n 2. Proposer un algorithme simple ; analyser sa complexité ? Proposer un algorithme ”diviser pour régner” ; analyser sa complexité ? 3 Stratégies algorithmiques Diviser pour régner Tri fusion Merge Sort Entrée : tableau T représentant un ensemble déléments muni d’un ordre 1. Couper le tableau T [1..n] en 2 : T [1..⌊ n2 ⌋] et T [⌊ n2 ⌋+1..n] 2. Trier chacune des 2 sous instances avec la même méthode 3. Tri(T ) est la fusion des deux sous-instances triées. la fusion intercale les éléments des deux tableaux Tri rapide Quicksort Entrée : tableau T représentant un ensemble d’éléments muni d’un ordre 1. Couper le tableau T [1..n] en 2 en utilisant un élément pivot T [k] tous les e dans T [1..m] sont ≤ au pivot et tous les e dans T [m+1..n] sont > au pivot 2. Trier chacune des 2 sous instances avec la même méthode 3. Tri(T ) est la concaténation des deux sous-instances triées. Le gain dépend du choix du pivot Le cas le meilleur (Exercice) Le cas le pire (Exercice) 4 Stratégies algorithmiques Diviser pour régner – Produit de Matrices Cas des matrices 2×2 Méthode classique. a c b d e g f h = ae+bg ce+dg af +bh cf +dh 8 multiplications 4 additions Algorithme de Strassen. a c b d e g f h = r t p1 = a(f − h) s u r = p5 + p4 − p2 + p6 7 multiplications p2 = (a + b)h s= p1 + p2 18 additions p3 = (c + d)e t = p3 + p4 p4 = d(g − e) u= p5 + p1 − p3 + p7 et donc p5 = (a + d)(e + h) p6 = (b − d)(g + h) p7 = (a − c)(e + f ) Strassen est meilleur, en terme de nombre de multiplications ... 5 Stratégies algorithmiques Diviser pour régner – Produit de Matrices – Algorithme de Strassen Cas général : matrices n×n avec n=2m [si n n’est pas une puissance de 2 alors les matrices sont étendues avec des 0] 1. Découper des matrices M et N comme suit : chaque matrice est découpée en 4 sous matrices de dimension 2m−1 ×2m−1 M = M M M no M so ne se et N no N= N so N ne N se 2. Calculer (avec l’algo Strassen) les produits de matrices Pi =Ai .Bi , pour i=1 à 7, où A1 = M no B1 = N ne − N se A4 = M so B4 = N so − N no A2 = M no + M ne B2 = N se A5 = M no + M se B5 = N no + N se A3 = M so + M se B3 = N no A6 = M ne + M se B6 = N so + N so A7 = M ne − M se B7 = N no + N ne ֒→ les matrices Ai et Bi sont de dimensions 2m−1 ×2m−1 3. Composer comme suit pour obtenir le résultat O Ono O= Oso O ne O se = M.N : Ono = P5 + P4 − P2 + P6 avec One = P1 + P2 Oso = P3 + P4 Ose = P5 + P1 − P3 + P7 6 Stratégies algorithmiques Diviser pour régner Complexité de l’algorithme classique – nombres d’additions et de multiplications on calcule n2 coefficients chaque coefficient ”coûte” n additions et n produits M ult(n) et Add(n) sont dans Θ(n3 ) Complexité de l’algorithme de Strassen Ce qu’on gagne ? M ult(1) = 1 M ult(n) = 7.M ult( n ) 2 M ult(n) = 7m = 7log2 n = nlog2 (7) Add(1) = 0 Add(n) = 7.Add( n ) + 18.( n )2 2 2 Add(n) = 6.(nlog2 (7) − n2 ) L’ordre de grandeur des calculs (multiplication et addition) est Θ(nlog2 (7) ) La borne inférieure ? 7 O(n2 ) Stratégies algorithmiques [il faut ”toucher” à chaque coefficient] Diviser pour régner Analyse de complexité des algorithmes ”Diviser pour régner” ֒→ un squelette général pour l’analyse ֒→ après, ça (la combinatoire) se complique parfois Hypothèse : L’algorithme A pour le problème P ”coupe” l’instance I de taille n en s sous-problèmes de taille n p dont la résolution, par A, permet de résoudre l’instance I. Squelette : S(1) = 1 S(n) = Coup(n) + s . S( n ) + Comp(n) p Exemple : pour l’algorithme de Strassen s=7 Coup(n) = 0 p=2 Comp(n) = 18.( n2 )2 Variations de l’algorithme de Strassen Qu’en est-il de la version ”Diviser pour régner” de l’algorithme de multiplication classique ? Et si on coupe chaque matrice en 9 blocs de taille n 3 ? Quel découpage pour faire mieux que Strassen ? 8 Stratégies algorithmiques