Leçons 1/4 · 25 min
Compter les étapes : parcours et recherche dichotomique
Un cahier de clients
Awa tient une boutique à Bonamoussadi et note ses clients dans un cahier : un nom, un numéro de téléphone par ligne, dans l'ordre où ils sont venus. Pour retrouver un numéro, elle lit ligne après ligne. Avec 50 clients, c'est supportable. Avec 10 000, il lui faudra en moyenne 5 000 lignes et, au pire, les 10 000. Si elle avait tenu son cahier par ordre alphabétique, elle l'ouvrirait au milieu, verrait que le nom cherché est avant ou après, et ne garderait que la bonne moitié — comme on cherche un mot dans un dictionnaire. Même cahier, même contenu : la façon de le ranger change tout.
Le vocabulaire du coût
On mesure le coût d'une opération en nombre d'étapes élémentaires (une comparaison, une lecture) en fonction du nombre n d'éléments. Trois opérations reviennent partout :
- la recherche (lookup) : retrouver un élément donné ;
- l'insertion : ajouter un élément ;
- le parcours (traversal) : visiter tous les éléments, par exemple pour tout afficher. Il coûte au minimum n étapes, quelle que soit la structure : on ne peut pas visiter n éléments en moins de n visites.
On note l'ordre de grandeur avec la notation « grand O » : O(n) veut dire « le coût croît proportionnellement à n », O(log n) « le coût croît comme le nombre de fois qu'on peut diviser n par 2 », O(1) « le coût ne dépend pas de n ». Cette notation ignore volontairement les constantes (« 3n » et « n » sont tous deux O(n)) ; nous y reviendrons dans la dernière leçon.
Le parcours séquentiel
Parcourir dans l'ordre, jusqu'à trouver ou jusqu'à épuiser la liste. Au pire (l'élément est absent ou en dernière position), il faut n comparaisons.
La recherche dichotomique
Elle exige un tableau trié. On compare la cible à l'élément du milieu ; selon le résultat on garde la moitié gauche ou droite, et on recommence.
def binary_search(items, target):
low, high = 0, len(items) - 1
while low <= high:
mid = (low + high) // 2
if items[mid] == target:
return mid
if items[mid] < target:
low = mid + 1
else:
high = mid - 1
return NoneRemarque : Python n'a pas de comparaison à trois issues, et ce code en fait jusqu'à deux par étape (== puis <). Dans ce cours on compte une comparaison à trois issues par étape (comme le comparateur d'un tri en C, qui rend un nombre négatif, nul ou positif) : c'est la convention du tableau ci-dessous.
Exemple suivi. Tableau trié [3, 8, 14, 21, 29, 35, 42], cible 35. Premier milieu : indice 3, valeur 21 ; 21 < 35, on garde la droite (indices 4 à 6). Nouveau milieu : indice 5, valeur 35 : trouvé en 2 étapes, au lieu de 6 pour un parcours.
Pourquoi si peu ? À chaque étape il reste au plus la moitié des candidats : après k étapes, au plus n ÷ 2^k. On a fini quand il n'en reste qu'un, donc après environ log₂ n étapes. Dans la convention de comptage de ce cours, on compte une comparaison à trois issues (égal, plus petit, plus grand) par étape, soit au pire ⌊log₂ n⌋ + 1 étapes :
| n | Parcours (pire cas) | Dichotomie (pire cas) |
|---|---|---|
| 100 | 100 | 7 |
| 1 000 | 1 000 | 10 |
| 5 000 | 5 000 | 13 |
| 100 000 | 100 000 | 17 |
| 1 000 000 | 1 000 000 | 20 |
Multiplier n par 1 000 multiplie le parcours par 1 000, mais n'ajoute qu'une dizaine d'étapes à la dichotomie. Voilà ce qu'on appelle « passer à l'échelle ».
Ce que cela ne garantit pas
- La dichotomie ne marche que sur des données triées. Trier coûte lui-même environ n × log₂ n comparaisons. Pour une seule recherche dans 50 000 éléments en désordre, trier d'abord coûte bien plus cher qu'un parcours : ce n'est rentable que si l'on cherche souvent.
- Garder un tableau trié a un prix à chaque insertion (leçon 2).
- Le « pire cas » n'est pas le cas moyen : un parcours trouve en moyenne vers n ÷ 2 si l'élément est présent. Et les constantes cachées comptent pour de petits n : sur dix éléments, le plus simple est souvent le plus rapide.
À retenir
Compter les étapes en fonction de n : parcours = n, dichotomie ≈ log₂ n (sur du trié). Un parcours complet coûte toujours au moins n. Le « pire cas » donne une garantie, pas une prédiction.
