Probleme – La dichotomie, jusqu'au nombre exact de tours
Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 3 — Algorithmes, programmes et complexité
Énoncé
- Écrire la recherche dichotomique avec sa spécification.
- Prouver sa terminaison par un variant, et sa correction par un invariant.
- Établir que le nombre de tours dans le pire cas vaut exactement , et le vérifier.
- Pourquoi la recherche dichotomique ne rend-elle pas nécessairement la première occurrence ?
Corrigé
1. Le code.
/* Renvoie un indice i tel que t[i] == v, ou -1 si v ne figure pas dans
t[0..n-1].
Precondition : n >= 0 et t[0..n-1] est croissant (au sens large).
Postcondition : soit le resultat vaut -1 et v est absent de t[0..n-1],
soit 0 <= resultat < n et t[resultat] == v. */
int cherche(const int t[], int n, int v) {
assert(n >= 0);
int g = 0, d = n - 1;
/* INVARIANT : si v figure dans t[0..n-1], alors il figure dans
t[g..d]. VARIANT : d - g + 1. */
while (g <= d) {
int m = g + (d - g) / 2;
if (t[m] == v) { return m; }
if (t[m] < v) { g = m + 1; } else { d = m - 1; }
}
return -1;
}
2. Les preuves.
Terminaison. Variant , le nombre de cases encore candidates. Il est entier, et positif tant que . À chaque tour où l'on ne retourne pas, ou bien passe à , ou bien passe à : dans les deux cas décroît d'au moins . La boucle s'arrête.
Correction. Initialisation : , , donc est tout le tableau : l'invariant est trivialement vrai. Conservation : supposons-le vrai et . Si , alors par croissance pour tout : ne peut figurer dans , donc s'il figure quelque part dans c'est dans — ce qui est exactement le nouvel intervalle. Le cas est symétrique. Utilisation : deux sorties possibles. Ou bien on retourne avec , et la postcondition est vérifiée. Ou bien on sort par : l'intervalle est vide, et l'invariant dit alors que ne figure nulle part — on rend .
3. Le nombre exact de tours. Notons la valeur du variant après tours, avec . Le tour retire la case et garde l'un des deux côtés, dont la taille est au plus . Donc , et la boucle s'arrête dès que , c'est-à-dire au plus tard pour , soit .
Cette borne est atteinte : en cherchant une valeur absente, on ne retourne jamais par le return m, et le pire cas est réalisé. Vérification, en essayant toutes les valeurs présentes et absentes pour chaque :
n | 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | 100 | 1000 | 10^6
tours| 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 | 7 | 10 | 20
log2 | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 | 7 | 10 | 20
Le maximum mesuré vaut pour les onze tailles. Noter le saut à chaque puissance de deux : de à , un tour de plus ; de à , aucun. Un million de cases se fouillent en vingt comparaisons.
4. La première occurrence. La postcondition dit « un indice tel que », pas « le plus petit ». Sur et , le premier milieu est et la fonction rend : ni la première occurrence, ni la dernière.
Ce n'est pas un défaut, c'est la spécification. La corriger a un coût qu'il faut connaître : pour obtenir la première occurrence, on ne s'arrête pas sur l'égalité, on continue de chercher à gauche.
/* Renvoie le plus petit i tel que t[i] >= v, ou n si v majore t.
Precondition : t[0..n-1] croissant. */
int borne_inferieure(const int t[], int n, int v) {
int g = 0, d = n; /* d est EXCLU : l'invariant change */
/* INVARIANT : tous les t[k] pour k < g sont < v, et tous les t[k]
pour k >= d sont >= v. */
while (g < d) {
int m = g + (d - g) / 2;
if (t[m] < v) { g = m + 1; } else { d = m; }
}
return g;
}
Elle n'a aucune sortie anticipée : le nombre de tours ne dépend pas de la valeur cherchée, seulement du chemin suivi, et il vaut au plus . Mesuré sur toutes les valeurs, présentes et absentes : entre et tours pour , et borne_inferieure rend bien sur , là où cherche rendait .
Le changement de convention sur — exclu au lieu d'inclus — est ce qui rend l'invariant énonçable : avec inclus, il faudrait parler de , indice qui peut valoir . Un intervalle mal choisi rend une preuve pénible ; c'est souvent le signe qu'il faut changer de convention, pas de code.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.