Aller au contenu

Algorithmes de recherche dichotomique 🔪

Programme

Notions Compétences Remarques
Recherche dichotomique dans un tableau trié Montrer la terminaison de la recherche dichotomique à l’aide d’un variant de boucle. Des assertions peuvent être utilisées.
La preuve de la correction peut être présentée par le professeur.
Activité 1 - Je joue contre l'ordinateur

Exécuter le programme du jeu du nombre mystère. Faire quelques parties, expliquer la stratégie de l'ordinateur pour trouver le nombre mystère.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Solution
A chaque fois, il se place au milieu.

Lorsque le nombre est compris entre 1 et 100, en combien d'essais au maximum l'ordinateur trouve-t-il la solution ?

Solution

En 7 essais.

Et si le nombre mystère est compris entre 1 et 200 ?

Solution

En 8 essais.

Définition et principe de la dichotomie

Le mot dichotomie vient du grec ancien διχοτομία, dikhotomia (« division en deux parties »).

La méthode de dichotomie fait partie des méthodes dites «diviser pour régner».

«dichotomie» se dit en anglais binary search.

Dichotomie, déroulement intuitif

  • on se place au milieu de la liste.
  • on regarde si la valeur sur laquelle on est placée est inférieure ou supérieure à la valeur cherchée.
  • on ne considère maintenant que la bonne moitié de la liste qui nous intéresse.
  • on continue jusqu'à trouver la valeur cherchée (ou pas)

Comprendre la méthode de dichotomie est relativement simple, mais savoir la programmer est plus difficile.

Pour des raisons d'efficacité, nous allons garder intacte notre liste de travail et simplement faire évoluer les indices qui déterminent le début et la fin de notre liste.

Une autre méthode pourrait être d'extraire à chaque étape une nouvelle liste (dont on espère qu'elle contient la valeur cherchée), mais la technique utilisée (le slicing de liste) consomme beaucoup trop de ressources.

Nous allons donc travailler avec trois variables :

  • indice_debut (en bleu sur le schéma)
  • indice_fin (en bleu sur le schéma)
  • indice_central, qui est égale à (indice_debut + indice_fin) // 2 (en rouge sur le schéma)

Dans cet exemple nous cherchons 14 dans la liste triée [2, 3, 6, 7, 11, 14, 18, 19, 24].

indices dichotomie

Nous allons faire se rapprocher les indices indice_debut et indice_fin tant que indice_debut <= indice_fin

Activité 2 - Recherche d'appartenance

Compléter la fonction appartient_dichotomique qui prend en paramètre une liste Python ma_liste et un valeur valeur. Cette fonction renvoie True si valeur est dans ma_liste et False sinon.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

