PTSI – IC ALGORITHMIQUE
I. Terminaison et preuve d’algorithmes
1) Terminaison : variant de boucle
On considère l’algorithme suivant :
Entrées : nN
Sorties : r=n!
r1
kn
tant que k>0faire
rr×k
kk1
On est sûr que ce programme va se terminer car :
kest un entier positif.
La valeur de kdiminue strictement à chaque itération.
Or, il n’existe pas de suite d’entiers naturels strictement décroissante infinie : il ne peut donc y avoir qu’un nombre fini
d’itérations.
Plus généralement, pour prouver qu’une boucle va se terminer, on introduit une quantité qui vérifie les deux propriétés :
être un entier naturel et décroître strictement à chaque itération. Cette quantité s’appelle un variant de boucle.
2) Preuve : invariant de boucle
Une preuve d’algorithme par invariant de boucle utilise la démarche suivante :
On prouve tout d’abord que l’algorithme s’arrête, par exemple en utilisant un variant de boucle;
On exhibe un invariant de boucle, c’est-à-dire une propriété P qui, si elle est vraie avant l’exécution d’un tour de
boucle, est aussi valide après l’exécution du tour de boucle;
On vérifie que les conditions initiales rendent la propriété P vraie en entrée du premier tour de boucle;
On en conclue que cette propriété est vraie en sortie du dernier tour de boucle.
Un bon choix de la propriété P prouvera qu’on a bien produit l’objet recherché. La difficulté de cette méthode réside dans
la détermination de la propriété P. Quand on l’a trouvé il est en général simple de montrer que c’est bien un invariant de
boucle.
3) Exemples
a) Calcul de n!
On a déjà prouvé que l’algorithme du 1) se termine.
Notons riet kiles valeurs de ret kaprès la i-ème itération, et P(i) la propriété ri×ki!=n!.
Initialisation : r0=1 et k0=nd’où r0×k0!=n! donc P(0) est vraie.
Hérédité : Supposons que P(i) est vraie pour un certain entier iet que l’on effectue une itération de plus.
Alors ri+1=ri×kiet ki+1=ki1 d’où ri+1×ki+1!=ri×ki×(ki1)! =ri×ki=n! par P(i). P(i+1) est donc vraie.
Conclusion : Après le dernier tour de boucle, ki=0 et comme ri×ki!=n!, on en déduit que ri=n!.
b) Calcul de xn
On considère l’algorithme « naïf » de calcul de puissance suivant :
Entrées : xRet nN
Sorties : r=xn
r1
tant que n>0faire
rr×x
nn1
Démontrer que cet algorithme se termine et est correct.
1/5 DELAY – Paul Constans – 2016 - 2017
PTSI – IC ALGORITHMIQUE
c) Exponentiation rapide
Pour accélérer le calcul de xn(où xRet nN), on se propose d’exploiter les identités (x2n=(x2)n
x2n+1=x(x2)nqui montrent
comment calculer une puissance en remplaçant xpar son carré et l’exposant par sa moitié. L’algorithme est le suivant :
Entrées : xRet nN
Sorties : r=xn
r1
tant que n>0faire
si nest impair alors
rr×x
xx×x
n¥n
2¦
1. Établir la terminaison de cet algorithme.
2. Démontrer que cet algorithme est correct.
3. Cet algorithme est dit d’exponentiation rapide. Pour comprendre pourquoi, évaluer combien de multiplications il
effectue et comparer avec la version naïve du b).
d) Division Euclidienne
Démontrer que l’algorithme suivant se termine, et est correct :
Entrées : aet bentiers naturels, b6=0
Sorties : le quotient qet le reste rde la division euclidienne de apar b
q0
ra
tant que rÊbfaire
rrb
qq+1
e) Algorithme d’Euclide et théorème de Bézout
Démontrer que l’algorithme suivant se termine, et est correct :
Entrées : x,ydeux entiers naturels non nuls.
Sorties : d=PGCD(x,y) et (m,n)Z2tels que mx +ny =d.
ax;by
m1; n0
k0; l1
tant que a6=bfaire
si a>balors
aab
mmk
nnl
sinon
bba
kkm
lln
da
II. Complexité d’un algorithme
1) Introduction
Il existe souvent plusieurs algorithmes différents pour résoudre un même problème, qui peuvent être plus ou moins
rapide, et demander plus ou moins de place en mémoire. Il est alors intéressant de savoir choisir l’algorithme le plus
2/5 DELAY – Paul Constans – 2016 - 2017
PTSI – IC ALGORITHMIQUE
rapide, ou le moins gourmand en mémoire, selon les contraintes que l’on a.
Par exemple, pour déterminer l’ensemble des diviseurs d’un nombre, on peut commencer par l’algorithme naïf :
Entrées : Un entier n
Sorties : Affichage de tous les diviseurs de n
pour pvariant de 1ànfaire
si pdivise nalors Afficher p
Cet algorithme fait ntests de divisibilité et au maximum naffichages. Mais on peut nettement améliorer cet algorithme
en remarquant qu’à chaque fois qu’on trouve un diviseur pde n, alors q=n/pest aussi un diviseur de n. On obtient
l’algorithme suivant :
pour pvariant de 1àpnfaire
si pdivise nalors
Afficher p
qn/p
si q6= palors Afficher q
Cet algorithme demande un calcul de racine carrée, puis réalise pnitérations avec dans chacune 1 test de divisibilité,
entre 0 et 2 affichages, 0 ou 1 division, 0 ou 1 affectation et 0 ou 1 test.
En général, on cherche à savoir comment le temps d’exécution, ou la place occupée en mémoire, d’un algorithme évolue
en fonction d’un paramètre que l’on appelle la taille du problème. Dans l’exemple précédent, il parait naturel de prendre
ncomme taille du problème. On voit alors que le temps d’exécution est proportionnel à ndans le premier algorithme,
alors qu’il est proportionnel à pndans le second.
Le point important est que, si la taille du problème est multipliée par 100, le temps de calcul sera multiplié par 100 dans
le premier cas, mais seulement par 10 dans le deuxième.
2) Évaluation de la complexité
Pour déterminer le coût d’un algorithme, nous nous fonderons en général sur le modèle de complexité suivant :
Une affectation, une comparaison ou l’évaluation d’une opération arithmétique ayant en général un faible temps
d’exécution, nous le considérerons comme l’unité de base dans laquelle on mesure le coût d’un algorithme.
Le coût de deux instructions pet ql’une après l’autre est la somme des coûts des instruction pet q.
Le coût d’un test « Si balors psinon q» est inférieur ou égal au maximum des coûts des instructions pet q, plus une
unité qui correspond au temps d’évaluation de l’expression b.
Le coût d’une boucle « Pour ivariant de 1ànfaire p» est égale à nfois le coût de l’instruction psi ce coût ne dépend
pas de la valeur de i. Quand le coût du corps de la boucle dépend de la valeur du compteur i, le coût total de la boucle
est la somme des coûts du corps de la boucle pour chaque valeur de i.
Le cas des boucles conditionnelles est plus complexe à traiter puisque le nombre de répétitions n’est en général pas
connu a priori. On peut majorer le nombre de répétitions de la boucle de la même façon qu’on démontre sa terminai-
son et ainsi majorer le coût de l’exécution de la boucle.
Exercice 1 : Déterminer la complexité des algorithmes du paragraphe I.
3) Notation O(n)
Dans la pratique, il est souvent difficile, et inutile, de compter précisément le nombre d’opérations effectuées par un
algorithme. On se contente en général de dire que le nombre d’instruction est proportionnel à n, où à pn. En effet, le
temps d’exécution dépend de la vitesse de l’ordinateur sur lequel on implante l’algorithme, du langage utilisé, ...
Un autre point important est la notion de terme dominant. Supposons que l’on trouve que le temps d’exécution est
proportionnel à n2+3n. Alors dès que nÊ3, n2Ê3net n2É2n2. Le temps d’exécution est donc finalement proportionnel
àn2. On dit que la quantité n2+3nest un grand O de n2, ce que l’on écrit n2+3n=O(n2). La notion de grand O sera
détaillée en cours de maths.
De manière générale, on dit qu’un algorithme a une complexité en O(f(n)) si son temps d’exécution est, à partir d’un
certain rang, inférieur au produit de f(n) par une constante.
3/5 DELAY – Paul Constans – 2016 - 2017
PTSI – IC ALGORITHMIQUE
Exemples de temps d’exécution :
Nom courant Temps pour
n=106Remarques
O(1) temps constant 1 ns
Le temps d’exécution ne dépend pas des données traitées, ce qui est
assez rare! En particulier, la plupart des données ne sont même pas
lues.
O(logn) logarithmique 10 ns
En pratique, cela correspond à une exécution quasi instantanée. Bien
souvent, à cause du codage binaire de l’information, c’est en fait la
fonction log2nqu’on voit apparaître; mais comme la complexité est
définie à un facteur près, la base du logarithme n’a pas d’importance.
O(n) linéaire 1 ms
Le temps d’exécution d’un tel algorithme ne devient supérieur à une
minute que pour des données de taille comparable à celle des mé-
moires vives disponibles actuellement. Le problème de la gestion de
la mémoire se posera donc avant celui de l’efficacité en temps.
O(n2) quadratique 1/4 h Cette complexité reste acceptable pour des données de taille
moyenne (n<106) mais pas au delà.
O(nk) polynomiale 30 ans si k=3Ici nkest le terme de plus haut degré d’un polynôme en n; il n’est pas
rare de voir des complexités en O(n3) ou O(n4).
O(2n) exponentielle
plus de 10300000
milliards d’an-
nées
Un algorithme d’une telle complexité est impraticable sauf pour de
très petites données (n<50). Comme pour la complexité logarith-
mique, la base de l’exponentielle ne change fondamentalement rien
à l’inefficacité de l’algorithme.
Exercice 2 : Déterminer le rôle des algorithmes suivants, et donner la complexité de chacun :
1. Algo 1 :
Entrées : nN
pour ivariant de 1à10 faire
Afficher i×n
2. Algo 2 :
Entrées : nN
pour ivariant de 1ànfaire
Afficher i×i
3. Algo 3 :
Entrées : nN
pour ivariant de 1ànfaire
pour jvariant de 1ànfaire
Afficher i×j
Aller à la ligne
4) Différentes nuances de complexité
a) Complexité dans le pire des cas
Pour deux données de même taille, un algorithme n’effectue pas nécessairement le même nombre d’opérations élémen-
taires. Par exemple, considérons l’algorithme de test de primalité :
Entrées : nN
Sorties : premier : un booléen égal à vrai si nest premier, faux sinon
premier vrai
pour ivariant de 2à¥pn¦faire
si idivise nalors
premier faux
quitter la boucle
Si nest un nombre premier, alors il faudra ¥pn¦1 itérations; mais pour n1 et n+1 qui sont pairs, le programme
s’arrête dès la première itération.
4/5 DELAY – Paul Constans – 2016 - 2017
PTSI – IC ALGORITHMIQUE
Si la fonction fcaractérise l’efficacité d’un algorithme, on veut avoir l’assurance que l’exécution du programme sera
terminée en un temps proportionnel à f(n), éventuellement moins mais pas plus. On cherche donc un majorant du
temps d’exécution, autrement dit on retiendra le pire des cas pour exprimer la complexité.
b) Complexité dans le meilleur des cas
La complexité au pire est la plus significative, mais dans certains cas, il peut être utile de connaître aussi la complexité
dans le meilleur des cas, pour avoir une borne inférieure du temps d’exécution d’un algorithme.
En particulier, si la complexité dans le meilleur et dans le pire des cas sont du même ordre, cela signifie que le temps
d’exécution de l’algorithme est relativement indépendant des données et ne dépend que de la taille du problème.
c) Complexité en moyenne et complexité amortie
Certains algorithmes ont des temps d’exécution très différents suivant les données d’entrées. Par exemple, certains al-
gorithmes pour trier un tableau de taille nprennent un temps proportionnel à nsi le tableau est trié et proportionnel à
n2dans le cas le pire. On s’intéresse donc parfois à la complexité en moyenne d’un algorithme.
Parler de moyenne des temps d’exécution n’a de sens que si l’on a une idée de la fréquence des différentes données
possibles pour un même problème de taille n. Les calculs de complexité moyenne sont très difficiles dans le cas général,
mais peuvent parfois être intéressants si on connaît à l’avance la fréquence d’apparition des différents types de données.
Par ailleurs, il existe des problèmes où le cas le pire peut se produire mais où, sur des exécutions répétées, on a la cer-
titude que celui-ci ne se produira que peu fréquemment. Prenons l’exemple d’une personne désirant envoyer un SMS
depuis un téléphone mobile. Quelle est la complexité en temps de cet envoi? Si tout se passe bien, la rédaction et l’en-
voi se font en 2 minutes. En revanche, dans le cas le pire, la batterie du téléphone est déchargée, il convient donc de la
recharger pendant 4 heures. La complexité dans le cas le pire pour un envoi est donc de 4 heures et 2 minutes.
Mais la complexité pour nenvois, où nest grand, est bien inférieure à 4nheures et 2nminutes. En effet, une fois le
téléphone chargé, son utilisateur va pouvoir envoyer un millier de SMS avant de devoir recharger la batterie. On peut
donc dire que dans le cas le pire, l’envoi de nSMS successifs va demander ( n
1000 +1) ×4 heures plus 2nminutes. On a
donc la garantie que pour ngrand, le temps mis pour envoyer nSMS est au plus de l’ordre de n
1000 ×4×3600 =n×14,4
secondes plus 2nminutes. Autrement dit, le temps mis pour envoyer un SMS est de l’ordre de 2 minutes et 14 secondes.
On dit que cette durée est la complexité amortie représentant le coût de l’envoi. La notion d’amortissement vient de
la comptabilité : le coût d’un kilomètre en voiture est nul si la voiture fonctionne et que le plein est fait alors qu’il est
extrêmement élevé s’il faut commencer par acheter la voiture.
La notion pertinente pour mesurer ce coût est en général de calculer l’amortissement des dépenses initiales (l’achat de
la voiture) sur la totalité du kilométrage. C’est une situation qu’on retrouve en informatique : il arrive ainsi que, dans
certains problèmes, une opération ait un coût O(n) dans le cas le pire, où nest la taille du problème, et un coût constant
en complexité amortie. En Python, l’opération d’ajout d’un nouvel élément à un tableau de taille nà la fin de ce tableau
rentre dans ce cadre. Cependant, la théorie de la complexité amortie dépasse le cadre du programme.
d) Complexité en espace
Jusqu’ici nous avons uniquement discuté du temps d’exécution des algorithmes. Une autre ressource importante en
informatique est la mémoire. On appelle complexité en espace d’un algorithme la place nécessaire en mémoire pour
faire fonctionner cet algorithme. Elle s’exprime également sous la forme d’un O¡f(n)¢nest la taille du problème.
Évaluer la complexité en espace d’un algorithme ne pose la plupart du temps pas de difficulté, il suffit de faire le total des
tailles en mémoire des différentes variables utilisées. La seule vraie exception à la règle est le cas des fonctions récursives,
qui cachent souvent une complexité en espace élevée, et sur lequel on reviendra en deuxième année.
5/5 DELAY – Paul Constans – 2016 - 2017
1 / 5 100%
La catégorie de ce document est-elle correcte?
Merci pour votre participation!

Faire une suggestion

Avez-vous trouvé des erreurs dans linterface ou les textes ? Ou savez-vous comment améliorer linterface utilisateur de StudyLib ? Nhésitez pas à envoyer vos suggestions. Cest très important pour nous !