Aller au contenu

Nombre de comparaisons

Rappelons que le temps nécessaire à l'exécution d'un programme est lié au nombre d'opérations élémentaires effectuées par ce programme. Les opérations élémentaires: comparaisons, affectations, additions, accès à un élément de liste...

Pour simplifier ce travail de décompte des opérations, nous nous limitons au décompte du nombre de comparaisons (c'est en général un bon choix pour avoir une bonne idée de "l'ordre de grandeur" du nombre d'opérations dans un tri).

Exercice

On reprend le code de la recherche de l'indice du maximum rechercheIndiceMax(tab, debut, fin):

Rappel du code de rechercheIndiceMax(tab, debut, fin)
def rechercheIndiceMax(tab, debut, fin):
    """
    tab -- liste d'entiers non vide 
    debut -- indice de tab
    fin -- indice de tab

    renvoie l'indice (debut <= indice <= fin) d'un élément 
    de valeur maximale parmi les éléments de tab[debut..fin].
    >>> rechercheIndiceMax([15, 6, 7, 8, 9], 1,3)
    3
    >>> rechercheIndiceMax([15, 6, 7, 8, 9], 0,3)
    0
    """
    m = tab[debut] # sera le max de tab[debut..fin]
    indice = debut # sera l'indice du max de tab[debut..fin]
    for i in range(debut+1, fin+1):
        if tab[i] > m:
            m = tab[i]
            indice = i
    return indice

Quel est le nombre de comparaisons effectuées lors d'un appel rechercheIndiceMax(tab, debut, fin)?

Remarque

On répondra bien entendu en fonction de debut et fin.

Solution

On effectue une comparaison pour chaque valeur de i comprise entre debut+1 et fin (bornes comprises). On effectue donc fin-debut comparaisons.

Exercice

On reprend le code du tri (en place) par sélection du max (rappelé ci-dessous):

Rappel du code du tri sur place par sélection du maximum
def rechercheIndiceMax(tab, debut, fin):
    """
    tab -- liste d'entiers non vide 
    debut -- indice de tab
    fin -- indice de tab

    renvoie l'indice (debut <= indice <= fin) d'un élément 
    de valeur maximale parmi les éléments de tab[debut..fin].
    >>> rechercheIndiceMax([15, 6, 7, 8, 9], 1,3)
    3
    >>> rechercheIndiceMax([15, 6, 7, 8, 9], 0,3)
    0
    """
    m = tab[debut] # sera le max de tab[debut..fin]
    indice = debut # sera l'indice du max de tab[debut..fin]
    for i in range(debut+1, fin+1):
        if tab[i] > m:
            m = tab[i]
            indice = i
    return indice




def triSelection(tab):
    """
    tab -- liste d'entiers

    Modifie tab en triant ses éléments en ordre croissant.
    """
    for i in range(0, len(tab)-1):
        im = rechercheIndiceMax(tab, 0, len(tab)-1-i)
        tab[len(tab)-1-i], tab[im] = tab[im], tab[len(tab)-1-i]

Quel est le nombre de comparaisons effectuées lors d'un appel à la fonction triSelection?

Remarque

On répondra bien entenu en fonction de la longueur n de la liste tab donnée en argument.

Solution

On note n = len(tab).

Les comparaisons sont faites lors des appels rechercheIndiceMax(tab, 0, len(tab)-1-i). Le nombre de comparaisons effectuées est n-1-i.

i prend successivement les valeurs 0, 1, 2, ..., n-2.

Le nombre total de comparaisons est donc la somme: S = (n-1)+(n-2)+(n-3)+...+1.

Cette somme a pour valeur S = \frac{1}{2} n(n-1).

Somme des n premiers entiers naturels

Pour un entier n, la somme 0 + 1 + 2 + ... + (n-1) est égale à \frac{1}{2}n(n-1).

On retrouve ce résultat avec "l'astuce" ci-dessous qui consiste à ajouter cette somme avec elle-même:

S = 0 + 1 + 2 + ... + (n-3) + (n-2) + (n-1)
+ S = (n-1) + (n-2) + (n-3) + ... + 2 + 1 + 0
2S = (n-1) + (n-1) + (n-1) + ... + (n-1) + (n-1) + (n-1)

On remarque que chaque "colonne" a nécessairement la même somme puisque l'unité ajoutée dans la ligne du haut pour passer au terme suivant est enlevée dans la ligne du bas.

Ainsi: 2S = n\times (n-1), d'où la formule.

Remarque

Rappelons que le tri par insertion présentait un nombre de comparaisons "au mieux" bien meilleur que le cas "au pire" (fonction affine en n versus fonction du second degré en n).

On voit ici que les cas "au pire" et "au mieux" sont les mêmes pour le tri par sélection.