Vous êtes sur la page 1sur 7

Algorithmique

1 Algorithme

♡Définition 1
• Un algorithme est une suite finie et non ambiguë d’opérations ou d’instructions permettant de
résoudre un problème ou d’obtenir un résultat.
• L’algorithmique est l’étude et la production de ces instructions.

3 niveaux d’abstraction :
• Les problèmes décrits en langage naturel
• Les algorithmes décrits dans un pseudo-langage de programmation, proche du langage naturel
• Les programmes décrits dans un lange de programmation

Exemple 1 :

Algorithme 1 Somme des n premiers entiers naturels (utilisation d’une boucle)


i←1
som ← 0
Tant que i ⩽ n Faire
som ← som + i
i←i + 1
Fin Tant que

Niveaux de difficulté :
• Conceptuel
• Difficulté du problème ?
• Comment le résoudre ?
• Quelle démarche utiliser ?
• Technique
• Comment mettre en œuvre mon algorithme sur une machine ?
• Quelles sont les ressources à ma disposition ?
• Quel est le langage le plus adapté ?

Questions relatives aux algorithmes :


• Le programme s’arrête-t-il ? C’est ce qu’on appelle la terminaison. Cette question n’a de sens que s’il y
a une boucle.
• Les sorties correspondent-elles à la solution de mon problème ? C’est ce qu’on appelle la correction ou
la preuve de l’algorithme.
• Combien de calculs élémentaires doit-on faire pour produire la sortie ? Combien d’espace mémoire mon
algorithme utilisera ? C’est ce qu’on appelle la complexité ou le coût de l’algorithme. Il y a la complexité
en temps et la complexité en espace.
2 La terminaison

♡Définition 2
On appelle variant d’une boucle une expression dont la valeur varie à chaque itérations de la boucle

Un variant de boucle bien choisi permet de prouver qu’une boucle Tant que se termine.

Exemple 2 : Dans l’algorithme 1, le variant de boucle i est augmenté de 1 à chaque itération, il finit donc par
devenir strictement supérieur à n et la boucle Tant que s’arrête.

3 La correction

♡Définition 3
Un invariant d’une boucle une propriété qui si elle est vraie avant l’exécution d’une itération le
demeure après l’exécution de l’itération.

Un invariant de boucle bien choisi permet de prouver qu’une boucle produit le résultat attendu.

Exemple 3 : Dans l’algorithme 1, la propriété som= 1 + 2 + · · · + k est un invariant de la boucle. En effet,


• som vaut 0 + 1 = 1 après la première itération, la propriété est donc vraie après la première itération ;
• on suppose que la propriété est vraie à une certaine itération k c’est-à-dire que som vaut 1 + 2 + · · · + k.
Après k itération, i vaut k + 1 donc à l’itération suivante on a som ← som + i donc som vaut
som +k + 1 = 1 + 2 + · · · + k + (k + 1) et donc la propriété est encore vraie ;
• la propriété demeure donc vraie après la n-ième itération et on a bien som = 1 + 2 + · · · + n.

4 La complexité
On va essentiellement s’intéresser à la complexité en temps.

On aimerait pouvoir comparer différents algorithmes pour un même problème. Comme les ordinateurs peuvent
être plus ou moins performants, rapides et des langages peut-être plus adaptés que d’autre dans certains cas, il
nous faut une mesure intrinsèque de la complexité de l’algorithme indépendamment de l’implémentation.

Illustration : Reprenons le problème précédent : la somme des nombres de 1 à n.


• Première idée : utilisation d’une boucle. (Algorithme 1)
On effectue :
∗ 2 additions par boucle ;
∗ n boucles.
On effectue donc 2n additions.
• Deuxième idée : utilisation des mathématiques.

n
n(n + 1)
i=
i=1
2

Algorithme 2 Somme des n premiers entiers naturels (utilisation des mathématiques)


som ← n + 1
som ← som * n
som ← som / 2

On effectue donc 1 addition, 1 multiplication et 1 division.

