O-notation 1. Introduction 2. O-notation 3. Opérations 3.1 Somme 3.2 Produit 4. Règles générales 5. Exemple 6.Analyse des algorithmes récursifs 6.1 Dilatation 6.2 Devinette 6.3 Utilisation d ’équations de récurrence 7. Analyse des algorithmes à "goto »
Introduction Le temps d ’exécution d'un programme dépend des facteurs suivants: - les données du programme - la qualité du code généré par le compilateur - la machine (vitesse et nature des instructions) - complexité de l'algorithme Le fait que le temps d' exécution dépend des données signifie que le temps d’exécution pourrait être défini comme une fonction des données ou comme une fonction de n. ( n étant la taille des données). Pour les problèmes de tri, il dépend du nombre d'éléments à trier. On représente le temps d ’exécution d'un programme avec n données par T(n)
Introduction Ex : T(n) = c n 2 c est une constante. ( T(n) pourrait être le nombre d'instructions exécutées dans un ordinateur) En pratique, le temps moyen est souvent difficile à déterminer.Nous utiliserons dans la mesure des algorithmes le temps dans le cas le plus défavorable et on mentionne le cas moyen quand on peut le donner. Dans l'exemple T(n) = c n 2 on peut dire que le temps d'execution est proportionnel à n 2. La constante c n'est pas spécifiée car elle dépend du compilateur, de la machine, ect...
O-notation ( exemple ) Quand on dit que le temps d'exécution d'un algorithme est O(n 2 ), cela veut dire qu'il existe une constante c > 0 et une constante n0 > 0 tel que pour tout n > n0 T(n) <= c n 2 Supposons que T(0) = 1, T(1) = 4 et en général T(n) = (n + 1) 2 Montrons que T(n) est O(n 2 )
O-notation ( exemple ) Il faut donc chercher une constante c > 0 et une constante n0 > 0 telles que Quelque soit n : n > 0 on ait T(n) = 0 Delta = 4c Pour c = 4, deux racines 1 et -1/3 Donc pour c = 4 et n0 = 1 on a T(n) = 0
Généralisation On dira que T(n) est O( f(n) )s'il existe c > 0 et n0 > 0 telles que T(n) = n0. un programme dont le temps d ’exécution est O( f(n) ) est un programme qui a f(n) comme taux de croissance. on dira aussi que f(n) est une limite supérieure du taux de croissance de T(n). Pour spécifier la limite inférieure du taux de croissance de T(n), on utilisera la notation Omega( g(n) ) qui veut dire: il existe une constante c > 0 telle que : T(n) >= c g(n) pour un nombre infini de valeur de n. Ex : T(n) = n 3 + 2n 2 est Omega(n3) car pour c = 1 T(n) >= n 3 pour n = 0, 1, 2,.....
Opérations Somme Si T1(n) = O ( f(n) ) et T2(n) = O( g(n) ) sont les temps d'exécution de 2 fragments de programme P1 et P2, alors le temps d'exécution de P1 suivi de P2 est T1(n) + T2(n) = O ( max (f(n), g(n) )
Opérations Somme Démonstration : T1(n) = O(f(n) ==> Il existe c1 > 0 et n1 > 0 telles que quelque soit n > n1 : T1(n) Il existe c2 > 0 et n2 > 0 telles que quelque soit n > n2 : T2(n) <= C2 g(n) T1(n) + T2(n) <= c1 f(n) + c2 g(n) pour un n0 = max ( n1, n2) <= (c1 + c2 ) max ( f(n), g(n) ) donc il existe un c = c1 + c2 et n0 = max(n1,n2)
Opérations Somme Exemple Cette règle peut être utilisée pour calculer le temps d'exécution d'une séquence d ’étapes d'un programme. Chaque étape peut être un fragment de programme avec des boucles et des branchements. Supposons que nous avons trois étapes dont les temps d ’exécution sont respectivement O(n 2 ), O(n 3 ) et O(nlogn). alors le temps d'exécution des deux premières étapes exécutées séquentiellement est O( max(n 2, n 3 ) ) = O (n 3 ). Le temps d'exécution des trois étapes est O( max(n 3, nlogn)) = O(n 3 ). En général, le temps d'exécution d'une séquence fixe d ’étapes et celui de l ’étape qui a le plus grand temps d'exécution.
Opérations Somme Observation : si g(n) n0 alors O( f(n) + g(n) ) = O ( f(n) ) Ex : O(n 2 + n ) = O(n 2 )
Opérations Produit Si T1(n) = O( f(n) ) et T2(n) = O(g(n)) alors T1(n) * T2(n) = O (f(n) * g(n) )
Opérations Produit Démonstration T1(n) = O(f(n) ==> Il existe c1 > 0 et n1 > 0 telles que quelque soit n > n1 : T1(n) Il existe c2 > 0 et n2 > 0 telles que quelque soit n > n2 : T2(n) = n0 avec n0 = max(n1, n2) Il existe donc un c = c1 * c2 et un n0= max(n1, n2) tels que T1(n) * T2(n) <= c f(n) g(n). Donc T1(n)*T2(n)=O(f(n)g(n)).
Opérations Produit Conséquences : on peut facilement montrer que : O( c f(n) ) = O( f(n) ) si c > 0. ex O(n 2 /2) = O(n 2 )
Règles générales 1.Le temps d'exécution de chaque affectation, lecture ou écriture est O(1) 2. Le temps d ’exécution d'une séquence d'instruction est déterminée par la règle de la somme. C'est donc le temps de la séquence qui a le plus grand temps d ’exécution. 3. Le temps d'exécution d'une instruction IF est le temps d ’exécution des instructions exécutées sous condition, plus le temps pour évaluer la condition. Ce dernier est O(1). Pour une alternative, on se place dans le cas le plus défavorable. 4. Le temps d ’exécution d'une boucle est la somme du temps pour évaluer le corps et du temps pour évaluer la condition. ce dernier prend O(1). Souvent ce temps est le produit du nombre d'itérations de la boucle par le plus grand temps possible pour une exécution du corps. Quelquefois le nombre d'itérations n'est pas connu précisément. Ce qui rend difficile sinon impossible la détermination du temps.
Exemple Procedure bubble ( var A : array(1..N) of integer) var i, j, temp : integer; begin (1) for i:= 1 to n-1 do (2) for j := n downto i+1 do (3) if A(j-1) > A(j) then begin (4) temps := A(j-1) (5) A(j-1) := A(j) (6) A(j) := temp end; end; Soit n le nombre de données à trier.
Exemple Chaque instruction d'affectation prend une valeur constante du temps indépendante de n. Donc les instructions (4) (5) et (6) occupent chacun O(1). ( C'est a dire il existe c > 0 et n0 > 0 telles que quelque soit n > n0 T(n) <= c.1 ; en d'autres termes on peut toujours trouver une constante c telle que le temps d'exécution de l'affectation soit inférieur à c) D'après la règle de la somme le temps d'exécution de (4) (5) et (6) est O( max(1, 1, 1) ) = O(1).
Exemple Considérons maintenant les instructions conditionnelle et répétitives en allant du niveau le plus interne vers le niveau le plus externe. Pour l'instruction IF, le test de la condition exige O(1). L'exécution des 3 affectations dépend de la valeur du test. puisque nous recherchons le temps d'exécution dans le cas le plus défavorable, donc l'instruction IF prend aussi O(1).
Exemple Analysons maintenant la boucle (2) à (6). La règle générale pour une boucle est que le temps d'exécution est la somme du temps dépensé par l'exécution du corps de la boucle pour chaque itération. le corps de la boucle prend O(1) pour chaque itération (incrémention de l'index, test des limites, branchement vers le début de la boucle). Le nombre d'itérations est n-i donc d'après la règle du produit: corps : O(1) boucle : O(n-i) Le temps dépensé de (2) a (6) est O( (n-i)*1 ) = O (n-i).
Exemple Analysons la boucle la plus externe qui contient toutes les instructions exécutables du programme. L'instruction 1 est exécutée n-1 fois. Donc le temps total d'exécution du programme est limite par Somme des (n-i) pour i=1, n-1 = n(n-1)/2= n 2 -n/2 qui est O(n 2 ).
Exemple Autre façon de procéder : Dans la boucle (1) (6) corps : O(n-i) ou O(n) (résultat précédent) boucle : O(n-1) ou O(n) Règle du produit : O(n.n) = O(n 2 )
Analyse des algorithmes récursifs Considérons les deux exemples suivants : Ex1 : Fact(n) : Si n <= 1 : Fact := 1 Sinon Fact := n * Fact(n-1) Fsi Dans l'exemple 1, on peut exprimer T(n) par T(n) = d si n <= 1 c + T(n-1) sinon
Analyse des algorithmes récursifs Ex2 : tri d'une liste L de n éléments avec n = 2 k. Tri(L, n) Si n = 1 : Tri := L Sinon L1 := L [1..n/2] L2 := L [n/2+1..n] Tri := Fusion( Tri(L1, n/2), tri(L2, n/2)) Fsi Dans l'exemple 2, on peut exprimer T(n) par : T(n)= a si n=1 2 T(n/2) + bn Sinon C'est ce qu'on appelle les équations de récurrence.
Analyse des algorithmes récursifs Pour mesurer un algorithme récursif, il faut d'abord déterminer son équation de récurrence, puis résoudre des équations de récurrence. Il existe en général 3 Méthodes : a) Par substitution de proche en proche.(dilater la récurrence) b) Deviner une solution f(n) et la démontrer par récurrence. c) Utiliser la solution de certaines équations de récurrence connues.
Analyse des algorithmes récursifs Dilatation Ex1 : Fact(n) : Si n <= 1 : Fact := 1 Sinon Fact := n * Fact(n-1) Fsi T(n) = d si n <= 1 c + T(n-1) sinon T(n)=c + T(n-1) =c + ( c + T(n-2)) =2c + T(n-2) =2c + (c + T(n-3))= 3c + T(n-3) =... =(i-c) + T(n-i) =... = (n-1)c + T(1) =nc + d - c C'est O(n).
Analyse des algorithmes récursifs Dilatation Ex2 : tri d'une liste L de n éléments avec n = 2 k. T(n)= 2 T(n/2) + bn =2 [2 T(n/4) + bn/2] + bn = 4 T[n/4] + 2 bn =8 T[n/8] + 3 bn =... =2 i + T[n/2 i ] + ibn =... =n T[1] + Log(n) bn =an + Log(n) bn = n( a + bLog(n)) C'est O(n Log(n))
Analyse des algorithmes récursifs Devinette Prenons le tri par fusion. On suppose que T(n) = anLog(n) + b,a et b donnés. Si n = 1, T(1) = b On suppose T(k) = 2, de (1) on obtient T(n)<=2T(n/2) + C2n <=2( a n/2Log(n/2) + b) + c2n <=anLog(n) - anLog2 + 2b + c2n <=anLog(n) - an + 2b + c2n <=anLog(n) + b + b + c2n - an
Analyse des algorithmes récursifs Devinette Pour que T(n) = b + c2n a >= (b + c2n)/n pour tout n >=1 a >= b + c2 Donc T(n) = = c1 a >= b + c2 En choisissant b = c1 et a = c1 + c2, on conclut que pour tout n >1 T(n) <= (c1 + c2) nLog(n) + c1 en d'autres termes T(n) est O( n Log(n) ).
Analyse des algorithmes récursifs Utilisation de certaines équations de récurrences connues. 1. Équations homogènes a 0 t n + a 1 t n a k t n-k = 0 (1) admet comme équation caractéristiques a0 x k + a1 x k a k = 0 dont les solutions sont r1, r2,....rk. Si toutes les solutions ri sont distinctes alors la solution de (1) est donnée par : t n = c 1 (r 1 ) n + c 2 (r 2 ) n c k (r k ) n Si rj est une solution multiple (de multiplicité m), alors la solution est donnée par : t n = c 1 (r 1 ) n + c 2 (r 2 ) n (c j1 (r j ) n + c j2 n (r j ) n + c j3 n 2 (r j ) n c jm n m-1 (r j ) n ) ck(r k )n. Remarque : les constantes cj sont déterminées par les conditions initiales.
Analyse des algorithmes récursifs Utilisation de certaines équations de récurrences connues. Exemples : a) Soit t n - 3t n-1 - 4t n-2 = 0 pour n >= 2 t 0 = 0, t 1 = 1 admet comme équation caractéristique : x 2 - 3x - 4 = 0 dont les solutions sont -1 et 4. Donc t n = c 1 (-1) n + c 2 4 n Comme t0 = 0 et t1 = 1, alors c1 = -1/5 et c2 = -1/5 c'est O(4 n ).
Analyse des algorithmes récursifs Utilisation de certaines équations de récurrences connues. Exemples : b) t n = 5 t n t n t n-3 pour n >= 3 t 0 = 0, t 1 = 1, t 2 = 2. admet comme équation caractéristique : x 3 - 5x 2 + 8x - 4 = 0 c'est équivalent à (x-1) (x-2) 2 = 0 solutions : 1 (multiplicité 1) et 2 ( multiplicité 2) Donc t n = c 1 1 n + c 2 2 n + c 3.n 2 n Les conditions initiales donnent c1=-2, c2=2, c3=-1/2.
Analyse des algorithmes récursifs Utilisation de certaines équations de récurrences connues. 2. Équations non homogènes: a 0 t n + a 1 t n a k t n-k = b 1 n P1(n) + b 2 n P2(n)+.... où les b i sont des constantes différentes et Pi des polynômes de degré di. Admet comme équation caractéristique (a 0 x k + a 1 x k a k ) (x-b 1 ) d1+1 (x-b 2 ) d2+1.. = 0
Analyse des algorithmes récursifs Utilisation de certaines équations de récurrences connues. Exemple : t n - 2t n-1 = n + 2 n t 0 = 0 ==> b1 = 1, P1 = n; b2 = 2, P2 = 1 équation caractéristique : (x-2) ( x-1) 2 (x-2) = 0 Donc t n = c 1 1 n + c 2 n 1 n + c 3 2 n + c 4 n 2 n
Analyse des algorithmes avec GOTO. Il est difficile sinon impossible de mesurer les algorithmes à goto. Ceci est du au chevauchement de boucle qu'ils peuvent renfermer