Aller au contenu

Correction

On prouve la correction de l'algorithme de dichotomie en explicitant un invariant de boucle.

Note

La notion d'invariant de boucle est travaillée plus systématiquement dans un autre cours.

programme dichotomie
def milieu(i, j):
    """
    i -- entier
    j -- entier

    renvoie le milieu entier de [i,j]
    """
    return (i+j)//2


def dichotomie(valeurCherchee, tab):
    """
    tab -- liste d'entiers, triée en ordre croissant
    valeurCherchee -- entier

    renvoie True si valeurCherchee est dans tab, False sinon.
    La recherche est faite suivant le principe de dichotomie.
    """
    gauche, droite = 0, len(tab)-1
    centre = milieu(gauche, droite)

    # 
    while gauche <= droite:

        if tab[centre] == valeurCherchee: return True
        if tab[centre] < valeurCherchee:
            gauche, droite = centre+1, droite
        else:
            gauche, droite = gauche, centre-1
        centre = milieu(gauche, droite)

    return False

Question 1

On se place d'abord dans le cas où la liste donnée en entrée contient la valeurCherchee donnée en paramètre.

L'invariant que l'on va chercher à prouver:

«Tout indice tel que tab[indice] == valeurCherchee vérifie gauche ≤ indice ≤ droite.»

Vérifier que cette propriété est satisfaite avant la boucle.

Justification

Avant d'entrer dans la boucle (ligne marquée #), on a gauche = 0 et droite = len(tab)-1. Comme on est dans le cas où valeurCherchee est présent et que tout indice vérifie 0 ≤ indice ≤ len(tab)-1, la propriété est satisfaite.

Question 2

On se place toujours dans le cas où la liste donnée en entrée contient la valeurCherchee donnée en paramètre.

On suppose être arrivé à une étape pour laquelle on a en début de boucle (ligne 23):

«Tout indice tel que tab[indice] == valeurCherchee vérifie gauche ≤ indice ≤ droite.»

programme dichotomie
def milieu(i, j):
    """
    i -- entier
    j -- entier

    renvoie le milieu entier de [i,j]
    """
    return (i+j)//2


def dichotomie(valeurCherchee, tab):
    """
    tab -- liste d'entiers, triée en ordre croissant
    valeurCherchee -- entier

    renvoie True si valeurCherchee est dans tab, False sinon.
    La recherche est faite suivant le principe de dichotomie.
    """
    gauche, droite = 0, len(tab)-1
    centre = milieu(gauche, droite)

    while gauche <= droite:

        if tab[centre] == valeurCherchee: return True
        if tab[centre] < valeurCherchee:
            gauche, droite = centre+1, droite
        else:
            gauche, droite = gauche, centre-1
        centre = milieu(gauche, droite)

    return False

On note gauchee, droitee, centree les valeurs des variables gauche, droite, centre en début de passage (ligne 23) (On a bien sûr centree = (gauchee + droitee)//2).

On notera gauchef, droitef, centref les valeurs des variables gauche, droite, centre en fin de passage (ligne 30).

Justifier que la propriété
«Tout indice tel que tab[indice] == valeurCherchee vérifie gauche ≤ indice ≤ droite.»
est encore vérifiée en fin de passage dans la boucle

Argumentation

Le corps de boucle est

 while gauche <= droite:

    if tab[centre] == valeurCherchee: return True
    if tab[centre] < valeurCherchee:
        gauche, droite = centre+1, droite
    else:
        gauche, droite = gauche, centre-1
    centre = milieu(gauche, droite)
  • Si tab[centre] == valeurCherchee, on sort de la boucle et de la fonction (en renvoyant l'indice centre). Dans ce cas, gauche, droite, centre n'ont pas changé de valeur: la propriété est encore vérifiée.

  • Sinon.

    • Soit tab[centre] < valeurCherchee: dans ce cas, la liste étant ordonnée croissante, valeurCherchee se trouve strictement à droite de centre. Donc en posant gauchef = centree+1 et droitef = droitee, la propriété est encore vraie en fin de passage dans la boucle.
    • Soit tab[centre] > valeurCherchee: dans ce cas, la liste étant ordonnée croissante, valeurCherchee se trouve strictement à gauche de centre. Donc en posant gauchef = gauchee et droitef = centree-1, la propriété est encore vraie en fin de passage dans la boucle.

Dans tous les cas, en fin de passage dans la boucle, la propriété est encore vraie.

Question 3

En déduire que soit valeurCherchee est présente dans tab et la fonction renvoie True,
soit valeurCherchee n'est pas élément de tab et la fonction renvoie False.

Argumentation
  • Si valeurCherchee est élément de tab, on ne sortira pas de la boucle par gauche > droite. En effet, nous avons établi que lorsque valeurCherchee est présente dans tab, on a à chaque fin de passage dans la boucle l'indice de valeurCherchee tel que gauche ≤ indice ≤ droite. Si on sort par gauche > droite, la propriété ne peut être satisfaite: contradiction. Si valeurCherchee est présente, on sort donc nécessairement de la boucle par la ligne ̀if tab[centre] == valeurCherchee: return True et la fonction renvoie bien True.
  • Si valeurCherchee n'est pas élément de tab. Alors la condition tab[centre] == valeurCherchee n'est jamais satisfaite, donc on sort de la boucle par gauche > droite et la fonction renvoie False.