Conclusion : Le nombre d’opération à effectuer dans l’algorithme 1 dépend donc de la valeur de n. Plus n est
grand et plus il faut faire d’opérations.
Par contre dans l’algorithme 2 il n’y a que 3 opérations à chaque fois.
On peut donc dire que l’algorithme 2 est meilleur que l’algorithme 1.

But : le but étant de pouvoir dire « sur toute machine, et quel que soit le langage utilisé, l’algorithme A est
meilleur que l’algorithme B pour des données de grande taille. »

On va donc choisir une mesure élémentaire (nombre de comparaisons, nombre d’affectations, nombre d’opération
arithmétiques ...) et on va l’utiliser pour mesurer la complexité d’un algorithme en fonction d’un paramètre
représentatif des entrées (taille).

Attention : il n’y a pas de système complet de règles. Comme on peut choisir différentes mesures élémentaires,
il se peut qu’on obtienne pas tout à fait la même complexité. Par contre elle seront du même ordre de grandeur.
Par exemple dans l’algorithme 1 on peut choisir :

• les affectations : • les opérations arithmétiques : • les comparaisons : il y en a autant


∗ 2 avant la boucle ; que de boucle plus 1 pour sortir de
∗ 2 additions par boucle ;
∗ 2 par boucle ; la boucle. Il y en a donc n + 1
∗ n boucles.
∗ n boucle.
il y a donc 2 + 2n affectations. On effectue donc 2n additions.

On voit que ce n’est pas tout a fait le même nombre en fonction de la mesure élémentaire qui a été choisie, mais
lorsque n est très grand ces trois valeurs sont quasiment identiques et elles ont le même ordre de grandeur que
n (si un chat prend 1 kg ça se voit, si un éléphant prend 1 kg ça ne se voit pas).

L’algorithme 1 s’exécutera en temps linéaire (O(n)) et l’algorithme 2 s’exécutera en temps constant (O(1)).

Voici quelques chiffres :


Temps d’exécution en fonction de la complexité d’un algorithme et de la taille des données.

Nombre d’opération par seconde = 109

Complexité
n= 1 log(n) n n log2 (n) n2 n3 2n
102 1ns 6, 6ns 0, 1µs 6, 6µs 10µs 1ms 1013 a
103 1ns 9, 9ns 1µs 9, 9µs 1ms 1s ∞
104 1ns 13, 2ns 10µs 1, 32ms 0, 1s 1 min ∞
105 1ns 16, 6ns 1ms 1, 66ms 10s 11 j ∞
106 1ns 19, 9ns 0, 001s 0, 019s 1 min 31a ∞

∞ =« > 1025 années »

5 Etude de quelques algorithmes


5.1 Recherche d’un élément
5.1.1 Recherche d’un élément x dans une liste L
Pour savoir si un nombre x est un élément de L, on va comparer x aux éléments de L jusqu’à ce que l’on trouve
x dans L, ou que l’on a comparé x à tous les éléments de L.
Voici une fonction de recherche dans une liste. Elle prend en paramètres la liste L dans laquelle il faut chercher
et l’élément x qu’il faut chercher. Elle retourne True s’il est dedans et False sinon.
Algorithme 3 Recherche d’un élément dans une liste
Fonction Recherche(L, x)
n ← taille de L
i←1
Tant que i ⩽ n Faire
Si L[i] = x Alors
return Oui
Fin Si
i←i + 1
Fin Tant que
return Non
Fin Fonction

On peut remarquer que dans le meilleur des cas (l’élément x est en première position) il faut 1 seule comparaison,
dans le pire des cas (l’élément x n’est pas dans la liste ou alors il est en dernière position) il faut autant de
comparaisons qu’il n’y a d’éléments dans la liste.
La terminaison et la correction sont évidents.

5.1.2 Recherche du plus grand élément dans une liste L


Voici une fonction qui recherche le plus grand élément dans une liste L.

Algorithme 4 Recherche du plus grand élément dans une liste


Fonction Max(L)
n ← taille de L
i←1
max ← L[1]
Tant que i ⩽ n Faire
Si L[i] > max Alors
max ← L[i]
Fin Si
i←i + 1
Fin Tant que
return max
Fin Fonction

5.2 Rechercher d’un élément dans une liste triée


