



























Étudies grâce aux nombreuses ressources disponibles sur Docsity
Gagnz des points en aidant d'autres étudiants ou achete-les avec un plan Premium
Prépare tes examens
Étudies grâce aux nombreuses ressources disponibles sur Docsity
Obtiens des points à télécharger
Gagnz des points en aidant d'autres étudiants ou achete-les avec un plan Premium
Communauté
Demandes de l'aide à la communauté et dissipes tes doutes concernant l'étude
Guide gratuite
Télécharges gratuitement nos guides sur les techniques d'étude, les méthodes de gestion de l'anxiété, les conseils pour la thèse réalisés par les tuteurs Docsity
Exercices d’informatique sur l'initiation à l’algorithmique - 3° partie. Les principaux thèmes abordés sont les suivants: Algorithme Glouton, Quelques algorithmes de tri,
Typologie: Notes
1 / 35
Cette page n'est pas visible dans l'aperçu
Ne manques pas les parties importantes!




























En fait deux cas principaux se pr´esentent selon que le r´esultat du produit de deux ´el´ements conserve la taille des ´el´ements initiaux ou prend pour taille la somme des tailles de ces ´el´ements initiaux.
Si conservation de la taille, puissance1 est exponentiel, puissance3 est quadra- tique. Si il y a conservation de taille, tous les produits ont la mˆeme complexit´e, celle not´ee taille(x), du premier ´el´ement x pass´e en argument. Ainsi, la complexit´e en temps dans le pire des cas de :
Si somme des taille, puissance1 et puissance3 sont exponentiels. Si la taille du produit est la somme des tailles des op´erandes, la complexit´e en temps dans le pire des cas de :
a 1, taille(x), 2.taille(x),.. .(k-1)taille(x). La somme fournit tres exactement (k(k + 1)/ 2 − 1)taille(x) que l’on approche selon k^2 taille(x).0 ≤i≤log(k) 2
i (^) = 2log(k)+1 (^) − 1 = Θ(k), on en d´eduit que la complexit´e en temps de l’algorithme est celle du dernier produit ex´ecut´e c’est `a dire Θ(k · taille(x)).
Nous pr´esentons ici diff´erents types element et interpretons le r´esultat dans le contexte de ce choix.
le type entierLong : augmentation de la taille
Un entier x est ici de taille log(x). Les deux algorithmes sont exponentiels. Mˆeme en supposant qu’un produit sur des entiers de 8 bits se fasse en 10^9 seconde, le calcul de puissance1(10,10000000000000000000) n´ecessite (10^20 )^2 = 10^40 op´erations ´el´ementaires c’est `a dire 10^23 ann´ees de calculs, ce qui fait beaucoup. Le second plus rapide n´ecessite tout de mˆeme 10^20 op´erations ´el´ementaires soit 100 ann´ees de calcul, ce qui n’est pas rien!
Le type int : conservation de la taille
Le type int est de taille constante, souvent 4 octets. Le produit est de com- plexit´e constante et ne modifie pas la taille des entiers multipli´es. En fait, l’analyse et th´eorique et pratique de la complexit´e n’a aucun sens puisque pour de tres tres petits entiers, on d´epasse la quantit´e maximale autoris´e : 1010 est d´eja sup´erieura 2^32 et donc n´ecessite plus de 4 octets.
Le type float : conservation de la taille
Le type float est de taille constante, 4 octets. Le produit est de complexit´e constante et ne modifie pas la taille des entiers multipli´es. Contrairement aux int, un float ´etant repr´esent´e grace notamment `a un exposant prenant 256 valeurs. On peut calculer la puissance d’un float selon un petit entier char. D’un point de vue pratique, le d´ebut ( ?) d’un comportement assymptotique peut ˆetre observ´e pour ces 256 valeurs.
Le type matrice carr´ee de r´eels : conservation de la taille
Si l’on considere une matrice n · n carr´ee de r´eels, ces derniers ´etant suppos´es de taille constante. Le produit clairement conserve la taille, toujours ´egalea n · n. L’algorithme puissance1 est de complexit´e exponentielle Θ(k · n^2 ). L’algorithme puissance3 est de complexit´e Θ(log(k) · n^2 ).
Remarque 12 Cette complexit´e peut ˆetre qualifi´ee de quadratique puisque la taille de l’entr´ee est log(k) + n^2. La complexit´e log(k) · n^2 est major´ee par le carr´e (log(k) + n^2 )^2. On peut atteindre cette complexit´e quadratique, il suffit de choisir un entier k de taille log(k) = n^2 , et obtenir une complexit´e log(k) · n^2 ´egale au carr´e (log(k) + n^2 )^2 de la taille de l’entr´ee (a la constante multiplicative 14 pres).
Exercice 36 Poursuivre l’´etude de complexit´e en consid´erant au choix :
Exercice 37 Evaluer la complexit´´ e en espace des diff´erents algorithmes dans chacune des deux nouvelles hypoth`eses.
L’approche “Diviser Pour R´egner” (Divide and Conquer) est une m´ethode qui permet de r´esoudre un probleme en fournissant un algorithme r´ecursif. Cette m´ethode n’offre naturellement aucune garantie : il n’existe pas de m´ethode (ou algorithme) permettanta partir d’un probleme d’obtenira coup sˆur une solution algorithmique. La structure g´en´erale d’un algorithme “Diviser Pour R´egner” A et permettant d’associer `a une entr´ee x une solution s comporte 3 parties :
a appliquer r´ecursivement la fonction A sur chacune des nouvelles entr´ees x,... , xa eta retourner les a solutions s 1 ,... , sa.a recomposera partir des solutions par- tielles s 1 ,... , sa la solution s associ´ee `a x.Supposons que nous manipulions des entiers de tr`es grande taille (plusieurs centaines d’octets) et que nous souhaitions les multiplier :
Probl`eme ProduitEntiers Entr´ee : a, b : entier Sortie : a·b
La taille n d´esigne la taille max(log(a), log(b)) maximale des entiers a et b.
L’algorithme peut s’´ecrire :
fonction produit(a,b: entier):entier
n ← taille(a,b) ;
si n= 1 si (a=1 ET b=1) retourner 1 sinon retourner 0
p ← n >> 1 ; % p=⌊n 2 ⌋
(a 1 ,a 2 ) ← d´ecomposition(a,n) ; (b 1 ,b 2 ) ← d´ecomposition(b,n) ;
s 1 ← produit(addition(a 1 ,a 2 ),addition(b 1 ,b 2 )) ; s 2 ← produit(a 1 ,b 1 ) ; s 3 ← produit(a 2 ,b 2 ) ;
res ← s 1 <<p ; % signifie : res ← s· 2 p res ← addition(res, s 2 <<n) ; res ← soustraction(res, s 2 <<p) ; res ← addition(res, s 3 ) ; res ← soustraction(res, s 3 <<p) ;
retourner res
Evaluation de la complexit´^ ´ e
Notons f : N → N∗^ la fonction de complexit´e en temps. Pour simplifier l’expos´e, la taille d’un couple est consid´er´ee comme le maximum des tailles des deux entiers. Observant que pour multiplier deux entiers a et b de taille chacun au max n, il est n´ecessaire de :
En clair, la fonction complexit´e f (n) est d´efinie r´ecursivement par f (1) = 1 et f (n) = n + 3f (
n 2
Nous verrons comment r´esoudre un tel systeme d’´equations. Nous pouvons ici le r´esoudrea la main. La solution est f (n) = n + 3(n 2 + 3( 2 n 2 +.. .)) = n((^32 )^0 + (^32 )^1 +
.. .) ≈ n.(^32 )ln^2 (n)) qui est ´egal a n · nln(^ (^32) )/ ln(2) . Observant que 1 + ln(^32 )/ ln(2) est ´egala ln(3)/ ln(2) = ln 2 (3), nous avons :
f (n) = nln^2 (3)^ ≈ n^1 ,^58
Cet algorithme est donc meilleur que le premier algorithme de complexit´e Θ(n^2 ). De nouvelles am´eliorations “Divide and conquer” sont possibles qui per- mettent pour tout r´eel ǫ > 0 de fournir un algorithme solution de Produit de complexit´e en temps Θ(n1+ǫ). D’autre part des techniques utilisant les transform´ees de Fourier permettent en temps lin´eaire Θ(n) de r´esoudre ce mˆeme probl`eme. Ainsi, le produit est de mˆeme complexit´e qu’une addition ou qu’une comparaison.
6.2 Evaluation de la complexit´´ e
Evaluer la complexit´^ ´ e d’un algorithme se fait en deux temps.
Cette premi`ere peut ˆetre imm´ediate. Il est n´ecessaire de “projeter” la d´efinition r´ecursive de l’algorithme en une d´efinition r´ecursive de la fonction complexit´e en temps. Nous pouvons alors obtenir par exemple l’´equation :
f (n) = f (
n − 3 4
) + 6 · f (
2 n − 1 8
) + 3 · n +
n + 879
Cette ´equation doit ˆetre d´ebarrass´ee des termes marginaux, des constantes multiplicatives n’apparaissant pas dans le terme r´ecursif. Nous obtenons alors pour ´equation : g(n) = 7 · g(
n 4
) + g
Exercice 40 D´emontrer que les fonctions f et g v´erifiant les deux syst`emes d’´equations pr´ec´edents v´erifient f = Θ(g) et donc g = Θ(f ).
Exercice 43 Pour r´ealiser le produit de deux entiers, une autre ´equation v´erifi´ee par a · b est :
a · b = (a 1 · b 1 ) · 2 n^ + (a 1 · b 2 + a 2 · b 1 ) · 2
n 2
Cette ´equation fournit un algorithme r´ecursif.
6.3 Un deuxi`eme exemple : la multiplication de
deux matrices
Nous pouvons nous inspirer du produit de deux entiers, pour r´ealiser le produit de deux matrices carr´ees :
probl`eme ProduitMat Entr´ee : deux matrices X Y carr´ees de m^eme taille Sortie : le produit matriciel de X et Y
Notons Z la matrice produite. D´ecomposant chacune des matrices carr´ees X, Y et Z de mˆeme taille suppos´ee paire en des matrices A, A, B, C, D, E, F, G, H, I, J, K, L, de la fa¸con suivante :
| A B | | E F | | I J | X = | C D | Y= | G H | Z= | K L |
La premi`ere m´ethode d´ecoule des ´equations ´evidentes :
I = AE + BG J = AF + BH K = CE + DG L = CF + DH
Exercice 44 En vous inspirant de la section traitant du produit de deux entiers :
3 (^2) ).
Reprenant les notations de la section pr´ec´edentes, la seconde m´ethode d´ecoule des ´egalit´es suivantes :
P1 = A(F-H) P5 = (A+D)(E+H) P2 = (A+B)H P6 = (B-D)(G+H) P3 = (C+D)E P7 = (C-A)(E+F) P4 = D(G-E)
Exercice 45 1. V´erifier la correction des ´equations propos´ees.
Une solution algorithmique am´eliore l’algorithme de Strassen (Θ(n^1 ,^403 ) et fournit un algorithme de complexit´e Θ(n^1.^138 ) (Coppersmith & Winogend). Pour des raisons ´evidentes, le produit de deux matrices n´ecessite de lire chacun des ´el´ements de la matrice et n´ecessite au moins Θ(n) op´erations. Peut-on `a l’image du produit de deux entiers (ou de deux vecteurs) atteindre cette limite Θ(n) ou sinon, s’en rapprocher davantage? La question est ouverte.
Nous verrons dans ce chapitre comment g´erer efficacement la m´emoire de fa¸con `a ´eviter la r´ep´etition qui peut ˆetre exponentielle de certains mˆemes calculs. Nous traiterons l’exemple d’un algorithme de complexit´e exponentielle trans- form´e en un algorithme polynomial. Consid´erons l’algorithme suivant :
fonction toto(s:s´equence d’entiers):entier
si longueur(s) = 1 alors retourner premierElement(s) sinon retourner toto(extract1(s)) + toto(extract2(s)) ;
ou extract1 et extract2 extraient deux sous-s´equences de s. Notons n la longueur de la s´equence s. Supposons pour simplifier que la complexit´e de extract1 et extract2 est Θ(1). Dans le cas ou extract1(s) et extract2(s) sont syst´ematiquement de longueur ≤ n b avec b > 1, nous nous trouvons face `a un algorithme “Divide and Conquer” de complexit´e en temps polynomial car solution de g(n) = 1 + 2 · g(n b ).
A contrario, supposons par exemple que` extract1(s) (resp. extract2(s) retire un unique ´el´ement de s par exemple le premier (resp. le dernier). La fonction complexit´e en temps v´erifie l’´equation :
g(n) = 1 + 2 · g(n − 1)
et est ´egale a Θ(2n)! L’algorithme est exponentiel et est impraticable (le traite- ment d’une s´equence de 100 ´el´ements n´ecessite un siecle et ce mˆeme si 1 milliard d’op´erations ´el´ementaires sont ex´ecut´ees par seconde).
L’observation des calculs effectu´es par toto nous montre que l’appel sur la s´equence (1,2,3,4,5,6) entraine l’ex´ecution de toto sur les s´equences (1,2,3,4,5) et (2,3,4,5,6). Ce qui provoquera r´ecursivement 4 appels sur les s´equences (1,2,3,4), (2,3,4,5), (2,3,4,5) et (3,4,5,6). Nous voyons ici que par deux fois toto est ex´ecut´e sur la s´equence (2,3,4,5). Si on d´etaille le nombre de fois o`u les appels sont ex´ecut´es sur chacune des sous s´equences nous obtenons le dessin suivant :
Il est facile d’observer que le nombre d’appels sur les s´equences de longueur l est exponentiellement proportionnel `a son oppos´e soit 2^6 −l^ : 2^5 appels sur les s´equences de longueurs 1 ont ´et´e ex´ecut´es alors que 6 en tout auraient suffit.
L’id´ee de la programmation dynamique repose sur l’id´ee de r´ealiser un com- promis espace-temps, c’est a dire d’´eviter de r´ep´eter les mˆemes calculs quitte, pour ´eviter ces r´ep´etitions,a utiliser une m´emoire auxiliaire pour se rappeler des calculs effectu´es.
7.1 Une solution en programmation dynamique
Pour m´emoriser les calculs, nous avons besoin de deux informations :
ere d´ejaCalc indique pour toute s´equence si le calcul de toto a d´ej`a ´et´e effectu´e.a s et du rang du dernier ´el´ement de t relativementa s. L’algorithme devient alors :En cons´equence, nous avons un algorithme quadratique Θ(n^2 ) alternative ef- fective `a un algorithme toto impraticable car de complexit´e exponentielle Θ(2n).
Dans l’exemple pr´ec´edent, nous pouvons abandonner l’´ecriture r´ecursive et dynamique et obtenir un meilleur algorithme qui organise it´erativement le rem- plissage de la matrice valeurs en prenant des couples (i, j) de fa¸con a faire croˆıtre j − i. Cette connaissance nous permet ainsi de faire l’´economie et de la matrice d´ejaCalc et de la pile d’appel induite par tout algorithme r´ecursif. L’algorithme est :
fonction totoIt´eratif(s:s´equence): entier
l ← longueur(s) ; valeurs ← matriceCarr´ee(l,0) ;
pour i de 1 a l faire valeurs[i][i] = iemeEl´ement(s,i) ;
pour d de 1 a l-1 faire pour i de 1a l faire valeurs[i][i+d] ← valeurs[i][i+d-1] + valeurs[i+1][i+d] ;
retourner valeurs[1][n]
7.2 Bien fond´e de l’approche dynamique
L’existence d’un algorithme it´eratif aussi simple que totoIt´eratif est li´ee a notre capacit´ea pr´edire quelles sont les sous-s´equences de s sur lesquelles le calcul doit ˆetre effectu´e (ici toutes les sous-s´equences de la forme (si,... , sj ) avec s = (s 1 ,... , sn)) et dans quel ordre elles doivent ˆetre ´evalu´ees (ici selon j − i croissant). Le caract`ere polynomial de totoIt´eratif ´etait possible car l’ensemble de ces sous-s´equences ´etait de cardinalit´e polynomiale ici Θ(n^2 ). Parfois cette connaissance est absente et seule une approche dynamique il- lustr´ee par totoDynamique est possible. Supposons que extract1 et extract2 extraient de toute s´equence s des sous- s´equences selon des algorithmes complexes. Par exemple :
fonction extract1(s:s´equence):s´equence % notation s = (s 1 ,...,sn)
si s 1 + s 2 < s 3 + 3 alors retourner (s 1 ,s 3 ,...,sn)
sinon si s 2 ≥ s 4 alors retourner (s 2 ,s 4 ,...,sn) sinon si etc
A cause d’une telle complexit´^ e, il est possiblea priori que toute sous-s´equence de s ait une ´evaluation n´ecessaire a celle de s. Or le nombre de sous-s´equences de s est exponentiel Θ(2n) : par exemple la s´equence (1, 2 , 3) de longueur 3 admet exactement 7 = 2^3 − 1 sous-s´equences : (), (1), (2), (3), (1, 2), (1, 3), (2, 3). Il n’est alors pas possible d’´evaluer toutes les sous s´equences de s comme l’a r´ealis´e totoIt´eratif. Il nous faut ´evaluer que les sous-s´equences r´eellement n´ecessaires, c’esta dire celles rencontr´ees lors du calcul r´ecursif. L’approche est celle utilis´ee par totoDynamique a la diff´erence pres qu’il faut reconsid´erer la d´efinition de d´ej`aCalc et de valeurs. Sans rentrer dans les d´etails, nous pouvons supposer :
aCalc est un ensemble de s´equences dans lequel nous pouvons ajouter toute nouvelle s´equence (fonction ajouter) et dans lequel nous pouvons d´ecider de l’appartenance d’une s´equencea cet ensemble (fonction appartient).fonction totoDynamique2(s:s´equence): entier
(d´ej`aCalc,valeurs) ← totoDynamRec2(ensVide(),ensVide(),s) ;
retourner calcValeur(valeurs,s) ;
fonction totoDynamRecG(d´ej`aCalc,valeurs: ensemble,s : s´equence) : ensemble × ensemble
si appartient(s,d´ej`aCalc) retourner calcValeur(valeurs,s) ;
sinon si longueur(s)= x ← uniqueEl´ement(s) ; sinon t ← extract1(s) ; u ← extract2(s) ;
(d´ejaCalc,valeurs) ← totoDynamRec2(d´ejaCalc,valeurs,t) ; (d´ejaCalc,valeurs) ← totoDynamRec2(d´ejaCalc,valeurs,u) ;
L’approche “Divide and Conquer” construit une solution apres avoir d´ecoup´e l’entr´ee initiale en plusieurs sous-entr´ees et apres avoir calcul´e les solutions par- tielles a chaque nouvelle sous-entr´ee. La construction de la solution se fait en fait apres avoir parcouru toute l’entr´ee. L’approche gloutonne diff`ere de celle-ci en construisant une partie de la solu- tion de fa¸con d´efinitive.
Soit E l’ensemble des monnaies en euro. Le probleme de la boulangere est de rendre une valeur S en euro en utilisant un nombre minimal de pi`eces.
probl`eme RenduDeMonnaie Entr´ee : S : entier, E : ensemble d’entiers Sortie : une s´equence d’entiers de E de somme S et de longueur minimale
L’algorithme s’´ecrit de fa¸con r´ecursive de la fa¸con suivante :
fonction renduRec(S:entier ; E: ensemble d’entiers): s´equence d’entiers
si S= retourner () sinon calculer x la plus grande valeur de E inf´erieure ou ´egale `a S ;
retourner ajouter(x,renduRec(S-x))
ou ainsi de fa¸con it´erative :
fonction renduIter(S:entier ; E: ensemble d’entiers): s´equence d’entiers
solution ← s´equenceVide() ;
tantque S 6 = 0 faire
calculer x la plus grande valeur de E inf´erieure ou ´egale `a S ;
solution ← ajouter(x,solution) ;
S ← S - x ;
retourner solution
Cet exemple illustre ce qu’est un algorithme glouton en ´ebauchant la solution sans prendre connaissance de la totalit´e de l’entr´ee : en effet, pour rendre la valeur de 17 centimes, seul le premier chiffre compte (le chiffre 1) et d´etermine de rendre une pi`ece de 10 centimes si S vaut { 1 , 2 , 5 , 10 , 20 , 50 }.
La correction de cet algorithme est li´e au choix de l’ensemble E. Si l’ensemble des pieces europ´eennes avait ´et´e { 1 , 4 , 6 } l’algorithme aurait ´et´e incorrect : il associea la valeur 8 la s´equence (6, 1 , 1) qui n’est pas optimale, la solution optimale ´etant (4, 4).
Pour des raisons de complexit´es en temps et en espace, une autre repr´esentation de la s´equence solution est pr´ef´erable. En effet si E est ´egal a { 1 , 2 , 5 , 10 }, la so- lution associ´eea la valeur 1000000 (resp .10^100 ) est la s´equence compos´ee de 10000 (resp. 10^99 ) ´el´ements ´egaux a 10. Cet algorithme est donc n´ecessairement exponentiel. Une autre repr´esentation peut se faire dans l’exemple E = { 1 , 2 , 4 , 10 } par un tableau indic´e de 1a 4 et indiquant le nombre de pi`eces correspondant. L’algo- rithme est :
fonction rendu(S:entier ; E: tableau d’entiers): tableau d’entiers
solution ← tableau(4)(0) ;
pour i de 1 `a 4 faire
solution[i] ← ⌊ (^) ES[i] ⌋ ;