.128013rt=7flb)1 2e/vyg:,-]8Tsw9nuph5aS(o3qFk_i4+6P;[cdm0050W0m0c0F0O0g0x0k0V0g0F0x0x0d010c0O0C010406050x0B0X0X0F0b0p040G0I0g0B0?0I0A050n0}0 11130{0C04051j1c1m0n1j0{0W0O0o0+0-0/0;0D0O0q0D0g1A0D0c0_050$0h0g0m1v0.0:011z1B1D1B0c1J1L1H0c0b1k0c0D0+160x0C0F0A0;0l011N1x010f0(0m0A0F0X0m1H1*1,1;1P1@1L1`1|0_0a0k0S0b0I0C0I0x0O190A0k0!1(0b0b0m0V2h1c1 0A1k0n1$2u1Z1#1!1I0W210;1D0A1_2e1H1s1u0,1O2E0O2G0A0I2K1H0C2n1k2s2u2Y0|1+2i2M1=2R0b100g0_0k0j2r2$0`2#202(1P2*2,2.0l2;1,2?2s2D012{0F2-040k0J2 2t0{322_0;35370k0P3b312$333h2.0E3l3d3n3f340I2+362.0R3s2@2%1w2`3x2|380e3C3e3F3g3H3z380v3L3u3N3w3y3i0z3T2^3V3p040j0Y3!3E2N3W3I0j2:1d2=3t3#3-3%0j2~3=303@3,2)3P370j3a3}2t1n2W1c2K2x0W1#2C3v0V2S1}1k4b1l492!461k4h0!2X3U3-0M0_0!0f3l0k3D3o0f0_0F0C1+0b0?1_0c0N1s0V0D0I0c0I0X0O0K0B0m3l4C3v0^040H4Z3M3_0_100N1D0x0c4Y4p4!3V4$0s4A4@4+040o360m0B0b4)4u1=4$0i4{4*560_0r3s0k5f4B5a2`0_2P4O0m4N0m0h1859551P0I0_0d5s3^1=4U0_3*4p065g5h5t3g5k0A5m0N1@1b4p5H5z5u5w5y402`0h0_24545S0;4$4(4?5i5J044-4/4;5#5W5%0_585Q4|1=5v040t5V335B3(5e5g5`1P4w040y1z1L5 3v0A5K5M0!5q0c6c3V5|020g0c0T5x5_5+346f0O0V5n5O6k3-4$5d5E5G5G655,5l6w5n6x0A1Z4=2Y5R5=015|6r6Q6H015(5;3o6v6x5o6i6A5{0_0Q6*5j046J6%6z5*[email protected];5|0n0n6{01613|2Y5F6F5f6X6e4~50520N6M1Z50706U70795.0O4:6P2=6X4$0U6!6d6$6L4L2n7t4^0_0u63766X670O4z6s6^794 1L7c7e0b7g7J5$6T5w6V2=6R6#7a7N537S6S6C7D76646t672n0c525P6W7-0V0_0w0b4X7*6G7-5k7I7?7K0_7M510b7d7x7R827T6m6o0T7j847b7$8b7(5c7}7+787v6(5r7%337i8t7u6:5L6K886N7y8w6l6,70613;747+7Y3v670m0)7o308M7A046D8K8L7,838y5M6?8k8u5U8E4}6;7w8C8R2t8T3-5|5~8+5A0O0_8J3?7~6^7.0#7;700M7^040L360x8:2?0n4r0m2u2V9f4a1t4c2x2A2v0F1K9i0n4b0{9s0#0%0)04.
Activité 3 - Déroulé à la main : v = 9 est-il dans t = [1, 3, 6, 9] ?

Écrire le déroulé à la main. Voici le début à poursuivre de façon analogue :

  • indice_debut = 0
  • indice_fin = 3
  • condition du while : True
  • indice_centre = (0+3)//2 = 1
  • valeur_centrale = ma_liste[indice_centre] = 3
  • v == valeur_centrale → False
  • valeur_centrale < v → True
  • indice_debut = 2
  • condition du while : True
  • ...
Solution
  • indice_debut = 0
  • indice_fin = 3
  • condition du while : True
  • indice_centre = (0+3)//2 = 1
  • valeur_centrale = ma_liste[indice_centre] = 3
  • v == valeur_centrale → False
  • valeur_centrale < v → True
  • indice_debut = 2
  • condition du while : True
  • indice_centre = (2+3)//2 = 2
  • valeur_centrale = ma_liste[indice_centre] = 6
  • v == valeur_centrale → False
  • valeur_centrale < v → True
  • valeur_debut = 3
  • condition du while : True
  • indice_centre = (3+3)//2 = 3
  • valeur_centrale = ma_liste[indice_centre] = 9
  • v == valeur_centrale → True
  • la fonction renvoie True
Activité 4 - Déroulé à la main : v = 7 est-il dans t = [1, 3, 6, 9, 10] ?

Écrire le déroulé à la main, comme dans l'exercice précédent

Solution
  • indice_debut = 0
  • indice_fin = 4
  • condition du while : True
  • indice_centre = (0+4)//2 = 2
  • valeur_centrale = ma_liste[indice_centre] = 6
  • v == valeur_centrale → False
  • valeur_centrale < v → True
  • indice_debut = 3
  • condition du while : True
  • indice_centre = (3+4)//2 = 3
  • valeur_centrale = ma_liste[indice_centre] = 9
  • v == valeur_centrale → False
  • valeur_centrale < v → False
  • indice_fin = 2
  • condition du while : False
  • la fonction renvoie False

👉 On voit dans cet exemple pourquoi l'instruction while indice_debut <= indice_fin : est absolument nécessaire.

Activité 5 - Recherche d'indice

Compléter la fonction recherche_dichotomique qui prend en paramètre une liste Python ma_liste et un valeur valeur. Cette fonction renvoie son indice si valeur est dans ma_liste et None sinon.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

.128013rt=7flb)1 2e/vyg:,-]8sw9nuph5aS(o3qNk_i4+6P;[cdm0050V0m0c0E0N0g0w0k0U0g0E0w0w0d010c0N0B010406050w0A0W0W0E0b0p040F0H0g0A0=0H0z050n0|0~10120`0B04051i1b1l0n1i0`0V0N0o0*0,0.0:0C0N0q0C0g1z0C0c0^050#0h0g0m1u0-0/011y1A1C1A0c1I1K1G0c0b1j0c0C0*150w0B0E0z0:0l011M1w010f0%0m0z0E0W0m1G1)1+1:1O1?1K1_1{0^0a0k0R0b0H0B0H0w0N180z0k0Z1%0b0b0m0U2g1b1~0z1j0n1#2t1Y1!1Z1H0V200:1C0z1^2d1G1r1t0+1N2D0N2F0z0H2J1G0B2m1j2r2t2X0{1*2h2L1;2Q0b0 0g0^0k0j2q2#0_2!1 2%1O2)2+2-0l2:1+2=2r2C012`0E2,040k0I2~2s0`312^0:34360k0O3a302#323g2-0D3k3c3m3e330H2*352-0Q3r2?2$1v2_3w2{370e3B3d3E3f3G3y370v3K3t3M3v3x3h0y3S2@3U3o040j0X3Z3D2M3V3H0j2/1c2;3s3!3,3$0j2}3;2 3?3+2(3O360j393|2s1m2V1b2J2w0V1!2B3u0U2R1|1j4a1k482Z451j4g0Z2W3T3,0L0^0Z0f3k0k3C3n0f0^2m0U0C0m0b4G0m0M1r4G0H0c0H0W0N0J0A0m3k4B3u0@040G4X3L3^0^0 0M1C0w0c4W4o4Y3U4!0s4z4=4)040o350m0A0b4%4t1;4!0i4_4(540^0r3r0k5d4A582_0^2O4N4L0Z0h1757531O0H0^0d5p3@1;4S0^3)4o065e5f5q3f5i0z5k0M1?1a4o5E5w5r5t5v3 2_0h0^23525P0:4!4$4;5g5G044+4-4/5Y5T5!0^565N4`1;5s040t5S325y3%5c5e5@1O4v040x1y1K5|3u0z5H5J5m5o5?5(015_020g0c0S5u6g5F336c0N0U4L5L694?5a605D5d625)5j6t4L6u0z1Y4:2X5O5/6i5R6p5Z015#5.3n6s6u4M0m5n0c6x3,5_0P6%2(6X6v2O6V4Z5;6+5Q040n0n6?0:5~3{2X5C6B6C6h6b4|4~500M6I1Y4~6{6P046o6M6D6r5*0E4,0N4.6L2;7h4!0T6:3#6-781^6K7t3,4!0u6A6B7h640N4y6R6O744}1K77790b7b7J325_0d7f2;6N6W757N517S6;045b5B71717F4E0!505M7g737v7P7o3}7,7Y3u7G7I7?6q7L760b7w6J7Q687%3U6j6l0S7c837#7c4!7*6 7|5D7h746F6Y6e6$8a6(6Q816S8p5I6G867y8u5^0^6*8E1O5~3:8l7,7.040m0(7`2s7}6y7)7D7|8o7v6w8I0:7U8f7^7x2m7c5_5{8$018K7D8O2m0c7;7c0L0U0^0K198S2=0n4q0m2t2U95491s4b2w2z2u0E1J980n4a0`9i0!0$0(04.
Activité 6 - Une fête

Nicolas organise une fête, et demande à ses amis s'ils viendront. Dès qu'un ami lui répond favorablement, il l'ajoute dans liste_amis. Compléter le code ci-dessous afin de pouvoir déterminer si Vincent, Romain et Valérie ont décidé de venir (bien respecter les majuscules, minuscules et accents). La liste liste_amis est dans du code caché.

Vous devez absolument réaliser une recherche dichotomique et pas une recherche naïve. Attention, c'est à vous de créer ma_liste_amis qui est utilisée dans les tests. (Vous pouvez regader l'astuce plus bas, en cas de besoin)

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

.128013rt=7flb)1 2e/vyg:,-]8Tsw9nuph5aS(o3qFk_i4+6P;[cdm0050W0m0c0F0O0g0x0k0V0g0F0x0x0d010c0O0C010406050x0B0X0X0F0b0p040G0I0g0B0?0I0A050n0}0 11130{0C04051j1c1m0n1j0{0W0O0o0+0-0/0;0D0O0q0D0g1A0D0c0_050$0h0g0m1v0.0:011z1B1D1B0c1J1L1H0c0b1k0c0D0+160x0C0F0A0;0l011N1x010f0(0m0A0F0X0m1H1*1,1;1P1@1L1`1|0_0a0k0S0b0I0C0I0x0O190A0k0!1(0b0b0m0V2h1c1 0A1k0n1$2u1Z1#1!1I0W210;1D0A1_2e1H1s1u0,1O2E0O2G0A0I2K1H0C2n1k2s2u2Y0|1+2i2M1=2R0b100g0_0k0j2r2$0`2#202(1P2*2,2.0l2;1,2?2s2D012{0F2-040k0J2 2t0{322_0;35370k0P3b312$333h2.0E3l3d3n3f340I2+362.0R3s2@2%1w2`3x2|380e3C3e3F3g3H3z380v3L3u3N3w3y3i0z3T2^3V3p040j0Y3!3E2N3W3I0j2:1d2=3t3#3-3%0j2~3=303@3,2)3P370j3a3}3c3D3o420_0j3k463m3^413X4b3r4e3 494i3(3B4e1n2W1c2K2x0W1#2C3v0V2S1}1k4v1l4t2!4r4B0!2X3U3_0_100N1D0x0c0m0N1{0O0x3l0k483v0I0_0d4!4$3$0h0_0x3x4U0W3l4,3-0^040H4@3M4O044S4U4W0X4Y4}4N1=4`0i3s064m3v0M0_0!0f4+4~2)0f0_0F0C1+0b0?1_0c0N1s0V0D0I0c0I540K0B0m564g1P4`4|4r5k2`4P0F4R4Y4U5G405I0_0s5j575N040o360m0B0b5T33595Y5H0;4`0r3s0k5@4#5M3g0_2P5w4V0!0h185.5U0;4(044*4e5_5Z0;540_3*4l5^6a5/345|0A5~0N1@1b694^1=66682Y6i64344.501_5+3v5J6D3$5O5Q4T5F5L6b015-6r5`01660t63336d3(5?5^6s1P5f040y1z1L6V3v0A6l6n60626Q6N66020g0c0T6v2=6x3o6/0O0V4V6p6,3V5;6Z6h5@6#5{045}724V730A1Z6L6w7c6S4)764_0_5K2!6R6.7e6m7g5v0m610c7q6t0_0Q7E5!7f736o2P6G7r045a6?6j660n0n7I6c0O0_3|2Y067a6!7v0_5$1L5)0N7i1Z5%7X7o677?7w4Q517l2=7n4`0U7O2)717L7:2n825V040u797a7n6%0O5i7S6y7w7,5(0b7/5t0b7=8i336u6}306 6-7+5%5)7?786g7(8d6R6%2n0c5)6q7m8H0V0_0w0b5E8c6h8e5|8h8N6N8k8A8n868r8Z7T0_6_6{7_8z7-5*8s6E0_5=8E8F7)8!845 7B6=8*6y6u8/7x6n867}8w7n667H8?3V6X3;7$8{8W040m0)992t8x778^8U8F7n7w7K747N9e3-949A83967z987?6T7?9g8c9k8J8L7?0M8P040L360x9o2?0n4K0m2u2V9#4u1t4w2x2A2v0F1K9(0n4v0{9=0#0%0)04.
Astuce

N'y-a-t-il pas une condition sur la liste dans laquelle on réalise la recherche dichotomique ?

Solution pour Vincent, Romain et Valérie
>>> appartient_dichotomique(ma_liste_amis, "Vincent")
False
>>> appartient_dichotomique(ma_liste_amis, "Romain")
False
>>> appartient_dichotomique(ma_liste_amis, "Valérie")
True
>>> 
Logarithme de recherche par dichotomie : à savoir refaire 💚
def recherche_dichotomique(ma_liste, valeur) :
    indice_debut = 0
    indice_fin = len(ma_liste) - 1
    while indice_debut <= indice_fin :  # (1)
        indice_centre = (indice_debut + indice_fin) // 2  # (2)
        valeur_centrale = ma_liste[indice_centre]
        if valeur_centrale == valeur :
            return indice_centre
        if valeur_centrale < valeur :
            indice_debut = indice_centre + 1  # (3)
        else :
            indice_fin = indice_centre - 1  # (4)
    return None
  1. ⚠ Il faut bien <= et pas <
  2. ⚠ Il faut une division entière donc // et pas /
  3. 👉 On cherche à droite
  4. 👈 On cherche à gauche

Prenez le temps de lire les commentaires (cliquez sur les +)

Terminaison de l'algorithme de recherche dichotomique

Est-on sûr que l'algorithme va se terminer ?
La boucle while qui est utilisée doit nous inciter à la prudence.
Il y a en effet le risque de rentrer dans une boucle infinie.
Pourquoi n'est-ce pas le cas ?


Aide : observer la position des deux flèches bleues lors de l'exécution de l'algorithme image

La condition de la boucle while est indice_debut <= indice_fin, qui pourrait aussi s'écrire indice_fin >= indice_debut.
Au démarrage de la boucle, on a :

    indice_debut = 0
    indice_fin = len(L) - 1

Ceci qui nous assure donc de bien rentrer dans la boucle.

Ensuite, à chaque étape, les deux variables indice_debut et indice_fin vont se rapprocher jusqu'à ce que le programme rencontre un return ou bien jusqu'à ce que indice_fin devienne inférieur à indice_debut.

Ceci nous assure donc que le programme va bien se terminer.

Variant de boucle

On dit que la valeur indice_fin - indice_debut représente le variant de boucle de cet algorithme. Ce variant est un nombre entier, d'abord strictement positif, puis qui va décroître jusqu'à la valeur 0.

Activité 7 - Complexité de l'algorithme de recherche dichotomique

Nous allons considérer que la complexité est due au nombre d'étapes nécessaires pour obtenir le résultat.

Quel est le pire des cas de recherche dichotomique d'une valeur dans une liste triée ?

Le pire des cas est lorsque l'élément n'est pas présent dans la liste.

Combien d'étapes (au maximum) sont-elles nécessaires pour arriver à la fin de l'algorithme ?

Imaginons que la liste initiale possède 8 valeurs. Après une étape, il ne reste que 4 valeurs à traiter. Puis 2 valeurs.
Puis une seule valeur.
Il y a donc 3 étapes avant de trouver la valeur cherchée.

Combien d'étapes en fonction de la taille de la liste ?

1. Remplissez le tableau ci-dessous :

taille de la liste 1 2 4 8 16 32 64 128
nombre d'étapes _ _ _ 3 _ _ _ _
Solution
taille de la liste 1 2 4 8 16 32 64 128
nombre d'étapes 0 1 2 3 4 5 6 7

2. Pouvez-vous deviner le nombre d'étapes nécessaires pour une liste de 4096 termes ?

Solution

12 étapes

3. Pour une liste de \(2^n\) termes, quel est le nombre d'étapes ?

Solution

\(n\) étapes

Nombres d'étapes pour une liste de taille \(n\)

Sachant qu'à chaque itération de la boucle on divise à peu près (division entière) le tableau en \(2\), cela revient donc à se demander combien de fois faut-il diviser le tableau en \(2\) pour obtenir, à la fin, un tableau comportant un seul élément ?
🙃 Autrement dit, combien de fois faut-il diviser n par 2 pour obtenir 1 ?

Le logarithme en base 2

Une mesure de la complexité est donc le nombre \(k\) tel que \(2^k=n\).

Nous n'allons pas rentrer dans les détails, mais il faut savoir qu'il existe une fonction mathématique (réciproque de la fonction qui à \(x\) associe \(2^x\)) qui permet de résoudre ce problème :
la fonction "logarithme en base 2" notée \(\text{log}_2\)

\(k=\text{log}_2(n)\)

log2

La courbe en rouge correspond à la complexité de la recherche dichotomique (logarithmique en base 2), et la droite verte à celle de la recherche séquentielle (linéaire).

Attention calculatrices

les calculatrices ont une touche log et une touche ln qui donnent respectivement le logaritme en base 10, et le logarithme en base \(\text{e}\).
On peut obtenir le résultat du logarithme en base 2 d'un nombre de la façon suivante, par exemple pour calculer \(\text{log}_2(1024)\) :
( log 1024 ) \(\div\) log 2
ou bien
( ln 1024 ) \(\div\) ln 2



Complément sur les fonctions logarithme
  • La fonction \(\text{log}_2\) est la fonction réciproque de celle qui à tout réel \(x\) associe \(2^x\)
  • La fonction \(\text{log}\) est la fonction réciproque de celle qui à tout réel \(x\) associe \(10^x\)
  • La fonction \(\text{ln}\) est la fonction réciproque de celle qui à tout réel \(x\) associe \(\text{e}^x\)
Le logarithme en base 2 en Python

En Python, il suffit d'importer la fonction log2

Tester le script ci-dessous :

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Complexité logarithmique de la recherche dichotomique 💚 A retenir

La recherche dichotomique se fait avec une complexité logarithmique.

On rencontrera parfois la notation \(O(\log_2(n))\).

Le \(O\) se prononce "grand O" (la lettre)

Cette complexité est bien meilleure qu'une complexité linéaire. Le nombre d'opérations à effectuer est très peu sensible à la taille des données d'entrée, ce qui en fait un algorithme très efficace.

N'oubliez pas que dans le cas de la recherche dichotomique, le tableau doit être trié !

Activité 8 - Temps de calcul

Recopier et exécuter le code suivant dans votre éditeur python local (pas en ligne)

from timeit import timeit

def dicho(tableau: list, x: int) -> bool :
    """
    :param tableau: une liste d'entiers triés par ordre croissant
    :param x: de type int
    :returns: bool : True si x est dans tableau, False sinon

    >>> dicho([1, 3, 4, 9], 4)
    True
    >>> dicho([1, 3, 4, 9], 11)
    False
    """
    deb = 0
    fin = len(tableau) - 1
    mil = (deb + fin) // 2

    while deb <= fin :

        if tableau[mil] ==  x:
            return True
        elif tableau[mil] < x:
            deb = mil + 1
        else :
            fin = mil - 1
        mil = (deb + fin) // 2
    return False

tailles = [500, 2500, 12500, 62500]

# tref est le temps de calcul pour une liste de taille 100
liste_ref = [i for i in range(100)]
tref = timeit("dicho(liste_ref, -1)", number = 1000, globals = globals())
print("n = 100 -> tref = ",round(tref, 6))

for n in tailles :
    print("n =", n, end='')
    tab = [i for i in range(n)]
    # Calcul du temps pour des listes triées de tailles n
    t = timeit("dicho(tab, -1)", number = 1000, globals = globals())

    print('\t-> temps = ',round(t, 6), '\t x', round(t/tref, 2) )
    tref = t
Que remarque-t-on?

A chaque étape, n est multilié par 5, et on voit dans le tableau que le temps est multiplié par un nombre très inférieur à 5.

Un exemple spectaculaire de l'efficacité de la recherche par dichotomie

terre

Imaginons un annuaire qui contienne les noms, prénoms, adresses.....des 7 milliards d'êtres humains vivant sur la terre.

🤔 Quel est le nombre maximum d'étapes pour trouver un individu ?

Plaçons nous dans le pire des cas.

Comme à chaque étape on divise le nombre de personne par 2, la question revient à : combien de fois faut-il que je divise 7 milliards par 2 pour qu'il n'en reste qu'un ?

Cela revient à trouver \(n\) tel que \(\dfrac{7000000000}{2^n}=1\) , c’est à dire \(2^n=7000000000\) .

A la calculatrice on voit que \(2^{32}=4294967296\) et que \(2^{33}=8589934592\).

Il faut donc au maximum 33 étapes

🌵 Un algorithme qui balaye la liste du début à la fin aurait fait 1 étape pour la première personne et 7 milliards d'étapes pour la dernière !

Un algorithme par parcours séquentiel de liste aurait nécessité, dans le pire des cas 7 milliards d’étapes. L’algorithme par dichotomie qui n'en nécessite que 33 est donc énormément plus efficace.

Cependant

Cependant, il ne faut pas perdre de vue que dans le cas de la recherche dichotomique, il est nécessaire d'avoir un tableau trié.

Attention

Si au départ le tableau n'est pas trié, il faut rajouter la durée du tri.

Activité 9 - Compléter la fonction dichotomie
  • prenant en paramètre un tableau de nombres triés dans l'ordre croissant nombres et une valeur cible

  • renvoyant True si cible est une valeur de nombres, False dans le cas contraire.

Exemples

>>> dichotomie([1, 2, 3, 4], 2)
True
>>> dichotomie([1, 2, 3, 4], 1)
True
>>> dichotomie([1, 2, 3, 4], 4)
True
>>> dichotomie([1, 2, 3, 4], 5)
False
>>> dichotomie([1, 2, 3, 4], 0)
False
>>> dichotomie([1], 1)
True
>>> dichotomie([1], 0)
False
>>> dichotomie([], 1)
False

Remarque

Vous utiliserez obligatoirement un algorithme de recherche dichotomique.

Compléter ci-dessous

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

.128013ÀtèlO1{2à/vyg.]Tsw9huqù}i6Pé[-Ndm0rA=7fb)ID êe:,8Rnp5(aS^oF3Lk_4+z;cx050G0U0c0%0z0e0r0S0@0e0%0r0r0L010c0z0!010406050r0v0H0H0%0J0m040(0*0e0v190*0Z0S020%0H0!0?0S0Y0U1j0J0w0v0U0r050k1g1i1k1m1e0!04051R1K1U0k1R1e0G0z0l111315170u0z0n0u0e1,0u0c1c050|0O0e0U1%1416011+1-1/1-0c1^1`1?0c0J1S0c0u111p0r0!0%0Z170i011|1)010N0~0U0Z1x0U1?2f2h2m1~2p1`2s0H2u040a0S0B0J0*0!0*0r0z1s1u0`2d0J0J0U0@2P1K2w0Z1S0k2b2#282a291@0G2y171/0Z2r2M1?1!1$121}2/0z2;0Z0*2^1?0!2U1S2Z2#361f2g1u2`2n2 0J1j0e1c0S0g2Y3a1d392x3c1~3e3g3i0i3l2h3n2Z2.013s0%3h040S0,3w2!1e3z3q173C3E0S0:3I3y3a3A3O3i0#3S3K3U3M3B0*3f3D3i0A3Z3o3b1(3r3(3t3F0M3-3L3:3N3=3*3F0X3_3#3{3%3)3P0t413p433W040g0I483/2{443?0g3k1L3m3!494h4b0g3v4m3x1V341K2^2(0G2a2-3$0@302E0_1#1S330U353m3S054F0`4N4p2n0.1c0`0N3S0S3.3V0N4X0z0@0u0*0c0*0H0z0U4P3`4h1b040$4?424q1c2 0H0O2U1J4u2!4$3$4_0W4!574a1c0@0z1_4=554T4g2n4_0P0V3Z0S5r4#4@3d4X0U0O1r5b5u1~0*1c0L5A4}2n4:1c4e5j065s5t5H3r1c2p0Z5G4U5C5E5V5l3r0O1c2B4|5W174_4{5j5c4~0450521I5)5!5+1c0P5Z3A5D040E5}3$5J4c5q5s5/4V1c0s1+1`625d040`5y0c6e4h5 020e0c0?5F5j5P5*3B5S2}5^3A4_5p5M5O5O685R044:1/0U0v6k2n5 6r366t5_015,6y3$0Z6w5U6s6F175 0;6M6G6h5z5.5B5`045|6-5Q6$1c0k0k6V43644t365N6D5r6#014W040z4Z6!6.6v045f5h6)6@040L6P3m6R3V4 4/5?54387a4_0D6{5:6I4;6L6=6u4_0p6B6 717173752U0c0v0J6Z6Q7I0@1c0q0J1H667H7a750U1/787P7a6X7c5g6d796?016m0n6p7f7b5=537v5m1c7u7A6S7)7x6K7`1~7C7E4n7G6D737)6+6j7-6u6O7?800~7y7?6%7?644l7F7G7I1c7!0r5i7r7.6A7W887l6W6Y8l5Y8e7 1c817z7%7.5 618H3A8o8A8s047K7M7O7k7Q1c0+3D8v3-0k4R4M4w8,0k4z1K0c4B8;2+2$0%5h2#4z1Q5k3A2U0H0/0N0%0.0U0/0u0,1c1C1E1G1I0S864v1X3n2^3A0%0G0H1t2O0z1t0S2}0N5 1Q9m9o9q2P0E190c26041C4+0U0J9J0S2g0J0S1!4+4-4/4;1V3n1R0Q0e0S0r000%0n2O9Q000v1u3D0n3(2O0u2D2y0z9e0o0S0-1{250U0%0v9N0J0z102r9N1k1x0d281{0G0*9*1f281t0n040*1_1,0%4.0z912r0c0S0Tac0S280z0C2Yai0Zak0o9W8~0b110u0%9e9u0c0C0Jap9s0Z0W0S9t8v9Q1D2h2R2N0S130S0l3D6K9P0@ataR1`0S1Iau0C0na,0S0ja(0%4#8+7*7e8*4G3F6B1Y050v0e3n1/04a%0*0v0zaV9t2U0Z0laf1{0z1i0C1!ap1Datb0b57T7Vb44S1Kbd1ebda%2 aU110{0c1{7da=a@9#afau5pbabc0zbe0v0!aQ7y2Ua|a~a*a,7MbK0Z28a{0V9u9!941raK9K9J9P0G2h10a)9O19ad2J2Oada29 a10S4{b18c0S0L0S8K0S0;3j1Kb16;bE05bd9vb#a-1{a}a)a+1`b,a/b.a;1{b;0~0Sb@aub{b{9Qb~b)c14;0Sa`0v9M0Gc78`1`c9cbb55Tcecg8j6K0S0EckbB4McnbX1e0kbVc;coc=bb8~0-a 0=1tad1{2UcJ0u1{0$0e000C0@1kaua:1{4Qc!6xc-3F0{bx4S8cclb50P9#d1bsbhaua}4+aM1HaOaQaS9tb;bf1u5T9*9O0S0%bnardl4Mc#6n6paZ6ido4SaV0@00bQaNa)df4SbO5ib10S0ZdZ0rau2g10b}0Zb 9~cWa00v9{0f1u0r3(au2Ra)0Obg121{dkbldMdeb18$0 d*b5aG9jc;8}4I2_439n9p0Z9r9t9v9x1T9zeqes0Z9D2O9G0F7ob%1j0^bq3DaZaybg0Jd=1{e5cS1`54b81R2I2=d@0S9-0Sd9db9+2}9Rbper19eKd%dP2}c*dT6,d+a_eKa}731k9=1jbM0^1c090$4d0)0t096;36aV0$a/0S1G0za?d/ay0J0d100naR0Z0G0PeieV040RcMa=0!ard010da0rbj0xb)d)d,d.d:14a30C8vb.0Ubj1u0J0C3Da59~bgd^104.7MeP0SeRe7102L157+1Ift3nc?cpbX0Ka24L4:9KeM7MfSdKe!1ue%0Jdcbmar9t2Re}a42bf00Uf204f4090NaR0@0hf60t0y0h0i0yf93SdXfKb(1!b%ga7ae~gdbuggf40#0S09192D10f60Xgu5.0kcobdf_aZ0veI0d9@f$0Jg0a e#g4g60lg82QdegCgc9?gFf30$gjgl0hgIgKbq9fgOgqgsgQ6QdYbQa}gzg/7.gDg=f1g@0ig|gLg 0I0)0Mh34OgSc:f@040K2}a%a$du1{0ohybDhpgUc7bl19gZfYf aW9.g2e$dag59+2rg,2J1ugbe g?ghg^gk0%gmgoh10)0h0,0I0ygtfa7kh5fjh7fAhPg7hS3jeic^f?1Rhsd@bicLeZ9-1{1/d/d0ay0}0ea=hUgEhdhXgohm3xaVa7c90!1qa6h^9th*f~eOhJ9#0}aua%d70C0e0C2Db.dOb27,d+bQ33fOatf;ek4Jek0{ia0r04.
Activité 10 - Déterminer l'indice d'un nombre dans une liste

On considère dans cet exercice des tableaux non vides contenant des nombres entiers, tous distincts, triés dans l'ordre croissant.

On cherche à déterminer l'indice d'une valeur cible dans ce tableau à l'aide d'une recherche dichotomique dans sa version itérative.

Écrire la fonction indice qui prend en paramètres le tableau de nombres tableau et la valeur cherchée cible.

Si la cible est dans le tableau, la fonction renverra son indice. Dans le cas contraire, la fonction renverra None.

Attention

Les tableaux des tests secrets peuvent être très grands. Une recherche linéaire naïve prendrait trop de temps lors de l'exécution.

Les tests secrets limitent le nombre de lectures dans le tableau à 100. Si votre code accède à plus de 100 valeurs dans le tableau, une erreur sera levée.

Exemples
>>> tableau = [23, 28, 29, 35, 37]
>>> indice(tableau, 23)
0
>>> indice(tableau, 29)
2
>>> indice(tableau, 37)
4
>>> indice(tableau, 10)
None
>>> indice(tableau, 100)
None
Question

Compléter le script ci-dessous :

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

.128013tCèlO12à/vyg.]TsEw9huqi6Pé[-Ndm0r=7*fb)ID e:,8Rnp5(aSo3Lk_î4+;cx050E0R0b0!0x0e0q0Q0/0e0!0q0q0I010b0x0X010406050q0v0F0F0!0H0l040#0$0e0v140$0W0Q020!0F0X0.0Q0V0R1e0H0w0v0R0q050j1b1d1f1h190X04051M1F1P0j1M190E0x0k0|0~10120u0x0m0u0e1%0u0b17050@0M0e0R1Y0 11011$1(1*1(0b1:1=1.0b0H1N0b0u0|1k0q0X0!0W120h011@1!010L0_0R0W1s0R1.2a2c2h1_2k1=2n0F2p040a0Q0z0H0$0X0$0q0x1n1p0=280H0H0R0/2K1F2r0W1N0j262W2325241/0E2t121*0W2m2H1.1V1X0}1^2*0x2,0W0$2:1.0X2P1N2U2W311a2b1p2=2i2`0H1e0e170Q0g2T3518342s371_393b3d0h3g2c3i2U2)013n0!3c040Q0%3r2V193u3l123x3z0Q0,3D3t353v3J3d0Y3N3F3P3H3w0$3a3y3d0y3U3j361Z3m3Z3o3A0J3(3G3+3I3-3#3A0U3;3W3?3Y3!3K0t3|3k3~3R040g0G433*2?3 3.0g3f1G3h3V444c460g3q4h3s1Q2 1F2:2Z0E252(3X0/2{2z0;1W1N2~0R303h3N054A0=4I4k2i0)170=0L3N0Q3)3Q0L172^1V0/0R4K3=4c16040Z4)3}4l17200R0!0v4/4P1_4,0T4V4X3X0W170/0x1;4(4p2V503~4,0N0S3U0Q5g4W4*384S0R0M1m4 5j1_0$170I5p4:2i0F0x174958185h5i5w3m172k0W5v4{125s045u5C5F5M3w0M172w4`4b2i4,4.5C5a4;044?4^5Y3v5c5L5Z5r170C5:3v5y174g31065E5(4Q170s1$1=5^515l5n0b663~5O020e0b0.5Q315S5;3I5I2^5-3X4,5e5C5~5E5h605H045y1*0R4_5R6x5N5t6b4+175$335q6m040=696I2i5O0-6S6y5J6p5b170N6W6G040j0j6%015`044o5}6v6w6N014R040x4U6E6@520454566,6d6f0.6,6 5+6D6M5G124,0B6Z5)6A0x6C7g5!170o6s6;6=6v6F3w6n5K6}7c015O6i3h6k3Q177i7k7x5T5O5@7I6l6-5z475f7r7t6_0R1*6|6j7t6 71657M3v7A7B3s7D675*0!565,5%6@7e7l6y7G7a4J7?7n7p4i7r7s6@6_2P0b0v0H7w7Y6~7F0_7j7{3s6u6=7T177V0q577b5T6r7R815g7Z685o7%3X7A778c6B8f2V7,6c176V8x3~6.5|806?7y840?87897C7T0/170D1o8n4i1F4M4H4r8(0j4u1F0b4w8-2$2X7/1=2W4u1L4O7N2P0F0*0L0!0)0R0*0u0%171x1z1B1D0Q7 4q1S3i2:3v0!0E0F1o2J0x1o0Q2^0L5O1L9i9k9m2K0C140b21041x0/0u0R0H9F1?4$0u0$0b0$5y9a9q0b0A0H0!140k1?0S0Q0i280W2n0+23571T1O040O0e0Q0q000!0m2J0Q2M0e9@0e0m3Z2J0u2y9{1?2P9J9I9G9{0x9F9N9P0x9R3lag1C0n1Q3i8+4E1U1W9v9l0W9n9p9r9t1Oas9x9o0W9z2J9C0(0!0Qa79Ga99K1?9}4#ac9K0v0Q7`am8`0(1?0/3y0/0v9;9|00aQ4%9{aT7`aU9U2J1?9O1m0R9s0x0{9a0e9a0{4A1d2m0@0x2P0q0n0Q0O540Q9p2~2F2H1?4L4B3v1{1)1+1-8{7E6z8d7H8a7y8z8J6J4-7^6O6Q8wbu7J8H8A046Y7=7y5/bx6T176*6,6.6:4J0j8%04al9f8`0f0H0T9{2cb09Hb40W0{0~0W0m9^9b2Mbe0m9V1I2K28b20Q1=0Q0cb%0~0Qb40ec00=0{b37j0Hca0q0bb 0x5y9T0Rb80caZa#a%4W8%bk1+1}1,2q7y6 aVbN5=5P6,5#bA7u6P5mbD8U6@6UbHbJ8o7NbMbE7N5ObQcB12bS4KbV4B3A0X6Ccg2b0Ha`ca0:0/0A0=0H0|0?0ba?0^c7aOcia=b80r1pa_0L0?b%2Ib b?6C0:9q0W4$9bbi2M3Xblcvbo8ucI6R5%c#4N0Q0?crbjdicubncx5T6 cPbUbW9=1ocgb_2c0E0qc36Cc?0q9Pa4dgaZ3~djdybp7-bC6acX7zbGd#dB6odpdEc(1m9{0A2b109Hb aI0k3ydNchcjclaW9.0Pb)c@0|0 bc36cpc0aPddaRa-aUbsaT2m0Q1m8d0q2ccgc40~0H0md{a)2^c_0Hd_c|9cdudhdUdx1~dz7Nczef736Hd(8vd!cT7(d%eO6q6KcGd)8T7+7t7KbHdZcGcScLbvbP6+d#cZd+c$bY9-1Me1b-d8e40{2~0A8m0W0bdM0Qbe1;0da42,9=1?2G8mf1e5a!b$ds1p0z0l261odMcae~cc0{0W00dGfb0{2Mc6e9c~c_b80p0$a^c-b%0#0xb fr1p0!d_9`1eau0Xa%0Aa5fp9P0Mb60Q0L0e9N0@e~b.f82Ef3a48m9V9`0!0Xc+aT6s9-9h3X9jatavd20:1s0X9C9uf`9wau9y9A9C0r0:1yg19,9gardw1|dW1O4s335%bW7t0m4,020m6ggsgugt1veUeMeJcDe,4Z6/0n5BeR8G040K0KbR7P480h3C6tgpgrgxgv0.gW4WbKdA7v4V8tcMeKgI4c0FgE0hgGgBgLgN5{0GgQgB8Ig+5xgO4a3vgq17gWh3gxgZcQbqe#d#cNeLbId*6j8F4c0/0g1703d/d;8m2ye~a5e42b549Tg 3Xa04!0W7X8#6@h104h4gw6gh67|bLeTg!eGgAhaeQe(g#hdeW597}046$hQ6)g?6/g%7thihkhm1^1yb3hra!ht2JfSf!cdhrc4dPb2hw3~hy6`hAh~4chEhGgXh5gzdncKeXg)04g{hScRhMh77-dCice)047Lg|6yh9ij6!hYgBe+iqcY7PbT8ggp5O0U0n0t0U0U0,0y0Y0y0J0,0%4f0Y0U0R0-0%0G3:gSdq8*4G8^ap4ti(ao0kan0=c60q04.
Activité 11 - Étude d'une panne

Une sonde interroge à intervalles réguliers l'état de fonctionnement d'un système électronique. Celui-ci peut être en marche ou en panne.

La sonde est programmée pour enregistrer les résultats de ses requêtes dans un log. Il s'agit d'un tableau de booléens (une list Python) dans lequel les valeurs True précèdent les False. La valeur True indique que le système est en marche, False qu'il est en panne.

Une panne nécessite une intervention humaine et ne peut donc pas disparaître seule : elle persiste jusqu'à la fin de l'enregistrement.

#         0     1      2      3      4
log = [True, True, False, False, False]

Lors d'une vérification on constate que le système est en panne : le log contient au moins une valeur False en dernière position. On se demande à quel moment a débuté cette panne.

Dans l'exemple précédent, le premier False est à l'indice 2 : la panne a débuté à l'instant 2.

Écrire la fonction indice_panne qui prend en paramètre le tableau de booléens log et renvoie l'instant du démarrage de la panne.

On garantit que le log n'est pas vide et que, au moment de la vérification, le système est en panne (la dernière valeur du tableau est False).

Attention

La panne du système a aussi corrompu le fichier de log. Vous ne pouvez pas lire plus de 500 valeurs dans celui-ci. Passé ce nombre de lectures, tout nouvel accès lèvera une erreur.

Il est donc important de bien concevoir votre algorithme car les logs utilisés dans les tests secrets peuvent être très longs : un milliard de valeurs !

Exemples
>>> indice_panne([True, False])
1
>>> indice_panne([False, False, False])
0
>>> indice_panne([True] * 10 + [False] * 100)
10
>>> indice_panne([True, True, False, False, False])
2
Question

Compléter le script ci-dessous :

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

.128013tèl12à/vygj.]sw9huqMi6Pé[-dm0r=7fb)D e:,8Rnp5(aSo3Lk_4+;cx050B0M0b0V0v0d0o0L0)0d0V0o0o0F010b0v0S010406050o0s0C0C0V0E0j040W0X0d0s0~0X0R0L020V0C0S0(0L0Q0M180E0t0s0M0o050h1517191b130S04051G1z1J0h1G130B0v0i0?0^0`0|0r0v0k0r0d1X0r0b11050.0I0d0M1S0_0{011W1Y1!1Y0b1*1,1(0b0E1H0b0r0?1e0o0S0V0R0|0f011.1U010H0:0M0R1m0M1(24262b1:2e1,2h0C2j040a0L0x0E0X0S0X0o0v1h1j0,220E0E0M0)2E1z2l0R1H0h202Q1}1 1~1)0B2n0|1!0R2g2B1(1P1R0@1/2!0v2$0R0X2*1(0S2J1H2O2Q2{14251j2,2c2;0E180d110L0e2N2 122~2m311:3335370f3a263c2O2Z013h0V36040L0Y3l2P133o3f0|3r3t0L0$3x3n2 3p3D370T3H3z3J3B3q0X343s370w3O3d301T3g3T3i3u0G3Y3A3#3C3%3V3u0P3+3Q3-3S3U3E0q3?3e3^3L040e0D3}3!2-3_3(0e391A3b3P3~46400e3k4b3m4d45323/3t0e3w4j3y3Z3K4o110e3G4s3I4e4n3`4x3N4A1K2_1z2*2T0B1 2Y3R0)2=2t0+1Q1H2^0M2`3b3H054R0,4Z4C1:0!110,0H3H0L4u3R0R0H112/1P0)0M0#2 2$4#3,4610040U513@4f110d0X0k574*0|540J0N3O0L5l4;522c4,040v4/4A5n582c0X0p112;0b4:4=3 5a5c5e4m1:540z5I3p0C0v11434H5o5K110n5j4A065m5#5v5f015q2J0b0s0E0R5D5U0|5P5R3O5!5m5E59040,0I1g5:5w1:0X110F615(5?045S2{5_5l5{32112e5/5u6f6365675J3C0I5a2g5N3R54565T623C5G5d6y5(5h6o3p64040A6G3R694a6c6d5%6p5)110p1W1,6L5F5}0M5 5C6k5;016I020d0b0(666(6z3q6h2/6u3^545Y6c5$5`6)0R115P1!0M0s6Y466I6:2{6R3p6w6_5|5~606;5(6I0%776g046i7f2c6F7j6S6I0h0h7n1:694i6}6~7c3R5q5s7z6A045b6C2}6)5L7r3g720:0v757J6*116K7u5O5Q417R5g5W7X5y11260B7,5z045B7X717L5H6D6S7Q7|3K7T74767 6v5W6|4c7E5$6l0|5*0-5-6j7b8b6?04737V837D6~8i5q0M1!5t8h6)7-7?0X6%8v6=7_7M7)017~7O8C818m8F545X5k896 8J7p6^7#3R797^8K7W8V3^6I7!8B687%6O887E8q118s0o0M8M11874k8Q8R5(7_7h8A3b7F8$6n8#5|8l8!8)7v117m952c6N3Y0h4%4Y4I9j0h4L1z0b4N9o2W2R0V1+9l4L1F4)6S2J0C0#0H0V0!4}0r0Y111r1t1v1x0L8_2P1K3c2*3p0V0B0C1i2D0v1i0L2/0H6I1F9V9X9Z2E0A0~0b1{040u9$9(0X9S9y0Z0V0L1!0o0b1-250`0y1-2g0L250E1m0c1}a8a20L0s2$a00va21-1}0va70m0L0K260=4|0?0_0A0d0g0O0?001x0b0L0l0s4{8m0*0L2G0S2g8=0E0L0gaiak2J0)0r0M0EaZ1-4{0r8z0X5P9N0m9|4K4V139m0-0/0;04.
Astuce (1)

Il s'agit d'une recherche dans un tableau trié : les valeurs True sont au début, les False à la fin.

Astuce (2)

Si le log ne contient que des valeurs False, il faut renvoyer 0.

Dans le cas contraire, l'indice cherché est l'unique indice i qui vérifie log[i - 1] and not log[i].