Si la liste est triée, pour un élément x dans une liste L de taille n, on applique la recherche dichotomique :
— si n = 0, x n’est pas un élément de la liste vide ;
— sinon, on compare x à l’élément ”milieu” de la liste L ;
— si x est égal à cet élément, on a fini, on a trouvé x ;
— si x est plus petit, on applique ce procédé à la première moitié de la liste ;
— si x est plus grand, on applique ce procédé à la deuxième moitié de la liste.
Ce qui donne en pseudo-code :
Algorithme 5 Recherche d’un élément dans une liste triée
Fonction Recherche_tri(L, x)
a ← 1, b ← taille de L, m ← (a+b)/2 (div. euc.)
Tant que a < b Faire
Si L[m] = x Alors
return Oui
Sinon
Si L[m] > x Alors
b←m - 1
Sinon
a←m + 1
Fin Si
Fin Si
m ← (a+b)/2 (div. euc.)
Fin Tant que
return Non
Fin Fonction

• La correction est évidente.


• Pour la terminaison on prend comme variant de boucle b - a qui est donc toujours strictement positif
pour rentrer dans la boucle.
On a par définition a < m < b.
Ensuite 3 cas sont possibles :
1. si L[m] = x alors la boucle s’arrête. La terminaison est assurée.
2. si L[m] > x alors on modifie b. Notons le nouveau b, b' et on a b' = m - 1.
On a alors b' - a < m - a <= b - a.
Donc le variant décroît strictement.
3. si L[m] < x alors on modifie a. De la même manière que le point précédent, on a
b - a' < b - m <= b - a.
Donc le variant décroît strictement.
• Pour la complexité, on fait autant de comparaison que de division de la taille de la liste par 2. Donc on peut
majorer le nombre maximum d’itérations k. S’il y a n éléments dans la liste, on a 2k ⩽ n ⇔ k ⩽ log2 (n).
La complexité est de l’ordre du logarithme de la longueur de la liste.

Il arrive très souvent qu’on ait besoin de trier un liste.

6 Les tris
6.1 Tri par comparaison ou tri à bulle
Voici le principe : on compare les deux premiers termes de la liste. S’ils sont dans le bon ordre alors on ne fait
rien. Sinon on les échange de place. On compare ensuite le deuxième et le troisième etc.
Ainsi on fait remonter la plus grande valeur à la fin de la liste.
Ensuite on recommence pour faire remonter la deuxième plus grande en avant-dernière position et ainsi de suite.

Exemple sur la liste suivante : 8 4 3 5 7 2 1 6

8 4 3 5 7 2 1 6 4 3 5 8 7 2 1 6 4 3 5 7 2 8 1 6

4 8 3 5 7 2 1 6 4 3 5 8 7 2 1 6 4 3 5 7 2 1 8 6

4 8 3 5 7 2 1 6 4 3 5 7 8 2 1 6 4 3 5 7 2 1 8 6

4 3 8 5 7 2 1 6 4 3 5 7 8 2 1 6 4 3 5 7 2 1 6 8

4 3 8 5 7 2 1 6 4 3 5 7 2 8 1 6 4 3 5 7 2 1 6 8
3 4 5 7 2 1 6 8 3 4 5 2 1 6 7 8 3 2 1 4 5 6 7 8
3 4 5 7 2 1 6 8 3 4 2 5 1 6 7 8 3 2 1 4 5 6 7 8
3 4 5 7 2 1 6 8 3 4 2 5 1 6 7 8 3 1 2 4 5 6 7 8
3 4 5 7 2 1 6 8 3 4 2 1 5 6 7 8
3 1 2 4 5 6 7 8
3 4 5 2 7 1 6 8 3 4 2 1 5 6 7 8
3 1 2 4 5 6 7 8
3 4 5 2 7 1 6 8 3 4 2 1 5 6 7 8
1 3 2 4 5 6 7 8
3 4 5 2 1 7 6 8 3 4 2 1 5 6 7 8
1 3 2 4 5 6 7 8
3 4 5 2 1 7 6 8 3 2 4 1 5 6 7 8
3 4 5 2 1 6 7 8 3 2 4 1 5 6 7 8 1 2 3 4 5 6 7 8

