TP - Recherche dans une liste⚓︎
1. l'algorithme naïf⚓︎
Pour trouver un nombre dans une liste il suffit de balayer la liste et de renvoyer True dès qu’on trouve l’élément recherché et False si on a parcouru toute la liste sans trouver l’élément.
fonction rechercheNaive(liste, valeurCherchee):
pour chaque index de la liste:
si liste[index] est égal à valeurCherchee:
retourne vrai
retourne faux // élément non trouvé
Exercice 1
- Implémentez la recherche naîve en Python de manière itérative. Pour cela écrire le script de la fonction
recherche_naive(lst, val)
oùlst
est une liste d'entier etval
la valeurà cherher dans la liste. Cette fonction renvoieTrue
sival
est dans la listelst
etFalse
sinon. - Puis tester les valeurs
3
,12
,48
et4
avec la liste:ma_liste = [3,5,12,15,48]
Exercice 2
- Faire le même exercice à l'aide d'un autre type de boucle.
2. La dichotomie: généralités⚓︎
Cette méthode, vous la connaissez tous!
Vous avez probablement joué au jeu :trouve un nombre entre 1 et 100. Vous avez commencé par proposer 50. Si on vous répond, c'est plus, vous proposez 75 puis si l'on vous répond c'est moins vous proposez 62... Vous êtes alors en train d'effectuer une recherche dichotomique !
Faire une recherche dichotomique, c'est chercher une valeur dans un tableau trié en prenant, à chaque étape, le milieu de l'ensemble des valeurs possibles pour éliminer la moitié des possibilités.
2.1 Méthode et exemple pas à pas⚓︎
Soit une liste de nnn objets déjà triés. On recherche un objet en particulier.
- on choisit dans la liste l'objet médian (une moitié des objets est avant, l'autre moitié est après).
- on compare les deux objets . Trois cas :
- si on a trouvé l'objet cherché alors c'est fini.
- si l'objet recherché est placé avant l'objet médian alors on recommence avec cette première partie de la liste.
- si l'objet recherché est placé après l'objet médian alors on recommence avec cette seconde partie de la liste.
- on répète cette démarche jusqu'à ce qu'au bout d'un certain nombre d'essais (cela se calcule) :
- soit on a trouvé l'objet cherché,
- soit il n'est pas dans la liste.
Prenons par exemple la liste triée suivante : [1,3,4,6,7,8,10,13,14]
, où nous allons rechercher par dichotomie l'élement 4
On utilise deux pointeurs indice_gauche
et indice_droit
pour délimiter la sous-liste sur laquelle porte la recherche courante, au départ il s'agit de la liste entière.
indice_gauche
pointe vers l'élément d'indice 0 et indice_droite
pointe vers le dernier élément de notre liste qui ici a l'indice 8.
On cherche l'indice indice_centre
de l'élément médian en ne gardant que la partie entière de la moyenne indice_gauche
et indice_droit
, ici \(\frac{0+8}{2}=4\), l`élément médian est donc l'élément d'indice 4, c'est-à-dire l'élément 7.
Maintenant on compare l'élément central 7 à l'élément recherché 4 : ici 4 < 7, cela signifie que 4 ce trouve dans la première moitié de la liste (celle avant 7).
On modifie alors la valeur du pointeur indice_droit
, qui prend la valeur de indice_centre-1
, et on recommence avec cette nouvelle sous-liste.
indice_gauche = 0
; indice_droit= 3
et indice_centre=1
(partie entière de \(\frac{0+3}{2}\), c'est-à-dire de 1.5)
Maintenant on compare l'élément central 3 à l'élément recherché 4 : ici 4 > 3, cela signifie que que 4 ce trouve dans la deuxième moitié de la sous-liste (celle après 3).
On modifie alors la valeur du poiteur indice_gauche
, qui prend la valeur de indice_centre+1
, et on recommence avec cette nouvelle sous-liste.
indice_gauche = 2
; indice_droit= 3
et indice_centre=2
(partie entière de \(\frac{2+3}{2}\), c'est-à-dire de 2.5)
Maintenant on compare l'élément central 4 à l'élément recherché 4 : ici 4 = 4, l'élément recherché est présent.
Si la sous-liste devient vide, c'est-à-dire que le pointeurs indice_gauche
devient supérieur au pointeur indice_droit
, la recherche est infructueuse et l'algorithme s'arrête
Exercice 3
-
Décrivez chacune des étapes de progression de la recherche dichotomique de l'élément 35 appliquée à la liste ordonnée [2, 12, 17, 25, 33, 35, 44, 54, 77, 91] puis indiquez le nombre de comparaisons nécessaires à cette recherche.
-
Décrivez chacune des étapes de progression de la recherche dichotomique de l'élément 50 appliquée à la liste ordonnée [2, 12, 17, 25, 33, 35, 44, 54, 77, 91] puis indiquez le nombre de comparaisons nécessaires à cette recherche.
2.2 L'algorithme⚓︎
Voici le pseudo-code en langage naturel de cet algorithme :
fonction rechercheDichotomique(liste, val):
//initialisation
indice_gauche ← 0
indice_droite ← longueur de liste - 1
//Boucle de recherche
// La condition début inférieur ou égal à fin permet d'éviter de faire
// une boucle infinie si 'val' n'existe pas dans le tableau.
Tant que indice_gauche <= indice_droite:
indice_centre ← partie_entière((indice_gauche + indice_droite)/2)
si liste[indice_centre] == val:
retourne vrai
sinon:
si val > liste[indice_centre]:
indice_gauche ← indice_centre + 1
sinon:
indice_droit ← indice_centre - 1
retourne faux
Exercice 4
- Ecrire une fonction
recherche_dichotomique
qui reçoit en paramètre une liste nomméelst
et l'élément recherché nomméelm
et qui retourne un booléen en fonction de la présence ou non de l'élément recherché. - Testez votre script avec les deux exemples traités dans l'exercice 1
Exercice 5
Voici une liste de prénom d'ami.e.s :
liste_amis = ['Ariel','Mehdi','Amir','Melvina','Hector','Zoé','Yasmine','Ursula','Andréa','Marie-Claire','Marc-Aurèle','Jim','Paula' 'Pietro','Xavier','Norah','Arthur','Firmin','Alice','Bob','Firdaous','Olga','Shinzo','Li','Mathéo','Rachel','Sabrina','Tao']
-
Trier par ordre croissant cette liste
liste_amis
. -
Utilisez votre programme de l'exercice 4 afin de savoir si 'Paulo' fait partie de la liste
liste_amis
. -
Utilisez votre programme de l'exercice 4 afin de savoir si 'Yasmine' fait partie de la liste
liste_amis
.
2.3 Complexité⚓︎
Nous avons vu précédement que l'algorithme naïf de recherche d'un nombre dans une liste triée avait dans le pire des cas une complexité linéaire. Voyons si la dichotomie est plus avantageuse de ce point de vu.
Exercice 6
Déterminez le nombre maximal de comparaisons nécessaires à la recherche d'un élément dans une liste, en complétant le tableau ci-dessous.
Taille de la liste | 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | N |
---|---|---|---|---|---|---|---|---|---|---|
Recherche séquentielle | ||||||||||
Recherche dichotomique |
Exercice 7
Ecrire une fonction nb_de_tours(n)
qui renvoie le plus petit entier \(k\) tel que \(2^k > n\), c'est-à-dire le nombre maximal de valeurs examinées par la recherche dichotomique dans un tableau de taille \(n\).
Saisir en argument successivement plusieurs puissances de 2. Que remarquez-vous quant au résultat affiché ?
Exercice 8
Exercice pour les plus rapides ? Qui reprend le jeu introductif.
Ecrire un programme qui permette à l'ordinateur de jouer contre l'utilisateur pour retrouver un nombre. L'utilisateur choisira un nombre entre 0 et 100 et l'ordinateur devra le trouver, le plus efficacement possible. A chaque proposition faite par l'ordinateur, l'utilisateur devra donner une réponse sous la forme d'une chaine de caractères codant explicitement les notions de "plus grand", "plus petit" ou "bravo, tu as trouvé !".
2.4 Terminaison⚓︎
Dans l'algorithme précédent, nous avons une boucle, nous devons donc nous interroger sur la terminaison de cet algorithme.
Dans la boucle tant que, il y a la condition indice_gauche <= indice_droite
qui nous indique que la liste n'est pas vide. On va s'intéresser à la variable longueur = indice_droite - indice_gauche
qui est un entier. En utilisant cette nouvelle variable, la condition indice_gauche <= indice_droite
devient longueur >= 0
.
Variant de boucle
On appelle variant de boucle une quantité entière qui :
- est positive ou nulle lorsque l'on reste dans la boucle;
- décroît strictement à chaque itération de la boucle.
Un variant de boucle sert à prouver la terminaison d'une boucle, c'est-à-dire que l’on sort nécessairement de la boucle au bout d’un nombre fini d’itérations puisqu'un entier positif ne peut décroître infiniment.
Preuve de terminaison
Positif ou nul: comme longueur
est la différence de l'indice de droite et de l'indice de gauche, c'est une quantité positive ou nulle.
Décroissant: On commence par définir indice_centre ← partie_entière((indice_gauche + indice_droite)/2)
.
A chaque passage dans la boucle, il y a trois cas possibles:
- si
liste[indice_centre] == val
, on sort directement de la boucle. La terminaison est assurée. - si
liste[indice_centre] > val
, on modifie la valeur de droite. En appelantindice_droite2
cette nouvelle valeur, on a :indice_droite2 - indice_gauche < indice_centre - indice_gauche <= indice_droite - indice_gauche
carindice_droite2 = indice_centre - 1 < indice_centre
Ainsi, le variant a strictement décru. - sinon, on modifie gauche et on a de même :
indice_droite - indice_gauche2 < indice_droite - indice_centre <= indice_droite - indice_gauche
. De même, le variant a strictement décru.
Donc longueur
est bien un variant de boucle et algorithme termine bien.
sources : - https://www.monlyceenumerique.fr/nsi_premiere/algo_a/a5_dichotomie.php