publié le mer. 21 février 2018

Maintenant que nous disposons de tableaux pour stocker de grandes quantités de données, il faut qu'on apprenne à les classer. Il existe de nombreux algorithmes de tri plus ou moins efficaces, qui sont pour la plupart répertoriées dans The Art of Computer Programming, Volume 3, Sorting and Searching. de Knuth, Donald. E. [1998]. Le livre de chevet de tout programmeur.

Problématique

Comment ranger des données afin de faciliter leur accès futur ? C'est par exemple l'ordre alphabétique du dictionnaire, où les mots sont rangés dans un ordre logique qui permet de ne pas devoir parcourir tout l'ouvrage pour retrouver une définition. Ce peut être aussi l'ordre intuitif dans lequel un joueur de cartes va ranger son jeu afin de limiter le temps de recherche pendant le déroulement de la partie. Cette problématique permet d'introduire la notion de tri (avec plusieurs sens distincts : séparer, ordonner, choisir), puis d'étudier différents algorithmes de tri. Le tri permet essentiellement d'accélérer les recherches, grâce à l'algorithme de recherche dichotomique.

Source eduscol

Situation d'accroche

Un joueur de cartes reçoit 9 cartes lors de la donne en début de partie ; il les trie ensuite pour faciliter la lecture de son jeu.

Vous rendre sur cette page sur laquelle vous est proposé un jeu de cartes à trier:

https://github.com/benjaminabel/order-cards-game.

Réalisez les consignes suivantes dans l'ordre.

  1. Consigne n° 1: « triez les cartes » en notant le nombre d'opérations nécessaires au tri, recommencer l'opération pour voir si le nombre de tours d'algorithmes varie, et de quoi peut dépendre ce nombre. Ensuite seulement,
  2. Consigne n° 2: « décrivez par écrit la façon précise dont vous vous y êtes pris pour effectuer le tri ».
  3. En plus, imaginez d'autres méthodes qui pourraient être plus efficaces pour effectuer le tri.

Implémentation en python

Nous allons maintenant voir comment implémenter deux algorithmes de tri pas forcément très efficaces, mais relativement simples en python:

Créer une liste de données aléatoire

Commencer par créer des données de façon aléatoire grâce au module random afin de pouvoir les classer.

In [1]:
# Importer le module random pour créer des nombres au hasard
import random

def genere_liste_aleatoire(N, n):
    """Génére une liste aléatoire de N éléments compris entre 0 et n"""
    # Créer une liste vide pour accueillir les nombres
    data = []
    # ajoute les éléments aléatoires dans la liste
    for i in range(N):
        data.append(random.randrange(n))
    return data

# Création d'une liste de 50 valeurs comprises entre 0 et 100
liste_aléatoire = genere_liste_aleatoire(50, 100)
print(liste_aléatoire)
[19, 89, 59, 17, 67, 26, 36, 23, 32, 65, 50, 94, 19, 53, 78, 39, 90, 54, 21, 20, 38, 24, 90, 74, 92, 7, 68, 22, 97, 66, 26, 88, 80, 49, 41, 78, 96, 31, 67, 94, 20, 6, 52, 66, 13, 60, 34, 65, 57, 85]

Le tri par sélection

Principe

Sur un tableau de n éléments (numérotés de 0 à n), le principe du tri par sélection est le suivant :

  • rechercher le plus petit élément du tableau, et l'échanger avec l'élément d'indice 0 ;
  • rechercher le second plus petit élément du tableau, et l'échanger avec l'élément d'indice 1 ;
  • continuer de cette façon jusqu'à ce que le tableau soit entièrement trié.

Source Wikipedia

Illustration graphique

Selection-Sort-Animation

Illustration en vidéo

In [2]:
from IPython.display import YouTubeVideo
# Select-sort with Gypsy folk dance
# Created at Sapientia University, Tirgu Mures (Marosvásárhely), Romania.
# Directed by Kátai Zoltán and Tóth László.
# In cooperation with "Maros Művészegyüttes", Tirgu Mures (Marosvásárhely), Romania.
YouTubeVideo('Ns4TPTC8whw')
Out[2]:

