Diviser pour régner
Ce que ce chapitre apporte 5 points
- Mesurer au compteur de pas l'écart entre une recherche linéaire et une recherche dichotomique.
- Expliquer pourquoi diviser par deux à chaque étape change l'ordre de grandeur du coût.
- Écrire une recherche dichotomique correcte, dans sa version itérative et dans sa version récursive.
- Écrire et employer une fonction Tranche qui extrait une portion de tableau.
- Reconnaître le prix de la dichotomie, et les cas où il n'est pas payable.
Chercher une valeur dans un relevé de quarante-huit mesures demande jusqu'à quarante-huit examens si l'on procède case par case. Si le relevé est trié et que chaque essai élimine la moitié des candidats, six examens suffisent toujours. Sur la valeur cherchée plus bas, la première méthode lit quarante-sept cases et la seconde cinq, et le compteur de pas de la page traduit cet écart en 98 pas contre 29. Ce chapitre montre d'où vient l'écart, ce qu'il devient quand les données grandissent, et ce qu'il faut payer pour l'obtenir.
Le chapitre précédent a montré une récursivité qui réduit le problème d'un cran à chaque appel : une case de moins, un chiffre de moins, une unité d'exposant de moins. La réduction était réelle mais avare, et le nombre d'appels restait proportionnel à la taille des données. Il existe une autre façon de réduire, qui consiste à couper le problème en deux à chaque étape. Le nombre d'étapes s'effondre alors, et c'est cet effondrement qui fait la valeur de la méthode.
Deux recherches, deux comptes de pas
Soit un relevé de quarante-huit mesures rangées par ordre croissant, et la valeur 209 à retrouver. Elle se trouve en quarante-septième position, mais l'algorithme ne le sait pas. Voici d'abord la recherche la plus directe, qui examine les cases une par une et rend la position trouvée, ou 0 si la valeur est absente, puisqu'aucune case ne porte ce numéro.
programme principal
Le compteur annonce 98 pas. C'est cohérent, et le détail se relève sur la figure : quarante-sept tours de boucle à deux pas chacun, l'en-tête du Pour et le test Si, le Retourner qui les conclut, la ligne qui range le relevé, et la ligne d'appel franchie deux fois, à l'aller et au retour. L'algorithme est correct et ne demande rien de particulier aux données.
Voici maintenant la même recherche, sur le même tableau et pour la même valeur, menée autrement. À chaque tour, l'algorithme examine la case du milieu de la zone encore possible. Si cette case porte la valeur cherchée, c'est fini. Sinon, la comparaison indique de quel côté continuer, et l'autre moitié est éliminée sans avoir été regardée.
programme principal
Le compteur annonce 29 pas, pour la même réponse. Le rapport est de trois pour un, et il ne vient pas d'une écriture plus habile : il vient de ce que la boucle n'a tourné que cinq fois. La figure suivante rend ces cinq tours visibles en affichant chaque case examinée.
programme principal
milieu passe à l'orange, dans l'encadré Dichotomie placé sous le listing. Noter alors bas et haut : la zone encore possible compte haut - bas + 1 cases. Elle en compte 48 au premier examen, puis 24, 12, 6 et 3 : chaque examen raté la divise au moins par deux, et le cinquième tombe sur la bonne case.Remarquer aussi que
haut reste à 48 du début à la fin : la valeur cherchée est chaque fois plus grande que la case examinée, et c'est bas seul qui avance, de 1 à 25, 37, 43 puis 46.
Les cases examinées sont les positions 24, 36, 42, 45 et 47. Cinq cases sur quarante-huit, et les quarante-trois autres n'ont jamais été lues. La différence entre les deux algorithmes est exactement là : non pas dans la vitesse d'un examen, mais dans le nombre d'examens nécessaires.
Ce que dit l'écart entre 98 et 29. Le compteur ne récompense pas une astuce d'écriture. Un examen coûte même plus cher à la dichotomie : quatre à six pas, pour tester la boucle, calculer le milieu, comparer et déplacer une borne, contre deux pas à la recherche linéaire. Mais la dichotomie fait cinq examens là où l'autre en fait quarante-sept, et c'est le nombre d'examens qui décide : neuf fois moins d'examens, un peu plus du double de travail pour chacun, et au bout du compte trois fois moins de pas. Cet écart n'est pas constant : il grandit avec la taille des données, parce que les deux comptes ne grandissent pas de la même façon. Doubler le tableau ajoute, au pire, quarante-huit examens à la recherche linéaire, et un seul à la dichotomie.
Sur le même relevé de 48 mesures, la recherche linéaire compte 98 pas et la dichotomie 29. D'où vient l'écart ?
Pourquoi couper en deux change l'ordre de grandeur
La recherche linéaire élimine un candidat par examen. La dichotomie en élimine la moitié. La question intéressante est : combien de fois faut-il diviser par deux pour tomber à un seul candidat ?
| Taille du relevé | Examens en linéaire, au pire | Examens en dichotomie, au pire |
|---|---|---|
| 48 | 48 | 6 |
| 1 000 | 1 000 | 10 |
| 1 000 000 | 1 000 000 | 20 |
| 1 000 000 000 | 1 000 000 000 | 30 |
La colonne de droite se calcule sans rien dérouler. Un examen divise la zone restante au moins par deux, puisque la case examinée en sort aussi ; deux examens la divisent par quatre, trois par huit, et dix par 1 024, puisque 2 puissance 10 vaut 1 024. Dix examens suffisent donc pour mille candidats ; six suffisaient pour quarante-huit, puisque 2 puissance 6 vaut 64, et il en faut vingt pour un million, trente pour un milliard. Comme mille est presque exactement 2 puissance 10, multiplier la taille du relevé par mille, à partir de la deuxième ligne, n'ajoute que dix examens. Passer d'un million à un milliard d'enregistrements coûte ainsi dix examens de plus à la dichotomie, et neuf cent quatre-vingt-dix-neuf millions à la recherche linéaire. C'est ce genre d'écart qui décide si un traitement est faisable ou non, bien avant toute question de machine ou de langage.
milieu ← (bas + haut) / 2 au lieu de DIV. La division ordinaire rend un décimal, et un indice décimal est refusé. La division entière est obligatoire.Écrire
bas ← milieu au lieu de bas ← milieu + 1. La zone peut cesser de rétrécir : quand elle est réduite à deux cases et que la valeur cherchée est à droite, milieu vaut bas et plus rien ne bouge. Sur le relevé du chapitre, une cible sur trois déclenche ce blocage, dont la dernière mesure ; les autres passent, ce qui rend la faute d'autant plus dangereuse. Le milieu vient d'être examiné : il doit être exclu de la zone restante.Écrire
TantQue bas < haut au lieu de bas <= haut. La zone d'une seule case n'est jamais examinée, et l'algorithme rate une valeur cherchée sur trois environ, sans jamais se plaindre.
La version récursive dit la même chose plus brièvement, et elle relie ce chapitre au précédent : le cas de base est la zone vide, le cas récursif est une moitié de la zone courante.
programme principal
Le compteur affiche 31 pas, deux de plus que la version itérative, et la pile monte à six cadres : le programme principal et un par case examinée. La hauteur de pile est ici tout à fait supportable, et pour une raison qui vaut d'être notée : elle suit le nombre de cases examinées, donc le nombre de divisions par deux. Sur ces quarante-huit mesures, aucune recherche n'empile plus de sept appels, et un milliard d'éléments n'en demanderait qu'une trentaine. Une récursivité qui coupe en deux ne fait donc pas déborder la pile, là où une récursivité qui retire une unité la fait déborder dès le millier d'éléments en Python.
Écrire bas ← milieu au lieu de bas ← milieu + 1 dans la boucle de dichotomie : que se passe-t-il ?
Le prix : un tableau déjà trié
Tout ce qui précède repose sur une hypothèse qui n'a jamais été discutée : le tableau est trié. Sans elle, la comparaison t[milieu] < cible ne dit rien sur le côté où continuer, et l'algorithme élimine une moitié au hasard.
programme principal
bas. La case examinée, la douzième, porte 91, plus petit que 99 : l'algorithme en conclut que 99 se trouve à droite, et bas passe à 13. Les cases 1 à 12 viennent d'être écartées pour de bon, sans avoir été lues. Noter ensuite les valeurs successives de milieu jusqu'à la fin, 12, 18, 21, 23 et 24, puis la réponse affichée : aucune case examinée ne porte 99.
Le tableau contient bien la valeur 99, en cinquième position. L'algorithme répond Position : 0, c'est-à-dire « absente », en 32 pas. Et il ne signale rien : il a fait son travail, tiré ses conclusions de comparaisons qui ne voulaient rien dire, et rendu une réponse fausse avec l'assurance d'une réponse juste. Sur le même tableau, il trouverait pourtant 91 du premier coup, parce que cette valeur occupe par hasard la case du milieu : une recherche qui réussit ne prouve donc rien.
C'est le défaut le plus dangereux de ce chapitre, parce qu'il ne se manifeste par aucun arrêt. Un algorithme qui exige une propriété des données doit l'énoncer, et le programme qui l'appelle doit la garantir. Vérifier que le tableau est trié coûterait un parcours complet, soit précisément ce que la dichotomie cherchait à éviter : la garantie doit donc venir d'ailleurs, d'un tri effectué une fois pour toutes en amont.
Cette remarque déplace le problème plutôt qu'elle ne le résout. Si obtenir un tableau trié coûte plus cher que la recherche linéaire qu'on voulait éviter, l'affaire n'est pas rentable. Elle le devient dès que le même tableau est interrogé plusieurs fois : le tri se paie une fois, les recherches se paient par cinq examens. C'est exactement le raisonnement qui justifie un index dans une base de données. Et c'est la raison pour laquelle le chapitre suivant est consacré aux tris.
Que produit une recherche dichotomique sur un tableau non trié qui contient pourtant la valeur cherchée ?
Le schéma général
La dichotomie est un cas particulier d'une manière de faire qui porte un nom.
Diviser pour régner consiste à résoudre un problème en trois temps : diviser les données en portions plus petites, régner en résolvant chaque portion par le même procédé, et combiner les résultats partiels en un résultat global. La récursivité fournit le second temps sans effort, puisque « le même procédé » est la fonction elle-même.
La recherche dichotomique est un cas dégénéré du schéma : elle divise en deux, mais n'en traite qu'une seule moitié, et n'a donc rien à combiner. Elle divise même sans rien découper : elle travaille sur des indices bas et haut, ce qui est le procédé le plus économique, puisqu'il ne recopie aucune donnée. D'autres algorithmes du même genre ont besoin de vraies portions, manipulables comme des tableaux autonomes : le tri fusion, au chapitre suivant, coupe le tableau en deux moitiés qu'il traite séparément avant de les réunir. Il leur faut d'abord un outil qui découpe.
Tranche : extraire une portion de tableau
programme principal
Trois détails méritent une lecture attentive, car aucun n'est évident avec des indices qui commencent à 1.
La taille vaut b - a + 1, et non b - a. Les deux bornes sont comprises : la tranche de 3 à 7 contient les cases 3, 4, 5, 6 et 7, donc cinq cases. Le + 1 est la trace du fait que la borne de départ compte elle aussi. Oublier ce + 1 réserve une case de moins qu'il n'en faut : la dernière copie vise alors une case qui n'existe pas, et l'algorithme s'arrête sur un indice qui sort du tableau, comme le ferait un langage réel. La tranche d'une seule case demanderait même un tableau de zéro case, refusé dès la réservation.
Le décalage d'indice s'écrit k - a + 1. La case lue est t[k], la case écrite est r[k - a + 1]. Quand k vaut a, l'expression donne 1 : le premier élément de la tranche va bien dans la première case du résultat. Quand k vaut b, elle donne b - a + 1, la dernière case. Écrire r[k - a] donnerait 0 au premier tour, et l'indice 0 provoque un arrêt immédiat, avec le message qui rappelle que le premier élément porte l'indice 1.
Une tranche est une copie, pas une vue. Tranche(t, 1, 3) fabrique un tableau neuf, rempli case par case. Modifier ce résultat ne modifie pas t, et c'est ce qui rend la fonction sûre à employer. En contrepartie, la copie a un coût proportionnel à la taille de la tranche, ce qui la déconseille dans une boucle interne.
b est plus petit que a, la taille demandée est nulle ou négative, et la réservation tableau[b - a + 1] s'arrête sur un message qui rappelle qu'un tableau compte au moins une case. Un algorithme qui coupe en deux doit donc garantir que chaque moitié contient au moins un élément, ce qui se fait en traitant le cas d'un seul élément comme cas de base, avant tout découpage.
Pourquoi la taille d'une tranche vaut-elle b - a + 1 ?
Les trois temps, et un contre-exemple
L'exemple suivant utilise les trois temps, avec Tranche pour le premier.
programme principal
Le cas de base est un tableau d'une seule case, dont le maximum est son unique élément. Le cas récursif coupe en deux, demande le maximum de chaque moitié, et combine en gardant le plus grand des deux. Le déroulement prend 179 pas et empile cinq cadres, pour huit mesures que le parcours par boucle du chapitre sur les tableaux traite en une vingtaine de pas.
Cette figure est donc un contre-exemple utile : ici, diviser pour régner coûte plus cher que la boucle, parce que chaque examen est précédé d'une copie, et que le nombre total d'examens ne diminue pas. Chercher un maximum oblige à regarder toutes les cases, quelle que soit la méthode. La dichotomie ne gagnait pas parce qu'elle coupait en deux, mais parce qu'elle jetait une moitié. C'est la distinction à retenir.
Diviser ne suffit pas. Diviser pour régner ne rapporte que dans deux situations : quand une des portions peut être abandonnée sans être examinée, comme dans la dichotomie, ou quand la combinaison des résultats partiels est nettement moins coûteuse que le traitement direct, comme dans le tri fusion. Découper sans l'un ni l'autre ajoute des copies et des appels à un travail qui n'a pas diminué.
Maximum par découpage en deux moitiés coûte 179 pas là où une boucle en coûterait bien moins. Pourquoi ?
Mettre en pratique
Le coût d'une recherche se prévoit sans rien dérouler : il suffit de compter ce que chaque examen élimine, et ce que chaque appel transmet.
Des examens, des pas et des appels
- 1.
Un référentiel de 5 000 pièces est rangé par numéro croissant. Combien de cases une recherche dichotomique y examine-t-elle au pire ?
- 2.
Et sur un référentiel de 4 096 pièces exactement ?
- 3.
Avec le décompte du chapitre, deux pas par tour de boucle, plus le
Retourner, la ligne qui range le relevé et la ligne d'appel franchie deux fois, combien de pas la recherche linéaire compte-t-elle pour une valeur rangée en douzième position ? - 4.
Combien de cases compte la tranche
Tranche(t, 17, 40)? - 5.
Combien d'appels de
PuissanceRapidele calcul dexà la puissance 100 déclenche-t-il, en comptant celui que fait le programme principal ? - 6.
Un magasin reçoit un référentiel de 1 000 pièces en vrac. Le trier coûte environ 10 000 comparaisons ; une recherche en coûte ensuite 10 au pire par dichotomie, contre 1 000 par recherche linéaire dans le référentiel non trié. À partir de combien de recherches le tri est-il rentabilisé ?
Écrire un algorithme qui devine un nombre compris entre 1 et 100 par dichotomie, en affichant chaque proposition et le nombre total de propositions nécessaires.
programme principal
aucune variable
C'est la dichotomie du chapitre, appliquée à un intervalle de nombres au lieu d'un tableau : la proposition est le milieu lui-même, et un compteur tours avance à chaque essai. Les propositions sont 50, 75, 62, 68, 71 et 73 : six essais pour cent possibilités, en 47 pas. Aucun nombre entre 1 et 100 ne demande plus de sept propositions, puisque sept divisions par deux suffisent à réduire cent candidats à un seul. Comme la recherche du chapitre, la fonction rend 0 pour un nombre hors de l'intervalle, ce qui n'arrive pas ici.
La même devinette s'écrit aussi en récursif, sur le modèle de DichotomieR :
programme principal
aucune variable
Le compteur ne peut plus être une variable de la fonction, puisque chaque appel a son propre cadre : il devient un paramètre, tours, que chaque appel transmet au suivant augmenté de 1. C'est la façon récursive de compter sans variable partagée. Les propositions sont les mêmes, en 43 pas, et la pile monte à sept cadres, un par proposition plus le programme principal.
Calculer la racine carrée entière d'un nombre, c'est-à-dire le plus grand entier dont le carré ne dépasse pas ce nombre, sans fonction racine carrée, avec seulement des multiplications pour tester un candidat.
programme principal
aucune variable
L'intérêt de cet exercice est qu'il n'y a aucun tableau : la dichotomie porte sur un intervalle de nombres, de 0 à n. Rien n'oblige à ranger quoi que ce soit, parce que la propriété qui remplace le tri est déjà vraie : si un carré dépasse n, tous les carrés plus grands le dépassent aussi. C'est cette propriété de croissance, et non l'existence d'un tableau trié, qui est la vraie condition de la dichotomie.
La variable reponse retient le dernier candidat acceptable rencontré, car la valeur cherchée n'est pas testée pour l'égalité : la boucle cherche une frontière, pas une case. Les résultats affichés sont 45 pour 2025 et 31 pour 1000, en 119 pas pour les deux calculs réunis.
Reprendre la puissance du chapitre précédent en coupant l'exposant en deux : x à la puissance n est le carré de x à la puissance n DIV 2, multiplié encore une fois par x quand n est impair.
programme principal
aucune variable
L'exposant ne perd plus une unité à chaque appel, il est divisé par deux : 10, 5, 2, 1, puis 0. Le résultat est 1 024, obtenu en 24 pas et six cadres, contre 34 pas et douze cadres au chapitre précédent. Pour un exposant de mille, la version par unité empilerait plus de mille appels, celle-ci onze.
Le gain tient à une seule ligne. Les deux moitiés du calcul sont identiques, si bien que l'appel récursif n'est fait qu'une fois et que son résultat, rangé dans moitie, sert deux fois : la seconde moitié est abandonnée sans être calculée, ce qui est le premier cas où diviser rapporte. Écrire PuissanceRapide(x, n DIV 2) * PuissanceRapide(x, n DIV 2) referait deux fois le même travail à chaque étage et perdrait tout le gain : le déroulement passerait à 109 pas, plus que les 34 de la version par unité.
Chapitre révisé en octobre 2026 · Une erreur dans ce chapitre ?
Dans la bibliothèque d'exercices
Combien de fois couper en deux, les trois fautes de la dichotomie, et l'écrire soi-même.
- Combien de fois faut-il couper en deux2 · Appliquer
- Trois fautes qui ne se voient pas à la lecture3 · Combiner
- Chercher par moitiés5 · Concevoir