Recherche séquentielle, dichotomique, arrière
Cours · algorithmique (école d'ingénieur), chapitre 12 · école d'ingénieur, informatique · le début du chapitre, la suite sur Adloun
Objectifs du chapitre
- Écrire une recherche séquentielle à partir de son invariant, et en connaître la complexité .
- Écrire sans erreur une recherche dichotomique dans un tableau trié, à partir d'un invariant sur un intervalle semi-ouvert , et justifier sa complexité .
- Savoir parcourir un tableau « à rebours » (recherche arrière) : dernière occurrence, insertion dans un préfixe trié, recherche en escalier dans un tableau à deux dimensions.
Chercher une valeur est sans doute l'opération la plus fréquente d'un programme. Ce chapitre présente trois façons de parcourir l'espace de recherche : du début vers la fin (séquentielle), en coupant l'espace en deux à chaque étape (dichotomique), de la fin vers le début (arrière). Chaque programme est écrit à partir d'une propriété invariante, selon la méthode du chapitre 11 : initialisation, progression, arrêt.
12.1 Recherche séquentielle
Problème
Un tableau et une valeur sont donnés. Retourner le premier indice tel que , ou si n'est pas dans .
On parcourt le tableau de gauche à droite. À tout instant, on a examiné les cases d'un préfixe sans y trouver . C'est l'invariant :
- Initialisation. est vraie : le préfixe est vide.
- Progression. et et .
- Arrêt. Lorsque , on a : est absent. Lorsque , est le premier indice cherché, puisque .
static int rechercheSeq(int[] T, int x) { int n = T.length;
int i = 0;
// I(i) : x n'est pas dans T[0:i]
while (i < n && T[i] != x)
i = i + 1; // I(i) et T[i] != x ==> I(i+1)
// I(i) et (i == n ou T[i] == x)
if (i == n) return -1; // x n'est pas dans T[0:n]
return i; // T[i] == x et x n'est pas dans T[0:i]
}
Attention
L'ordre des deux conditions de la boucle est essentiel. En Java, l'opérateur && n'évalue sa seconde opérande que si la première est vraie : quand , la case (qui n'existe pas) n'est jamais lue. Écrire T[i] != x && i < n provoquerait une ArrayIndexOutOfBoundsException lorsque est absent.
Complexité.
Le corps de boucle est en temps constant ; il est exécuté au plus fois. Dans le pire cas ( absent, ou présent seulement en dernière case), le temps de calcul est de la forme : la recherche séquentielle est en . On ne peut pas faire mieux sur un tableau quelconque : tant qu'une case n'a pas été lue, elle pourrait contenir .
12.2 Recherche dichotomique
Si le tableau est trié, chaque case lue renseigne aussi sur ses voisines. Comparer à la case du milieu permet d'éliminer d'un seul coup la moitié du tableau : c'est la dichotomie (du grec : « couper en deux »). C'est ainsi que l'on cherche un mot dans un dictionnaire papier.
12.2.1 Le bon problème : la position d'insertion
Plutôt que « est-il dans ? », on résout un problème un peu plus général, qui a toujours une réponse et qui se programme sans cas particulier.
Soit un tableau trié par ordre croissant et une valeur. La position d'insertion de dans est le plus petit indice tel que toutes les valeurs de sont . Autrement dit : .
Par exemple avec : la position d'insertion de est (la première occurrence de ), celle de est , celle de est , celle de est . Une fois connu, est dans si et seulement si et .
12.2.2 L'invariant
On maintient deux indices et qui délimitent la zone d'incertitude : à gauche de tout est , à partir de tout est .
- Initialisation. et : les deux sous-tableaux et sont vides, est vraie.
- Arrêt. Lorsque , la zone d'incertitude est vide : ; est la position d'insertion.
- Progression. Si , soit un indice de (au milieu). Comme est trié :
- si , alors : est vraie ;
- sinon , donc : est vraie.
Dans les deux cas, la zone d'incertitude a strictement diminué, car .
static int premierIndice(int[] T, int x) { int n = T.length;
int g = 0, d = n;
// I(g,d) : T[0:g] < x et T[d:n] >= x
while (g < d) {
int m = g + (d - g) / 2; // g <= m < d
if (T[m] < x) g = m + 1; // T[0:m+1] < x ==> I(m+1,d)
else d = m; // T[m:n] >= x ==> I(g,m)
}
// I(g,d) et g == d : T[0:g] < x et T[g:n] >= x
return g;
}
static boolean dans(int[] T, int x) { // T trié croissant
int i = premierIndice(T, x);
return i < T.length && T[i] == x;
}
La suite du cours est sur Adloun
Le chapitre en entier, formules et figures comprises, avec le tuteur, les fiches, les QCM et les exercices.
Lire la suite du coursEn accès libre, sans inscription.