Implémentation en python

Voici un exemple de code implémentant cet algorithme de tri présentant l'état de la liste à chaque tour avancée dans le tableau. Vous pouvez voir que le tableau est bien classé en plaçant systématiquement l'élément minimum du tableau restant à trier à la fin des éléments triés.

In [3]:
# Création d'une liste de 10 valeurs comprises entre 0 et 100 à trier
data = genere_liste_aleatoire(10, 100)
print("Liste initiale: ", data)

# Calculer la taille du tableau
N = len(data)
# Parcourir le tableau entier
for i in range(N):
    print('-' * 80)
    print("i= ", i)
    # Stocker la valeur initiale de la case d'indice i, et son indice
    minimum = data[i]
    i_min = i
    #  Parcourir le reste du tableau pour rechercher l'élément le plus petit restant
    for j in range(i, N):
        if data[j] < minimum:
            # Stocker la valeur du minimum et son indice
            minimum = data[j]
            i_min = j
    # Intervertir la valeur initiale de la case d'indice i et le minimum trouvé
    tmp = data[i]
    data[i] = minimum
    data[i_min] = tmp
    # Affiche les états intermédiaires de la liste
    print("Etat de la liste:", data)
    print("Éléments triés: ", data[:i+1], "Reste à trier: ", data[i+1:N])

print("Liste triée: ", data)
Liste initiale:  [86, 85, 98, 73, 62, 1, 95, 54, 95, 70]
--------------------------------------------------------------------------------
i=  0
Etat de la liste: [1, 85, 98, 73, 62, 86, 95, 54, 95, 70]
Éléments triés:  [1] Reste à trier:  [85, 98, 73, 62, 86, 95, 54, 95, 70]
--------------------------------------------------------------------------------
i=  1
Etat de la liste: [1, 54, 98, 73, 62, 86, 95, 85, 95, 70]
Éléments triés:  [1, 54] Reste à trier:  [98, 73, 62, 86, 95, 85, 95, 70]
--------------------------------------------------------------------------------
i=  2
Etat de la liste: [1, 54, 62, 73, 98, 86, 95, 85, 95, 70]
Éléments triés:  [1, 54, 62] Reste à trier:  [73, 98, 86, 95, 85, 95, 70]
--------------------------------------------------------------------------------
i=  3
Etat de la liste: [1, 54, 62, 70, 98, 86, 95, 85, 95, 73]
Éléments triés:  [1, 54, 62, 70] Reste à trier:  [98, 86, 95, 85, 95, 73]
--------------------------------------------------------------------------------
i=  4
Etat de la liste: [1, 54, 62, 70, 73, 86, 95, 85, 95, 98]
Éléments triés:  [1, 54, 62, 70, 73] Reste à trier:  [86, 95, 85, 95, 98]
--------------------------------------------------------------------------------
i=  5
Etat de la liste: [1, 54, 62, 70, 73, 85, 95, 86, 95, 98]
Éléments triés:  [1, 54, 62, 70, 73, 85] Reste à trier:  [95, 86, 95, 98]
--------------------------------------------------------------------------------
i=  6
Etat de la liste: [1, 54, 62, 70, 73, 85, 86, 95, 95, 98]
Éléments triés:  [1, 54, 62, 70, 73, 85, 86] Reste à trier:  [95, 95, 98]
--------------------------------------------------------------------------------
i=  7
Etat de la liste: [1, 54, 62, 70, 73, 85, 86, 95, 95, 98]
Éléments triés:  [1, 54, 62, 70, 73, 85, 86, 95] Reste à trier:  [95, 98]
--------------------------------------------------------------------------------
i=  8
Etat de la liste: [1, 54, 62, 70, 73, 85, 86, 95, 95, 98]
Éléments triés:  [1, 54, 62, 70, 73, 85, 86, 95, 95] Reste à trier:  [98]
--------------------------------------------------------------------------------
i=  9
Etat de la liste: [1, 54, 62, 70, 73, 85, 86, 95, 95, 98]
Éléments triés:  [1, 54, 62, 70, 73, 85, 86, 95, 95, 98] Reste à trier:  []
Liste triée:  [1, 54, 62, 70, 73, 85, 86, 95, 95, 98]

