Complexité en temps

I. Approche intuitive de la complexité en temps

Une première façon d'estimer la complexité en temps d'un algorithme est de l'exécuter sur des problèmes de tailles différentes et d'étudier la courbe obtenue. Pour chaque taille de problème, il faut faire la moyenne sur plusieurs exécutions, avec des données aléatoires, et en essayant de limiter au maximum la charge de la machine. On obtient alors une idée approximative de la complexité en temps d'un algorithme.

Une autre façon consiste à incrémenter un compteur à chaque opération "coûteuse" et à procéder comme précédemment, mais à utiliser la valeur de ce compteur plutôt que le temps mis. On a alors des valeurs qui ne sont plus sujettes aux perturbations extérieures, mais cela reste approximatif et intuitif.

Ces 2 méthodes présentent en plus l'inconvénient d'être éventuellement lentes, puisqu'elles prennent le temps nécessaire à l'exécution.

À titre d'exemples, vous pouvez regarder les fichiers complexiteTemps01fibo.py — en testant jusqu'à fibo(40) — et complexiteTemps02recherche.py — en testant jusqu'à un tableau de 1000 cases.

II. Calcul formel de la complexité en temps

Le meilleur moyen de connaître la complexité en temps d'un algorithme est de la calculer formellement. Pour ce faire, on va en général compter "1" pour les opérations les plus lourdes, et on négligera les autres.

III. Exercices

Exercice 1 : faire une comparaison graphique empirique des complexités en temps des algorithmes de tri suivant :

  • tri à bulle
  • tri rapide
  • tri fusion

Ci-dessous figurent 2 propositions de correction. La première n'effectue le tri qu'une fois pour chaque taille de liste, la deuxième 1000 fois. Les bibliothèques utilisées sont accessibles en bas de la page.

import matplotlib.pyplot as plt
from random import randint

from calcTemps import calcTempsFonction

from triBulle import triBulle
from triFusion import triFusion
from triRapide import triRapide

def genererListe(n):
    liste = []
    for i in range(0, n):
        liste.append(randint(1,100))
    return liste

tailles = [n for n in range(0, 1000)]
listeListes = list(map(genererListe, tailles))

print(listeListes)
tempsBulle = list(map(lambda x: calcTempsFonction(triBulle, x), listeListes))
tempsFusion = list(map(lambda x: calcTempsFonction(triFusion, x), listeListes))
tempsRapide = list(map(lambda x: calcTempsFonction(triRapide, x), listeListes))
print(tempsBulle)
print(tempsFusion)
print(tempsRapide)

plt.plot(tailles, tempsBulle)
plt.plot(tailles, tempsFusion)
plt.plot(tailles, tempsRapide)

plt.show()
import matplotlib.pyplot as plt
from random import randint

from calcTemps import calcTempsFonction

from triBulle import triBulle
from triFusion import triFusion
from triRapide import triRapide

def genererListe(n):
    liste = []
    for i in range(0, n):
        liste.append(randint(1,100))
    return liste

def genererNListes(n,t):
    retour = []
    for i in range(0, n):
        retour.append(genererListe(t))
    return retour

tailles = [n for n in range(0, 50)]
listeListes = list(map(lambda x:genererNListes(1000,x), tailles))

print(listeListes)
#tempsBulle = list(map(lambda x: calcTempsFonction(triBulle, x), listeListes))
tempsBulle = list(map(lambda x: sum(map(lambda y: calcTempsFonction(triBulle, y),x)), listeListes))
tempsFusion = list(map(lambda x: sum(map(lambda y: calcTempsFonction(triFusion, y),x)), listeListes))
tempsRapide = list(map(lambda x: sum(map(lambda y: calcTempsFonction(triRapide, y),x)), listeListes))
#print(tempsBulle)
#print(tempsFusion)
#print(tempsRapide)

plt.plot(tailles, tempsBulle)
plt.plot(tailles, tempsFusion)
plt.plot(tailles, tempsRapide)

plt.show()

Exercice 2 : établir formellement la complexité du tri à bulle.

IV. Annexes : bibliothèques utilisées plus haut