Exercice 1 : Diviser pour régner : la recherche dichotomique
Diviser pour régner tient en trois mots : couper, résoudre, recombiner. La recherche dichotomique en est le cas le plus simple, celui où la recombinaison est gratuite.
def dicho(t, v):
g, d = 0, len(t) - 1
while g <= d:
m = (g + d) // 2
if t[m] == v:
return m
if t[m] < v:
g = m + 1
else:
d = m - 1
return -1- a) Quelle précondition la fonction exige-t-elle ? Que renvoie-t-elle si la valeur est absente ?
- b) Faites la trace de dicho([2, 5, 8, 12, 16, 23, 38, 56, 72, 91], 23) : donnez à chaque tour les valeurs de g, d et m.
- c) Même travail pour la valeur 40. Combien de tours dans chaque cas ?
- d) Combien de comparaisons au maximum pour un tableau de 1 000 éléments ? de un million ? Justifiez par la formule et comparez à une recherche séquentielle.
- e) Un élève écrit m = (g + d) / 2 au lieu de la division entière. Décrivez l'erreur. Un autre écrit g = m au lieu de m + 1 : décrivez la sienne.
Voir la correction
Réponses
- a) Tableau trié ; si absent
- b) 23 trouvé en 3 tours
- c) 40 absent : 4 tours
- d) 10 et 20 comparaisons au plus
- e) Division flottante bruyante ; g = m boucle
a) La précondition est que le tableau soit TRIÉ par ordre croissant. Sans elle, la comparaison t[m] < v ne permet plus de conclure dans quelle moitié chercher, et la fonction renvoie un résultat arbitraire sans jamais signaler d'erreur. Si la valeur est absente, la boucle se termine lorsque g dépasse d, et la fonction renvoie .
b) Le tableau a 10 éléments, d'indices 0 à 9. Tour 1 : , , , , donc devient 5. Tour 2 : , , , , donc devient 6. Tour 3 : , , , , trouvé : la fonction renvoie 5. Trois tours.
c) Pour la valeur 40. Tour 1 : , , , , devient 5. Tour 2 : , , , , devient 6. Tour 3 : , , , , devient 6. Tour 4 : , , , , devient 7. La condition est alors fausse et la boucle s'arrête : la fonction renvoie . Quatre tours. On retient que l'échec coûte à peine plus cher que le succès, contrairement à la recherche séquentielle où l'échec coûte le maximum.
d) Chaque tour divise par deux la taille de la zone restante, donc le nombre de tours est au plus . Pour : , donc au plus 10 comparaisons. Pour : , donc au plus 20 comparaisons. Une recherche séquentielle en demanderait respectivement 1 000 et 1 000 000. Le rapport vaut donc 100 dans le premier cas et 50 000 dans le second : c'est le propre d'un gain logarithmique, il grandit avec la taille du problème.
e) Avec une division non entière, m devient un FLOTTANT, par exemple . L'accès t[4.5] lève un TypeError, car un indice de liste doit être entier. L'erreur est donc immédiate et bruyante, ce qui est une chance. Avec g = m au lieu de m + 1, la boucle peut ne plus PROGRESSER : quand il ne reste qu'un élément, , la valeur n'est pas trouvée et g reste égal à m, si bien que le tour suivant recalcule exactement le même m. La boucle est infinie, et cette fois rien ne le signale : le programme se fige. Le remède est de vérifier, comme on l'a fait pour un variant, que la quantité décroît STRICTEMENT à chaque tour, ce que garantit le passage à et .