Programmation algorithmique/Nombre d'opérations optimal

La rapidité d'un algorithme est exprimée sous la forme du nombre d'opérations effectuées par celui-ci. En général, ce nombre dépend des données d'entrée.

Il existe différents nombres d'opérations :

  • le nombre d'opérations minimal, qui se produit dans le meilleur des cas,
  • le nombre d'opérations maximal, qui se produit dans le pire des cas,
  • le nombre moyen d'opérations, qui est une moyenne de ce que l'on peut espérer d'un algorithme dans la plupart des cas.

Exemple : un algorithme de tri d'un tableau de n éléments effectuera un nombre moyen d'opérations dépendant de n (la taille du tableau) :

  • L'algorithme de tri à bulle s'effectue en O(n²) ;
  • L'algorithme de tri rapide (appelé Quicksort) s'effectue en moyenne en O(n*log(n)).

Comparaison de quelques algorithmes modifier

Calcul de np modifier

But: Élever un nombre entier n à la puissance p (entier également).

Algorithme simple modifier

L'implémentation simple de cet algorithme est la suivante :

ENTIER resultat
resultat <- 1
pour i de 1 à p
faire
  resultat <- resultat * n
finfaire

Cet algorithme s'effectue en O(p)

Algorithme plus compliqué modifier

Cet algorithme est plus compliqué, mais effectue moins d'opérations que le précédent :

ENTIER resultat
resultat <- 1
tantque p > 0
faire
  si p ET 1 <> 0 alors          # test du bit 0 de p
    resultat <- resultat * n
  finsi
  n <- n * n
  p <- p / 2   # division entière ou décalage de bits
finfaire

Cet algorithme s'effectue en O(log(p))

Cet algorithme fonctionne de la façon suivante :

  • A chaque itération de la boucle, n vaut successivement  ,  ,  ,  , ...
  • La valeur de l'exposant p en binaire s'exprime de la façon suivante :
 
  • Chaque bit   de p est testé, s'il vaut 1, il faut multiplier le résultat temporaire par  . La relation mathématique entre les puissances est utilisée. Par exemple :  . Donc pour tous les bits de  , on a :
 

Nous avons donc ici deux algorithmes qui calculent le même résultat mais en effectuant des opérations différentes. Le second algorithme est bien plus rapide que le premier car lorsque   devient très grand,   devient négligeable devant  . Pour de petites valeurs de  , le second algorithme peut être plus lent.

Dans cet exemple, l'algorithme le plus rapide est aussi le plus compliqué à écrire et à comprendre, mais ce n'est pas toujours le cas.


Machine séquentielle, machine parallèle, machine quantique modifier

Toutes les complexités indiquées ici valent pour des machines séquentielles, c'est à dire une machine munie d'un seul processeur, ou bien une machine multi-processeurs sur laquelle on n'utiliserait qu'un seul cœur. D'ailleurs, le langage algorithmique que nous utilisons décrit uniquement des séquences d’instructions et d'opérations.

Notons que le facteur d'accélération amené par une machine parallèle, c'est-à-dire munie de plusieurs processeurs, est limité. Par exemple, pour une machine à 64 processeurs, on peut espérer aller 64 fois plus vite au maximum. Pour un algorithme en   et   valant 100, le nombre d'opérations sur une machine séquentielle est de l'ordre de  , tandis que sur une machine à 64 processeurs on peut espérer un ordre de   soit   ou   opérations. On voit bien ici que le nombre de processeurs, forcément limité, n'influence pas forcément la complexité en temps. Ceci s'explique également par le fait qu'un facteur multiplicatif constant (ici  ) est négligeable devant un carré ou une exponentielle quand la taille du problème devient grande.

Quant aux machines quantiques, qui sont loin d'être exploitables actuellement, leur utilisation remettrait à plat l'état de l'art en algorithmique et en complexité. Par exemple, pour des problèmes dont on ne connaît que des algorithmes de complexité en temps exponentielle, on connaît des algorithmes quantiques de complexité linéaire. Alors qu'il faut actuellement des dizaines d'années à des machines parallèles surpuissantes pour casser une clé de chiffrement, un ordinateur quantique mettrait quelques instants.