Le tri par insertion

Dans l'algorithme, on parcourt le tableau à trier du début à la fin. Au moment où on considère le i-ème élément, les éléments qui le précèdent sont déjà triés. Pour faire l'analogie avec l'exemple du jeu de cartes, lorsqu'on est à la i-ème étape du parcours, le i-ème élément est la carte saisie, les éléments précédents sont la main triée et les éléments suivants correspondent aux cartes encore mélangées sur la table.

L'objectif d'une étape est d'insérer le i-ème élément à sa place parmi ceux qui précèdent. Il faut pour cela trouver où l'élément doit être inséré en le comparant aux autres, puis décaler les éléments afin de pouvoir effectuer l'insertion. En pratique, ces deux actions sont fréquemment effectuées en une passe, qui consiste à faire « remonter » l'élément au fur et à mesure jusqu'à rencontrer un élément plus petit.

Source Wikipedia

Illustration graphique

Insertion-sort-example-300px.gif
"Insertion-sort-example-300px" by Swfung8 - Own work. Licensed under CC BY-SA 3.0 via Wikimedia Commons.

Illustration en vidéo

In [4]:
from IPython.display import YouTubeVideo
# Insert-sort with Romanian folk dance 
# Created at Sapientia University, Tirgu Mures (Marosvásárhely), Romania.
# Directed by Kátai Zoltán and Tóth László.
# In cooperation with "Maros Művészegyüttes", Tirgu Mures (Marosvásárhely), Romania.
YouTubeVideo('ROalU379l3U')
Out[4]:

Implémentation en python

Voici un exemple d'implémentation ou le tableau est parcouru de la gauche vers la droite, observer bien ou est placée la valeur à insérer à chaque tour de la boucle.

In [5]:
# Création d'une liste de 10 valeurs comprises entre 0 et 100 à trier
data = genere_liste_aleatoire(10, 100)
print("Liste initiale: ", data)

# Parcourir l'ensemble de la liste à partir de la deuxième case
for i in range(1, len(data)):
    print('-' * 80)
    print("i= ", i)
    # Stocker la valeur à "insérer"
    val = data[i]
    print("Valeur à insérer:", val)
    # Parcourir le tableau déjà trié de dimension i-1 vers la gauche
    # jusqu'à rencontrer une valeur inférieure à notre valeur à insérer
    j = i - 1
    while data[j] > val and j >=0:
        # Intervertir  les valeurs aux indices j et j+1
        data[j+1] = data[j]
        data[j] = val
        # Diminuer j de 1 pour la prochaine comparaison
        j = j - 1
        print("On remonte la valeur <-", data)
    print("Etat intérmédiaire de la liste: ", data)