3 4 5 2 1 6 7 8 3 2 1 4 5 6 7 8 1 2 3 4 5 6 7 8

3 4 5 2 1 6 7 8 3 2 1 4 5 6 7 8 1 2 3 4 5 6 7 8

On se rend compte qu’à chaque étape il y a une partie de la liste qui est triée et un autre non triée. On peut
noter TC (taille courante) le rang du dernier élément de la partie non triée et on obtient l’algorithme suivant :

Algorithme 6 Tri à bulle


Fonction Tri_a_bulle(L)
n ← Taille de la liste L
Pour TC allant de n à 2 en décroissant Faire
Pour i allant de 1 à TC - 1 Faire
Si L[i] > L[i+1] Alors
L[i] ↔ L[i+1]
Fin Si
Fin Pour
Fin Pour
Fin Fonction

Analyse :
• La terminaison est évidente puisque nous avons des boucles bornées.
• Pour la correction on choisit la propriété suivante : la liste est séparée en deux parties. Une des rangs 1 à
k non triée et une des rangs k+1 à n triée avec tous ses éléments plus grands que la première partie.
Après la première itération de la boucle externe, la plus grande valeur est remontée à la fin. On a donc la
première partie des rangs 1 à n-1 non triée et la deuxième ne contenant qu’un seul élément, le plus grand
de la liste.
Supposons qu’après une certaine itération k quelconque la propriété soit vraie, c’est-à-dire que la partie
allant des rangs 1 à k soit non triée et que la partie allant des rangs k+1 à n soit triée avec tous ses
éléments plus grands que la première partie. Montrons alors que la propriété est conservée à l’itération
suivante (k+1) :
A l’itération k+1, la boucle interne fait remonter le plus grand élément de la partie allant dans rang 1 à k
en dernière position, c’est-à-dire au rang k. On a donc l’élément de rang k plus grand que tous ceux de la
première partie mais plus petit que tous ceux de la deuxième partie. Donc on a à présent une partie non
triée allant des rangs 1 à k-1 et une partie triée allant des rang k à n avec tous ses éléments plus grands
que ceux de la première partie.
La propriété est donc conservée.
La propriété sera donc encore vraie après la dernière itération. Donc la liste sera bien triée.
• Pour la complexité, on fait n-1 comparaison pour la première itération de la boucle externe puis n-2
comparaison pour la deuxième itération et ainsi de suite jusqu’à une ultime comparaison à la dernière
itération.
n(n − 1)
On fait donc en tout 1+2+· · ·+n−1 = comparaison. Donc la complexité est d’ordre quadratique
2
2
(O(n )).
6.2 Tri par insertion
Principe : On a une partie triée et une partie non triée. On traite le premier élément de la partie non triée. Le
traitement consiste à trouver la place où insérer cette élément dans la partie triée puis lui faire de la place en
décalant certains éléments puis d’insérer celui-ci.

Exemple sur la liste suivante : 8 4 3 5 7 2 1 6

4 7 6
8 3 5 7 2 1 6 3 4 5 8 2 1 6 1 2 3 4 5 7 8
↑ ↑ ↑
4 7 6
8 3 5 7 2 1 6 3 4 5 8 2 1 6 1 2 3 4 5 7 8
↑ ↑ ↑
4 8 3 5 7 2 1 6 3 4 5 7 8 2 1 6 1 2 3 4 5 6 7 8

3 2
4 8 5 7 2 1 6 3 4 5 7 8 1 6
↑ ↑
3 2
4 8 5 7 2 1 6 3 4 5 7 8 1 6
↑ ↑
3 4 8 5 7 2 1 6 2 3 4 5 7 8 1 6

5 1
3 4 8 7 2 1 6 2 3 4 5 7 8 6
↑ ↑
5 1
3 4 8 7 2 1 6 2 3 4 5 7 8 6
↑ ↑
3 4 5 8 7 2 1 6 1 2 3 4 5 7 8 6

Remarques :
• Etude en exercices
• Il existe d’autres tri dont certain sont présenté en exercices.

Vous aimerez peut-être aussi