Dichotomie 9 mars 2007 static int dicho(int[] t, int i) int bas=0, haut=t.length-1; while (bas <= haut) { int centre = (haut+bas)/2; if (i==t[centre]) return centre; //(1) if (i < t[centre]) haut=centre-1; else bas=centre+1; } return -1; } De façon générale, il faut être très attentif sur ce qui se passe lorsque l’intervalle devient très petit. Terminaison : si on note l=haut − bas, l décroit au moins de 1 à chaque passage dans la boucle où i! = t[centre] (en général il est même plus que divisé par 2, mais attention à ce qui peut se passer pour les petites valeurs. . .). Nécessairement, si on n’a jamais i == t[centre], l va donc finir par vérifier l < 0, ce qui est la condition d’arrêt. Invariant de boucle : «si i est dans le tableau, il est à un indice compris entre bas et haut». En effet, cette propriété est vraie avant la boucle. Si elle est vraie au début de la boucle (i est dans [bas, haut]), et si on a par exemple i < t[centre] (strictement !), nécessairement, si i est dans le tableau, il est dans [bas, centre − 1] par monotonie du tableau (le seul cas problématique est celui où t[centre − 1] < i < t[centre]. Dans ce cas, i n’apparait pas dans le tableau, et notre propriété reste vraie). On peut donc maintenant prouver simplement l’algorithme : on voit déjà que s’il retourne, il retourne une valeur correcte. Reste à voir qu’il ne rate pas i. Si i est dans le tableau, on a à tout moment que i apparaît entre bas et haut. On commence par regarder ce qu’il se passe lorsque l == 0 ou l == 1. Une petite étude de cas permet de voir que l’on trouve bien i dans ces cas là. Dans le cas général, on remarque juste que si l’algorithme de retourne pas, il passe 1 forcément par les cas l == 0 ou l == 1 (là aussi, une petite étude de cas est nécessaire : regarder ce qui se passe pour l == 2, l == 3, l == 4 et l > 4), ce qui permet de conclure. On a ainsi : «la boucle retourne via (1) si et seulement si i est dans le tableau, et la valeur retournée est correcte». On en déduit «l’algorithme retourne -1 si et seulement si i n’est pas dans le tableau», et le comportement de l’algorithme est entièrement prouvé. Complexité : notons n la longueur du tableau passé en argument.On a vu que l’on passait nécessairement à l == 0 ou l == 1. Une fois que l’on est à l == 0 ou l == 1, l’algorithme termine en un temps constant (< t0 , indépendant de n). la question est de savoir au bout de combien de temps on arrive à 0 ou 1. Au début, on a l0 = n, puis trivialement li+1 ≤ li /2 pour li ! = 0 ou 1. Notons k = ceil(log(n)) où ceil désigne la partie entière +1. On a alors 2k ≥ n, soit n/2k ≤ 1. Donc au plus au bout k itérations, on aura l ≤ 1 (et l’algorithme finit alors en temps constant). La complexité est donc inférieure à O(log(n)). Resterait à montrer qu’il y a des cas où cette borne est atteinte pour voir que la complexité au pire est exactement O(log(n)) (ce qui est le cas). 2