# Afficher le résultat
print('\nListe triée:')
print(data)
Liste initiale:  [72, 49, 57, 13, 38, 1, 21, 97, 22, 66]
--------------------------------------------------------------------------------
i=  1
Valeur à insérer: 49
On remonte la valeur <- [49, 72, 57, 13, 38, 1, 21, 97, 22, 66]
Etat intérmédiaire de la liste:  [49, 72, 57, 13, 38, 1, 21, 97, 22, 66]
--------------------------------------------------------------------------------
i=  2
Valeur à insérer: 57
On remonte la valeur <- [49, 57, 72, 13, 38, 1, 21, 97, 22, 66]
Etat intérmédiaire de la liste:  [49, 57, 72, 13, 38, 1, 21, 97, 22, 66]
--------------------------------------------------------------------------------
i=  3
Valeur à insérer: 13
On remonte la valeur <- [49, 57, 13, 72, 38, 1, 21, 97, 22, 66]
On remonte la valeur <- [49, 13, 57, 72, 38, 1, 21, 97, 22, 66]
On remonte la valeur <- [13, 49, 57, 72, 38, 1, 21, 97, 22, 66]
Etat intérmédiaire de la liste:  [13, 49, 57, 72, 38, 1, 21, 97, 22, 66]
--------------------------------------------------------------------------------
i=  4
Valeur à insérer: 38
On remonte la valeur <- [13, 49, 57, 38, 72, 1, 21, 97, 22, 66]
On remonte la valeur <- [13, 49, 38, 57, 72, 1, 21, 97, 22, 66]
On remonte la valeur <- [13, 38, 49, 57, 72, 1, 21, 97, 22, 66]
Etat intérmédiaire de la liste:  [13, 38, 49, 57, 72, 1, 21, 97, 22, 66]
--------------------------------------------------------------------------------
i=  5
Valeur à insérer: 1
On remonte la valeur <- [13, 38, 49, 57, 1, 72, 21, 97, 22, 66]
On remonte la valeur <- [13, 38, 49, 1, 57, 72, 21, 97, 22, 66]
On remonte la valeur <- [13, 38, 1, 49, 57, 72, 21, 97, 22, 66]
On remonte la valeur <- [13, 1, 38, 49, 57, 72, 21, 97, 22, 66]
On remonte la valeur <- [1, 13, 38, 49, 57, 72, 21, 97, 22, 66]
Etat intérmédiaire de la liste:  [1, 13, 38, 49, 57, 72, 21, 97, 22, 66]
--------------------------------------------------------------------------------
i=  6
Valeur à insérer: 21
On remonte la valeur <- [1, 13, 38, 49, 57, 21, 72, 97, 22, 66]
On remonte la valeur <- [1, 13, 38, 49, 21, 57, 72, 97, 22, 66]
On remonte la valeur <- [1, 13, 38, 21, 49, 57, 72, 97, 22, 66]
On remonte la valeur <- [1, 13, 21, 38, 49, 57, 72, 97, 22, 66]
Etat intérmédiaire de la liste:  [1, 13, 21, 38, 49, 57, 72, 97, 22, 66]
--------------------------------------------------------------------------------
i=  7
Valeur à insérer: 97
Etat intérmédiaire de la liste:  [1, 13, 21, 38, 49, 57, 72, 97, 22, 66]
--------------------------------------------------------------------------------
i=  8
Valeur à insérer: 22
On remonte la valeur <- [1, 13, 21, 38, 49, 57, 72, 22, 97, 66]
On remonte la valeur <- [1, 13, 21, 38, 49, 57, 22, 72, 97, 66]
On remonte la valeur <- [1, 13, 21, 38, 49, 22, 57, 72, 97, 66]
On remonte la valeur <- [1, 13, 21, 38, 22, 49, 57, 72, 97, 66]
On remonte la valeur <- [1, 13, 21, 22, 38, 49, 57, 72, 97, 66]
Etat intérmédiaire de la liste:  [1, 13, 21, 22, 38, 49, 57, 72, 97, 66]
--------------------------------------------------------------------------------
i=  9
Valeur à insérer: 66
On remonte la valeur <- [1, 13, 21, 22, 38, 49, 57, 72, 66, 97]
On remonte la valeur <- [1, 13, 21, 22, 38, 49, 57, 66, 72, 97]
Etat intérmédiaire de la liste:  [1, 13, 21, 22, 38, 49, 57, 66, 72, 97]

Liste triée:
[1, 13, 21, 22, 38, 49, 57, 66, 72, 97]

Autres algorithmes

Ces deux algorithmes ne sont que des exemples d'algorithmes de tri, et il en existe bien d'autres plus efficace comme le fameux quicksort, ou le timsort utilisé comme algorithme par défaut en Python.

La littérature ne manque pas sur ce sujet car il s'agit d'une introduction de choix à de nombreux concepts clés de l'algorithmique:

  • la complexité: l'étude du temps et de la mémoire nécessité par l'algorithme.
  • les cas extremes ou edge cases: que se passe-t-il dans le cas ou la liste est déjà triée, ou au contraire si elle est en ordre inversé.
  • la correction de l'algorithme: comment prouver par une méthode de récurrence que l'algorithme donne le bon résultat en toute occasion par une approche de démonstration mathématique.

Vous pouvez consulter cet article du site interstices.info pour en savoir plus.