Programmer la dichotomie.
L étant une liste de nombres (ou de chaînes de caractères) triée (ordre croissant), programmer la recherche dichotomique d'un element x dans L en langage python.
Avant d'aborder les exercices de cette page (notamment les exercices intitulés "nombre d'étapes"), vous devez travailler cette page de cours et cette page d'exercices.
L étant une liste de nombres (ou de chaînes de caractères) triée (ordre croissant), programmer la recherche dichotomique d'un element x dans L en langage python.
# liste au hasard pour les tests
from random import randint
L = [ randint(1,100) for j in range(30) ]
#tri de la liste :
L.sort()
def dicho(L,x) :
debut, fin = 0, len(L)-1
while debut <= fin :
milieu = (debut+fin)//2
if x == L[milieu] : return milieu
elif x < L[milieu] : fin = milieu-1
else : debut = milieu+1
return -1 # cas x absent de L
##### test ######
print(L)
x = int(input('Entrez un entier naturel : '))
print()
print('Recherche par dichotomie.')
r=dicho(L,x)
if r != -1 : print("Elément trouvé à l'indice :", r,'.')
else : print('Elément absent.')
print()
print('Recherche par fonction builtin pour contrôle.')
for i, y in enumerate(L) :
if y == x : print("Elément présent à l'indice ", i)
On pourra également chercher à programmer une version récursive de la recherche par dichotomie.
On raisonnera ici à partir du programme donné dans la solution de l'exercice précédent :
def dicho(L,x) :
debut, fin = 0, len(L)-1
while debut <= fin :
milieu=(debut+fin)//2
if x == L[milieu] : return milieu
elif x < L[milieu] : fin = milieu-1
else : debut = milieu+1
return -1 # cas x absent de L
On appellera étape un passage dans la boucle while.
La liste L est [l0, l1, ..., l7 ].
Supposons que l'on cherche l'élément maximal de L avec l0 ≤ l1 ≤ ... ≤ l6 < l7. On recherche donc l'élément x=l7.
Initialisation : debut=0, fin=7.
Il a donc fallu 4 étapes pour trouver une occurrence de x. Or blog(8)+1=blog(23)+1=3+1=4.
Remarque. Si on cherche un élément x> L[7], la boucle de l'étape 4 ne permet pas de conclure, mais on ne rentrera toutefois pas une fois de plus dans la boucle puisqu'on aura debut>fin (condition du while non respectée). On aura donc le même nombre d'étapes si l'on appelle, comme suggéré dans l'énoncé, 'étape' une exécution du contenu de la boucle.
Supposons comme précédemment que la liste contient des nombres distincts et que l'on cherche l'élément x de valeur maximale (donc d'indice 2k-1 dans la liste).
On a donc eu besoin de k+1= blog( 2k)+1=blog(n)+1 étapes.
On remarquera qu'une étape nécessite en général un test == et un test <. Ici la dernière étape ne nécessitait qu'un seul des deux tests, mais les deux peuvent avoir lieu (cas de la recherche d'une valeur strictement plus grande que le maximum de la liste). On peut donc avoir besoin de 2(blog(n)+1) tests (== ou <) dans la recherche d'un élément dans la liste.
On continue le raisonnement commencé à l'exercice précédent.
Dans l'exercice précédent, il a été montré que l'on pouvait avoir besoin de blog(n)+1 étapes dans la recherche d'un élément (où étape = un passage dans la boucle while).
Montrer maintenant que blog(n)+1 étapes sont toujours suffisantes (quelle que soit la taille n de la liste initiale).
On aura ainsi établi le résultat à retenir : il faut au plus blog(n)+1 étapes pour la recherche dichotomique dans une liste de taille n (ce nombre d'étapes pouvant être atteint dans certains cas).
Pour établir que blog(n)+1 étapes suffisent, on va utiliser le fait que partant d'une liste de longueur m, on aboutit à une liste de longueur au plus m//2.
Soit L une liste de longueur n.
Par définition de blog(n), on a n//(2blog(n)) ≤1. Il ne reste donc qu'au plus un élément à tester après cette étape blog(n). On aura donc terminé à l'étape suivante.
blog(n)+1 étapes suffisent donc toujours.
Il s'agit ici de mettre en application les conclusions des exercices précédents.
Dans les deux cas, il s'agit de calculer blog(n)+1. Rappelons que blog(n) se calcule facilement par un petit programme python :
def blog(x) :
n = 0
while x>1 :
x/=2
n+=1
return n
print('Pop française : ', blog(65*10**6)+1 )
print('Pop mondiale : ', blog(7*10**9)+1)
ce qui donne :
Pop française : 27 Pop mondiale : 34
Comparer ces nombres aux nombres obtenus dans la recherche « naïve » de début de cours, cela devrait vous permettre de comprendre l'efficacité d'un algorithme présentant une complexité en log(n)...
\( \frac{7 \times 10^9}{65 \times 10^6} \approx 107,69 \)
La population mondiale est plus de 107 fois la population française.
Comme \( \log_2(ab) = \log_2(a) + \log_2(b) \), on peut écrire : \[ \log_2( \text{pop mondiale} ) \approx \log_2( 107,69 ) + \log_2( \text{pop française}) \]
Comme \( \log_2( 107.69) \approx 6.75 \), on comprend maintenant pourquoi le nombre d'étapes augmente aussi peu entre une recherche dans un annuaire de 65 millions d'entrées et une recherche dans un annuaire de 7 milliards d'entrées.