Licence MPI, Semestre 1, Introduction `a l`informatique (Info 111

Licence MPI, Semestre 1, Introduction `a l’informatique (Info 111) Ann´ee 2016–2017
TD 10 : complexit´e
Dans tous les exercices, lorsque l’on demande de d´eterminer une complexit´e, bien pr´eciser
le mod`ele de calcul : taille du probl`eme, op´erations ´el´ementaires, ... En revanche, on pourra
se contenter de d´eterminer l’ordre de grandeur de la complexit´e et non sa valeur exacte. On
pourra utiliser la notation O(...), ou se contenter de calculer «avec les mains ».
Correction : On fait quelques rappels sur les notations de Landau. Dans toute la suite, on
suppose que fet gsont des fonctions `a valeurs strictement positives et enti`eres.
On dit que
f=O(g)
s’il existe une constante
C
telle que pour tout
x
, on ait
f(x)Cg(x)
.
Dans ce cas, on dit que fest major´ee par g.
On dit que
f= Θ(g)
s’il existe deux constantes
C1
et
C2
telles que pour tout
x
, on ait
C1f(x)g(x)C2f(x). Contrairement `a O, c’est une relation d’´equivalence.
De fa¸con plus g´en´erale, on vous renvoie `a votre cours de math´ematiques :)
Exercice 1.
(1) Proposer une implantation de la fonction suivante :
/** Trouve la derni`ere apparition dun entier dans un tableau
* @param v un tableau dentiers
* @param x un entier
* @return la position de la derni`ere occurence de x dans v,
* ou -1 sil ny a pas de telle occurence
**/
int dernierePosition(vector<int> v, int x) {
(2) Quelle est la complexit´e au pire de cet algorithme ?
(3)
(
) Quelle est la complexit´e en moyenne de cet algorithme, sous l’hypoth`ese que
x
apparaˆıt dans v?
Correction :
(1) Il y a deux fa¸cons de faire que nous allons commenter s´epar´ement.
int pos = -1;
for(int i=0; i < tab.size(); i++)
if (tab[i] == x)
pos = i;
return pos;
for (int i = v.size() - 1; i >= 0; i-- )
if ( v[i] == x )
return i;
(2) Prenons le mod`ele de calcul suivant :
Taille ndu probl`eme : la longueur du tableau ;
Op´erations ´el´ementaires : tests d’´egalit´es ==.
1
2 TD 10 : COMPLEXIT´
E
Dans le pire des cas, l’´el´ement ne se trouve pas dans le tableau et on le parcourt
donc int´egralement (dans les deux cas). Cela donne
n
tests d’´egalit´es. Il est par
contre tout `a fait possible que l’´el´ement cherce soit le dernier ´el´ement du tableau.
Dans ce cas on ne fait qu’un seul test dans le deuxi`eme programme. En revanche le
premier programme fait quand mˆeme ntests.
(3)
On remarque que pour le deuxi`eme programme le nombre de tests ==” d´epend de
la position de
x
dans
tab
. On peut donc se demander quelle est la complexit´e en
moyenne de l’algorithme pr´ec´edent. En faisant l’hypoth`ese que l’´el´ement
x
apparaˆıt
exactement une fois dans le tableau
tab
, en moyenne
x
est au milieu de
tab
, ce qui
donne une complexit´e en moyenne de n/2 pour le second programme et de npour
le premier.
Remarques :
Vous avez sans doute remarqu´e que ce dernier point est peu rigoureux. Il est tout a fait
possible de le rendre s´erieux en d´ecrivant une mesure de probabilit´e sur les diff´erents
tableaux, et sur les occurences de
x
. Puis il reste `a calculer pr´ecis´ement l’esp´erance
de la derni`ere position de
x
dans
tab
selon les pr´ec´edentes mesures. C’est cependant
technique et un peu hors de propos dans un TD d’introduction `a la complexit´e.
Le premier programme a une complexie en moyenne de n, alors que le second a
une complexit´e en moyenne de n/2. On peut aussi remarquer que le nombre de tests
effectu´es par le second algorithme est inf´erieur ou ´egal au nombre de tests effectu´es par
le premier. Le second algorithme est donc plus efficace que le premier.
Exercice 2.
On consid`ere les deux fragments de programmes suivants :
for (int i = 0; i < n; i++ )
for (int j = 0; j < n; j++ )
if ( i == j ) drawPixel(i, j, rouge);
for (int i = 0; i < n; i++ )
drawPixel(i, i, rouge);
Que font-ils ? Quelle est leur complexit´e ? Lequel est meilleur ?
Correction : Les deux programmes propos´es ont le mˆeme comportement : ils dessinent
chacun, point par point, la diagonale d’un carr´e de cˆot´e n. Pour la complexit´e, on compte
le nombre de fois que le test
if
est ex´ecut´e, ainsi que le nombre de pixels dessin´es. Dans le
premier cas le test est ins´er´e dans deux boucles
for
imbriqu´ees, chacune ayant npassages.
On effectue donc n
2
tests et on dessine npixels, soit une complexit´e en O(n
2
). Dans le
deuxi`eme cas, une seule boucle ayant npassages, donc la complexie est en O(n).
G´eom´etriquement, le premier programme parcourt le carr´e point par point, de gauche
`a droite, de haut en bas et si le point se situe sur la diagonale (coordonn´ees (i, i)), il est
affich´e en rouge. On voit que l’on parcourt donc les n2points du carr´e.
Le second programme ne visite que les points sur la diagonale (`a coordonn´ees (i, i)) et les
affiche directement.
Le second programme est ´evidement le meilleur. . .
Exercice 3.
On consid`ere deux algorithmes Aet Bde complexit´e respective 1000 log2n+ 50 et 4n.
(1) Tracer les deux courbes sur le mˆeme graphique, pour nentre 1 et 10000.
(2) Pour de petites valeurs de n, quel algorithme est le plus performant ?
(3) Pour de grandes valeurs de n, quel algorithme est le plus performant ?
TD 10 : COMPLEXIT´
E 3
(4) `
A partir de quelle valeur seuil faut-il passer de l’un `a l’autre ?
Correction : Asymptotiquement, le premier algorithme est bien plus efficace que le second.
Cependant pour de petites valeurs de nle second ira plus vite `a cause de la constante. Le
basculement se fait pour n= 2886 (cela peut se trouver par dichotomie en commen¸cant par
essayer des valeurs de nal´eatoires).
Table 1 : Courbes de A (en orange) et B (en vert).
Ce genre de comportement se retrouve tr`es souvent en pratique. Certains algorithmes
pourtant meilleurs asymptotiquement se r´ev`elent moins efficaces sur de “petites” valeurs.
Exercice 4.
Le graphique ci-dessous (figure 1) repr´esente les temps de calcul d’un ordinateur faisant un
milliard d’op´erations par seconde sur diff´erentes complexit´es.
(1)
En vous appuyant sur la figure 1, d´eterminer combien de temps il faudrait pour
ex´ecuter un algorithme de complexit´e n3sur une entr´ee de taille n= 1000 ?
(2) Mˆeme chose pour n= 106et n= 109.
(3)
On consid`ere un algorithme dont la complexit´e est de n
2
. En vous appuyant sur la
figure 1, ´evaluer l’ordre de grandeur de la taille maximale d’un probl`eme qui peut
ˆetre trait´e en moins d’une heure par un humain et par un ordinateur ?
(4) Mˆeme chose pour des algorithmes de complexit´e log n,n,n3, 2n?
(5) () Connaissez vous des algorithmes pour chacune de ces complexit´es ?
Exercice 5.
Quelle est la complexit´e des algorithmes d’addition et de multiplication tels que vous les
avez appris `a l’´ecole primaire ?
Correction :
(1)
L’algorithme d’addition classique pour additionner deux nombres aet bde respec-
tivement pet qchiffres consiste `a mettre asur une ligne, bsur une seconde ligne
et d’additionner successivement les chiffres en commen¸cant par les plus faibles. De
cette fa¸con, on transporte une ´eventuelle retenue. Sans compter les retenues, on
effectue donc
max
(p, q) additions chiffre `a chiffre. Si on rajoute les retenues on fait
au maximum 2
max
(p, q) additions de chiffres. En bref, l’algorithme est en O(p+q).
Si on note nla taille de l’entr´ee, c’est `a dire la m´emoire utilis´ee par l’entr´ee (a, b),
alors n=p+qvu qu’il faut pchiffres pour m´emoriser a, et qchiffres pour m´emoriser
b. En pratique, aest ´ecrit comme un nombre binaire `a pchiffres, de mˆeme pour b.
On peut donc ´ecrire que l’addition classique demande O(n) additions ´el´ementaires.
Ce qu’il faut retenir est qu’ici, la complexie est lin´eaire en fonction de la taille
de l’entr´ee.
4 TD 10 : COMPLEXIT´
E
Figure 1. Quelques courbes de complexit´e : en abscisse n, en ordonn´ee le
temps en secondes
(2)
Si on reprend l’algorithme classique de multiplication, comme pour l’addition on
´ecrit aet bsur deux lignes puis on somme le produit de tous les chiffres de bavec a.
Cela consiste donc `a faire au pire pmultiplications pour chaque chiffre de b, suivit
de qadditions de nombres `a p+qchiffres. Au final, on effectue donc pq +qO(p+q)
op´eration ´el´ementaires, soit un algorithme en O(pq). Comme pr´ec´edemment si on
note nla taille de l’entr´ee p+qalors cet algorithme de multiplication est de
complexit´e O(n2). On dit que l’algorithme est quadratique.
Pour aller plus loin : Il est possible d’obtenir des algorithmes beaucoup plus efficaces
asymptotiquement. Les plus c´el`ebres (et utilis´es en pratique) sont :
Algorithme de Karatsuba : d´ecoupage r´ecursif de la multiplication en (a+b)(c+d) =
ac +ad +bc +bd, que l’on calcule avec seulement 3 multiplications (au lieu de 4) et
quelques additions. D’o`u une complexit´e en O(n
log2(3)
)O(n
1.58
). Rentable d`es la
dizaine de chiffres.
FFT (Fast Fourier Transform) : Tr`es complexe, demande du calcul de flottants etc,
mais O(nlog nlog log n). Rentable `a partir de quelques dizaines de chiffres.
On peut faire encore un peu mieux mais c’est relativement anecdotique.
Exercice 6.
Rappeler les diff´erentes implantations de la fonction
Fibonacci
vues lors des TDs pr´ec´edents.
Quelle est, dans chaque cas, la complexit´e en temps et en emoire ?
Correction : Jusqu’ici nous avons vu trois algorithmes pour Fibonacci.
(1)
r´ecursif na¨
ıf : dessiner l’arbre des appels et remarquer que l’on calcule le ni`eme
nombre de Fibonacci en faisant +1 autant de fois que n´ecessaire. On effectue donc
F
n
appels r´ecursifs, soit une complexit´e en temps et en m´emoire de Θ(F
n
) ; ce qui
est particuli`erement mauvais (exponentiel). En fait, ce programme va calculer des
TD 10 : COMPLEXIT´
E 5
millions de fois la mˆeme chose, en oubliant constamment qu’il l’a d´ej`a fait, puis va
recommencer.
Par exemple, pour calculer F
50
= 102334155 il va falloir faire autant d’appels
r´ecursifs, pour autant de m´emoire (un peu plus de 8Go) ! Ce qui est compl`etement
idiot : c’est seulement 50 additions de “petits” nombres. Pas ´etonnant que ¸ca ramait
en TP.
Cet exemple est l’arch´etype mˆeme de la mauvaise implantation d’un programme.
Vous entendrez souvent parler de l’effet Fibonacci parfois dans un contexte diff´erent.
Ce qu’il faut retenir ici, c’est qu’il faut
toujours
bien r´efl´echir `a ce que fait votre
implantation. Corriger un tel effet est en g´en´eral tr`es facile (voir questions suivantes) :
vous n’avez d´esormais plus le droit de produire sciemment ce genre d’horreur.
(2)
it´eratif avec tableau : Θ(n) en temps et en m´emoire. Autant dire que le calcul de
F50 est instantan´e, pareil pour F43876.
(3) it´eratif avec 3 variables : Θ(n) en temps et Θ(1) en m´emoire.
Pour aller plus loin : il est possible de faire encore mieux en remarquant que
Fn+1
Fn=1 1
1 0Fn
Fn1.
Un algorithme d’exponentiation rapide (voir exercice 9, (5)) donne donc F
n
en temps
Θ(log n).
Sinon il est relativement ais´e (un bel exercice de maths) de d´er´ecursiver la d´efinition pour
obtenir la formule
Fn=1
5φn(1
φ)navec φ=1 + 5
2.
L`a aussi il faut savoir calculer des puissances. Cependant, pour utiliser cette derni`ere
formule il faut calculer avec des flottants, et donc ˆetre pr´ecis sur les multiples erreurs
d’approximations qui vont avoir lieu durant le d´eroulement du calcul. Il est possible de s’en
sortir en O(
log
n) mais le calcul en flottant rend cette derni`ere expression moins efficace
que la pr´ec´edente en pratique.
Exercice 7.
Pour chacun des probl`emes suivants, donner un algorithme, borner sa complexit´e, et essayer
de d´eterminer s’il est optimal ou non :
(1) Compter les entiers plus grand que 10 dans un tableau ;
(2) Ins´erer un ´el´ement au d´ebut d’un tableau ;
(3) Tester si un nombre est premier ;
(4) Rechercher un ´el´ement dans un tableau tri´e ;
(5) Compter les entiers plus grand que 10 dans un tableau tri´e ;
(6) Calculer l’intersection de deux ensembles repr´esent´es par des tableaux d’entiers ;
(7) Calculer la puissance n-i`eme d’un nombre ;
(8) () Calculer le pgcd de deux nombres ;
(9) () Calculer la n-i`eme d´ecimale de 2.
(10) () Calculer la n-i`eme d´ecimale de π.
Correction :
(1)
Il faut parcourir tout le tableau et augmenter un compteur chaque fois qu’on trouve
un ´el´ement plus grand que 10, ce qui donne un algorithme en Θ(n) pour nla taille
du tableau.
1 / 7 100%