Telechargé par pierre.kelbert

NotationO

publicité
La notation asymptotique
François Lemieux
Hiver 2007
Nous avons vu que le temps d’exécution d’un algorithme A pouvait être
exprimé comme une fonction T : N→R+ telle que T (n) représente le temps
maximal que prend A sur une entrée de longueur n. Si nous voulons pouvoir comparer les temps d’exécution de plusieurs algorithmes, il est d’abord
nécessaire de savoir comparer les fonctions entre elles. Afin d’atteindre cet
objectif, nous allons définir une notation qui sera d’une très grande utilisé
par la suite.
1
La notation “grand O”
La notation grand O sert à exprimer le fait que l’ordre de grandeur d’une
fonction est inférieur ou égal à une autre.
Définition 1 Soit f : N→R+ une fonction positive. On défini l’ordre de
f (n) comme l’ensemble:
O(f ) = {g : N→R+ | (∃c > 0)(∃n0 ≥ 0)(∀n ≥ n0 )[g(n) ≤ cf (n)]}
Pour montrer qu’une fonction f (n) est dans l’ordre d’une autre fonction
g(n), on doit donc trouver deux constantes c et n0 telles que f (n) ≤ cg(n)
est vrai sauf, possiblement, pour des petites valeurs inférieures à n0 .
Exemple. Pour montrer que n2 ∈ O(5n2 + 3n + 4) on doit trouver deux
constantes c > 0 et n0 ≥ 0 tels que n2 ≤ 5n2 + 3n + 4. Pour ce faire, on
1
observe que puisque 3n + 4 > 0 alors n2 ≤ 5n2 + 3n + 4 quelque soit la valeur
de n ≥ 0. Il suffit donc de prendre n0 = 0 et c = 1.
Exemple. Montrons qu’on a aussi 5n2 + 3n + 4 ∈ O(n2 ). On a
5n2 + 3n + 4 ≤ 5n2 + 3n2 + 4n2 = 12n2
L’inégalité se vérifiant pour tout n ≥ 1. Donc, si on pose c = 12 et n0 = 1
on obtient
(∀n ≥ n0 )[5n2 + 3n + 4 ≤ cn2 ]
Ce qui prouve que 5n2 + 3n + 4 ∈ O(n2 ).
Les deux exemples précédent peuvent facilement être généralisés:
Lemme 1 Soit p(n) un polynôme de degré d ≥ 0. Alors p(n) ∈ O(nd ) et
nd ∈ O(p(n)).
Preuve. Montrons d’abord que p(n) ∈ O(nd ). On a p(n) =
a0 , a1 , . . . ad sont des constantes. On peut donc écrire:
p(n) =
d
X
ai ni ≤
i=0
d
X
|ai |ni ≤
i=0
d
X
Pd
i=0
ai ni où
|ai |nd
i=0
La dernière
inégalité étant vrai pour tous les entiers n ≥ 1. Si on pose
Pd
c = i=0 |ai | et n0 = 1 on obtient que p(n) ≤ cnd pour tout n ≥ n0 . Cela
démontre que:
(∃c > 0)(∃n0 ≥ 0)(∀n ≥ n0 )[p(n) ≤ cnd ]
et qu’ainsi p(n) ∈ O(nd ).
Il reste à montrer que nd ∈ O(p(n)). Nous devons trouver deux constantes
c > 0 et n0 ≥ 0 telles que pour tout n ≥ n0 l’inégalité nd ≤ cp(n) est vérifiée.
En fait, il est plus simple de montrer que p(n) ≥ nd /c. On a:
p(n) =
d
X
i=0
i
d
ai n = ad n +
d−1
X
i
d
ai n ≥ ad n −
i=0
d−1
X
i=0
2
i
d
|ai |n ≥ ad n − n
d−1
d−1
X
i=0
|ai |
La dernièrePinégalité est vrai pour tout n ≥ 1. Finalement, pour tout n ≥
max {1, a2d d−1
i=0 |ai |}, on a aussi:
d
ad n − n
d−1
d−1
X
|ai | ≥ ad nd −
i=0
En prenant c =
2
ad
et n0 = max {1, a2d
ad d ad d
n = n
2
2
Pd−1
i=0
|ai |} on obtient:
(∀n ≥ n0 )[nd ≤ c · p(n)]
Ce qui démontre que nd ∈ O(p(n)).
Le lemme suivant est très utile et nous permettra, entre autres, de démontrer
que si p(n) et q(n) sont deux polynômes de même degré alors O(p(n)) =
O(q(n)). Pour cette raison on ne fera aucune différence entre deux polynômes
de même degré. Plus généralement, si f, g : N→R+ sont deux fonctions telles
que O(f ) = O(g) alors on ne fera aucune distinction entre f et g.
Lemme 2 Soit f, g, h : N→R+ trois fonctions positives. Si f ∈ O(g) et
g ∈ O(h) alors f ∈ O(h).
Preuve. Puisque f ∈ O(g) et g ∈ O(h) alors on a:
1. (∃c1 > 0)(∃n1 ≥ 0)(∀n ≥ n1 )[f (n) ≤ c1 g(n)]
2. (∃c2 > 0)(∃n2 ≥ 0)(∀n ≥ n2 )[g(n) ≤ c2 h(n)]
Puisque le premier énoncé est vrai pour tout n ≥ n1 et que le second est
vrai pour tout n ≥ n2 alors les deux énoncés sont vrais pour tout n ≥
max{n1 , n2 }. On obtient donc que pour tout n ≥ max{n1 , n2 }:
f (n) ≤ c1 g(n) ≤ c1 c2 h(n)
Si on pose n0 = max{n1 , n2 } et c = c1 c2 , on obtient:
(∃c > 0)(∃n0 ≥ 0)(∀n ≥ n0 )[f (n) ≤ ch(n)]
On conclu que f (n) ∈ O(h(n)).
3
Corollaire 1 Soit f, g : N→R+ . Si f ∈ O(g) alors O(f ) ⊆ O(g).
Preuve. Supposons que f ∈ O(g). Pour montrer que O(f ) ⊆ O(g) il faut
montrer que pour tout h ∈ O(f ) on a h ∈ O(g), ce qui est vrai par le Lemme
2.
Exemple. Poursuivant avec l’exemple précédent, le lemme 1 nous indique
que 5n2 − 3n − 4 ∈ O(n2 ) et n2 ∈ O(5n2 − 3n − 4). Utilisant le corollaire 1
nous concluons que O(5n2 − 3n − 4) = O(n2 ).
Proposition 2 Soit p(n) et q(n) deux polynômes de degré d ≥ 0. Alors
O(p(n)) = O(q(n)).
Preuve. Par le lemme 1 nous savons que p(n) ∈ O(nd ) et que nd ∈ O(q(n)).
Par le lemme 2, on a p(n) ∈ O(q(n)). Finalement, par le corollaire 1, on a
O(p(n)) ⊆ O(q(n)). Similairement, on démontre que O(q(n)) ⊆ O(p(n)) ce
qui prouve la proposition.
Tout comme on ne fera aucune différence entre deux polynômes de même
degré, on ne fera aucune distinction entre deux fonctions logarithmiques utilisant des bases différentes.
Proposition 3 Pour toutes valeurs a, b > 0 on a O(loga n) = O(logb n).
Preuve. Nous devons montrer que:
1. O(loga n) ⊆ O(logb n)
2. O(logb n) ⊆ O(loga n)
Seule la démonstration de la première inclusion est nécessaire: la preuve de
la seconde étant identique. En fait, par le corollaire 1, il suffit de montrer
que loga n ∈ O(logb n). On sait que:
loga n =
Donc, en choisissant c =
1
logb a
logb n
logb a
et n0 = 1 on a:
(∀n ≥ n0 )[loga n ≤ c logb n]
4
Ainsi, on a bien loga n ∈ O(logb n).
Remarque: Il faut faire attention de ne pas tirer de fausses conclusions de
la proposition précédentente. Par exemple si a < b on a 2logb n ⊆ O(2loga n )
mais O(2loga n ) 6= O(2logb n ).
2
Analyse asymptotique
Exemple. Considérez le segment de code C suivant:
x=0;
for (i=1; i<=n; i++)
x++;
for (j=1; j<=n; j=j*2)
x++;
Le tableau suivant analyse le temps d’exécution de chacune de ces cinq
lignes de code.
ligne
1
2
3
4
5
temps/exécution
c1
c2
c3
c4
c5
nb exécutions
1
n+1
n
blg nc + 1
blg nc
ordre
O(1)
O(n)
O(n)
O(lg n)
O(lg n)
On suppose qu’une seule exécution de la ligne i nécessite un temps constant inconnu que nous dénotons ci (i = 1, 2, 3, 4, 5). Le nombre de fois que
chacune des lignes est exécutée est indiqué dans la troisième colonne. La
dernière colonne donne l’ordre du temps total d’exécution pour chacune des
lignes. Le temps total d’exécution du segment est:
T (n) =
=
=
∈
c1 + c2 (n + 1) + c3 n + c4 (blg nc + 1) + c5 blg nc
(c1 + c2 + c4 ) + (c2 + c3 )n + (c4 + c5 )blg nc
d1 + d2 n + d3 blg nc
O(n)
5
Cet exemple démontre que l’estimation du temps d’exécution de chacune
des lignes se fait beaucoup plus simplement si l’on utilise la notation assymptotique. Cependant, cette méthode ne sera utile que si elle permet de
calculer plus facilement le temps total d’exécution T (n) du segment de code.
Par exemple, il serait utile de pouvoir écrire:
T (n) = O(1) + O(n) + O(n) + O(lg n) + O(lg n)
= O(1 + n + n + lg n + lg n)
= O(n)
Intuitivement, ce type d’arithmétique semble correct sauf que O(n) et
O(lg n) sont des ensembles et que l’addition d’ensembles n’est pas définie.
Ce problème peut facilement être corrigé lorsqu’il est question, comme ici,
d’ensembles de fonctions:
Définition 2 Soit f, g : N→R+ .
(a) O(f ) + O(g) = {h : N→R+ | ∃h1 ∈ O(f ), ∃h2 ∈ O(g), h(n) = h1 (n) +
h2 (n)}
(b) O(f )O(g) = {h : N→R+ | ∃h1 ∈ O(f ), ∃h2 ∈ O(g), h(n) = h1 (n)h2 (n)}
La définition du produit de deux ensembles de fonctions est justifiée par
l’exemple suivant:
Exemple. Considérez le segment de code C suivant:
x=0;
for (i=1; i<=n/2; i++)
for (j=1; j<=n; j=j*2)
x++;
L’analyse de ce code est effectuée dans le tableau suivant:
ligne
1
2
3
4
temps/exécution
c1
c2
c3
c4
nb exécutions
1
bn/2c + 1
bn/2c(blg nc + 1)
bn/2c(blg nc)
6
ordre
O(1)
O(n)
O(n)O(lg n)
O(n)O(lg n)
Si l’on considère l’ordre du nombre d’exécutions de la dernière ligne, on
observe que la première instruction for effectue O(n) itérations et qu’à chacune d’elles la seconde instruction for effectue O(lg n) itérations. Il est donc
naturel d’exprimer le nombre total d’exécutions de la dernière ligne sous la
forme O(n)O(lg n).
Même après avoir défini l’addition et le produit d’ensembles de fonctions
il reste à se convaincre que l’arithmétique des ensembles que nous voulons
utiliser est cohérente et qu’elle fonctionne dans toutes les situations. C’est
ce qui résulte des deux lemmes suivants:
Lemme 3 Soit f, g : N→R+ , deux fonctions positives.
(a) O(f ) + O(g) = O(f + g)
(b) O(f )O(g) = O(f g)
Preuve. Nous allons seulement démontrer (b) puisque la preuve de (a) est
similaire. Montrons d’abord que O(f )O(g) ⊆ O(f g). Soit h ∈ O(f )O(g).
Alors il existe deux fonctions h1 ∈ O(f ) et h2 ∈ O(g) telles que h(n) =
h1 (n)h2 (n). On a alors
1. (∃c1 > 0)(∃n1 ≥ 0)(∀n ≥ n1 )[h1 (n) ≤ c1 f (n)}]
2. (∃c2 > 0)(∃n2 ≥ 0)(∀n ≥ n2 )[h2 (n) ≤ c2 g(n)}]
Donc, pour tout n ≥ max {n1 , n2 } on a :
h(n) = h1 (n)h2 (n) ≤ c1 c2 f (n)g(n)
Ce qui montre que h(n) ∈ O(f g).
Montrons maintenant que O(f g) ⊆ O(f )O(g). Soit h(n) ∈ O(f (n)g(n)).
On a alors deux constantes c > 0 et n0 ≥ 0 telles que h(n) ≤ cf (n)g(n)
pour tout n ≥ n0 . Définissons deux fonctions h1 (n) et h2 (n) telles que
h1 (n) = f (n) et
½
h(n)/f (n) si f (n) 6= 0
h2 (n) =
0
sinon
On a donc h(n) = h1 (n)h2 (n) et h1 (n) ∈ O(f (n)). On a aussi h2 (n) ∈
O(g(n)) puisque:
h(n) = h1 (n)h2 (n) = f (n)h2 (n) ≤ cf (n)g(n) =⇒ h2 (n) ≤ cg(n)
7
On conclu que h(n) ∈ O(f (n))O(g(n)).
Le prochain lemme sera utile pour simplifier les fonctions exprimant le
temps d’exécution d’un algorithme (ou son espace mémoire).
Lemme 4 Si f (n) ∈ O(g(n)) alors O(f (n) + g(n)) = O(g(n))
Exemple. Poursuivant avec le dernier exemple, le temps total du segment
de code est:
T (n) = O(1) + O(n) + O(n)O(lg n) + O(n)O(lg n)
= O(1 + n + 2n lg n)
= O(n lg n)
3
Les autres notations
Intuitivement, on utilise la relation grand O entre les fonctions un peu comme
on utilise la relation ≤ avec les entiers. On peut aussi définir quatre autres
relations dont le rôle pour les fonctions est similaire aux relations d’entiers:
≥, =, < et >.
Définition 3 Soit f : N→R+ .
1. Ω(f (n)) = {g : N→R+ | f (n) ∈ O(g(n))}
2. Θ(f (n)) = {g : N→R+ | O(g(n)) = O(f (n))}
3. o(f (n)) = O(f (n) − Θ(f (n))
4. ω(f (n)) = Ω(f (n)) − Θ(f (n))
Exemple. Soit p1 (n) et p2 (n) deux polynômes de degré d1 > 0 et d2 > 0
respectivement. Si d1 = d2 alors p1 (n) ∈ Θ(p2 (n)) mais si d1 < d2 alors
p1 (n) ∈ o(p2 (n)).
8
Lemme 5 Soit f, g : N→R+ .
1. f (n) ∈ O(g(n)) si et seulement si g(n) ∈ Ω(f (n)).
2. f (n) ∈ o(g(n)) si et seulement si g(n) ∈ ω(f (n)).
3. f (n) ∈ Θ(g(n)) si et seulement si f (n) ∈ O(g(n)) ∩ Ω(g(n)).
Theorem 4 Soit 0 < c < ∞.
f (n)
n→∞ g(n)
= 0 alors f (n) ∈ o(g(n))
f (n)
n→∞ g(n)
= ∞ alors f (n) ∈ ω(g(n))
f (n)
n→∞ g(n)
= c alors f (n) ∈ Θ(g(n))
1. Si lim
2. Si lim
3. Si lim
Preuve. Nous allons simplement démontrer la première partie.
(n)
Si lim fg(n)
= 0 alors f (n) ∈ O(g(n). Il faut donc montrer que g(n) 6∈
n→∞
O(f (n)). On a:
(n)
(∀² > 0)(∃n² ≥ 0)(∀n ≥ n² )[| fg(n)
| ≤ ²]
=⇒ (∀² > 0)(∃n² ≥ 0)(∀n ≥ n² )[f (n) ≤ ²g(n)]
=⇒ (∀² > 0)(∀n0 ≥ 0)(∃n ≥ n0 )[f (n) ≤ ²g(n)]
La dernière implication provient du fait que si f (n) ≤ ²g(n) est vrai pour
tout n ≥ n² alors cela est vrai pour une infinité de valeur n. Cela implique
que quelque soit la valeur n0 on peut toujours touver un n encore plus grand
tel que f (n) ≤ ²g(n) est vrai.
La dernière expression est équivalente à la négation de:
(∃c > 0)(∃n0 ≥ 0)(∀n ≥ n0 )[g(n) ≤ cf (n)], où c = 1/²
Ce qui démontre que g(n) 6∈ O(f (n)).
9
4
Les limites
Un outil très utile pour démontrer qu’une fonction est dans l’ordre d’une
autre fonction consiste à utiliser les limites. Rappelons d’abord la définition.
Définition 4 La limite d’une fonction h(n) est défini comme suit. On a:
lim f (n) = c
n→∞
si et seulement si
(∀² > 0)(∃n² ≥ 0)(∀n ≥ n² )[|f (n) − c| ≤ ²]
En d’autres mots, f (n) se rapproche de plus en plus de c à mesure que n
augmente.
Exemple. Considérons la fonction h(n) = 1/n. Cette fonction se rapproche
de plus en plus près de 0 à mesure que n augmente. On a donc
1
=0
n→∞ n
lim
f (n)
n→∞ g(n)
Lemme 6 Soit f, g : N→R+ et c ≥ 0. Si lim
f (n)
n→∞ g(n)
Preuve. Si lim
= c alors f (n) ∈ O(g(n)).
= c alors on a:
(n)
(∀² > 0)(∃n² ≥ 0)(∀n ≥ n² )[| fg(n)
− c| ≤ ²]
(n)
=⇒ (∀² > 0)(∃n² ≥ 0)(∀n ≥ n² )[ fg(n)
≤ ² + c]
=⇒ (∀² > 0)(∃n² ≥ 0)(∀n ≥ n² )[f (n) ≤ (² + c)g(n)]
=⇒ (∃d > 0)(∃n² ≥ 0)(∀n ≥ n² )[f (n) ≤ dg(n)]
=⇒ f (n) ∈ O(g(n))
Exemple. On peut vérifier que n ∈ O(n2 ) en utilisant le lemme précédent.
Bien sur on a:
1
n
lim 2 = lim = 0
n→∞ n
n→∞ n
10
Lorsque f, g : N→R+ sont deux fonctions dérivables qui tendent vers
l’infini lorsque n augmente, on peut alors appliquer la règle de l’Hospital:
f (n)
f 0 (n)
= lim 0
n→∞ g(n)
n→∞ g (n)
lim
Exemple. Montrons que ln n ∈ O(n). Par le lemme 6, il suffit de montrer
que lim lnnn = 0. En appliquant la règle de l’Hospital nous obtenons:
n→∞
lim
n→∞
ln n
1/(n)
1
= lim
= lim = 0
n→∞
n→∞
n
1
n
Cet exemple est généralisé par la proposition suivante:
Proposition 5 Pour tout ² > 0 et pour tout a > 0 on a loga n ∈ O(n² ).
Preuve. Par le lemme 6, il suffit de montrer que lim logna² n = 0. En applin→∞
quant la règle de l’Hospital nous obtenons:
lim
n→∞
loga n
1/(n ln a)
1
= lim
= lim
=0
²
²−1
n→∞
n→∞
n
²n
²(ln a)n²
Exemple. Montrons que n1.5 ∈ O(2n ). En appliquant deux fois la règle de
l’Hospital on obtient:
n1.5
(1.5)n0.5
(0.5)(1.5)n−0.5
=
lim
=
lim
n→∞ 2n
n→∞ (ln 2)2n
n→∞
(ln 2)2 2n
lim
On a donc
(0.5)(1.5)n−0.5
(0.75)
√ =0
=
lim
n→∞
n→∞ (ln 2)2 2n n
(ln 2)2 2n
lim
Cet exemple peut être généralisé par la proposition suivante.
11
Proposition 6 Soit d ≥ 0 et r > 1 deux constantes réelles. Alors nd ∈
O(rn ).
Preuve. Soit k = dde. Si on dérive k fois la fonction f (n) = nd , on
obtient la fonction f (k) (n) = d(d − 1) · · · (d − k + 1)nd−k où d − k ≤ 0. Si on
dérive k fois la fonction g(n) = rn , on obtient la fonction g (k) (n) = (ln r)k an .
Donc, en appliquant k fois la règle de l’Hospital on obtient:
f (n)
f (k) (n)
d(d − 1) · · · (d − k + 1)nd−k
= lim (k)
= lim
n→∞ g(n)
n→∞ g
(n) n→∞
(ln r)k an
lim
d(d − 1) · · · (d − k + 1)
=0
n→∞
(ln r)k an nk−d
= lim
12
Téléchargement