Aller au contenu principal

Informatique · Terminale · 6 min de lecture

Diviser pour régner : le tri fusion

Méthode « diviser pour régner » : découper le problème en sous-problèmes de même nature plus petits, les résoudre (souvent récursivement) puis combiner leurs solutions ; tri fusion : couper le tableau en deux moitiés, les trier, puis fusionner deux tableaux triés ; coût en n log₂ n dans tous les cas, contre n² pour les tris par sélection et par insertion dans le pire cas.

Chapitre du programme : Numérique et sciences informatiques (spécialité)

Bloc 1 sur 7 · Comprendre

Comprendre : diviser, résoudre, combiner

La méthode « diviser pour régner » résout un problème en trois temps :

  1. Diviser le problème en sous-problèmes de même nature, plus petits ;
  2. Résoudre chaque sous-problème, souvent par un appel récursif ;
  3. Combiner les solutions des sous-problèmes en une solution du problème.

Le tri fusion l'applique au tri d'un tableau : on le coupe en deux moitiés, on trie chaque moitié, puis on fusionne les deux moitiés triées. La fusion parcourt les deux tableaux triés en parallèle et prend à chaque fois le plus petit des deux premiers éléments non encore pris :

def fusion(a, b):
    i = 0
    j = 0
    r = []
    while i < len(a) and j < len(b):
        if a[i] <= b[j]:
            r.append(a[i])
            i = i + 1
        else:
            r.append(b[j])
            j = j + 1
    return r + a[i:] + b[j:]

print(fusion([3, 8, 12], [5, 6, 20, 25]))
[3, 5, 6, 8, 12, 20, 25]

Quand l'un des deux tableaux est épuisé, la boucle s'arrête et on ajoute à la fin ce qui reste de l'autre, déjà trié : a[i:] désigne le tableau des éléments de a d'indices i à la fin (vide si i = len(a)), de même pour b[j:] ; le signe + met des tableaux bout à bout.

Bloc 2 sur 7 · Approfondir

Approfondir : la récursion et le coût n log₂ n

Le tri fusion, écrit à la suite de la fonction fusion : un tableau de longueur 0 ou 1 est déjà trié (cas de base) ; sinon, on coupe au milieu. t[:m] est le tableau des éléments d'indices 0 à m − 1, t[m:] celui des éléments d'indices m à la fin.

def tri_fusion(t):
    if len(t) <= 1:
        return t
    m = len(t) // 2
    return fusion(tri_fusion(t[:m]), tri_fusion(t[m:]))

print(tri_fusion([38, 27, 43, 3, 9, 82, 10]))
[3, 9, 10, 27, 38, 43, 82]

Coût. On coupe en deux jusqu'à des tableaux d'un élément : il y a environ log₂ n niveaux de découpage, et à chaque niveau les fusions traitent en tout n éléments. Le coût est de l'ordre de n log₂ n dans tous les cas, contre n² dans le pire cas pour les tris par sélection et par insertion.

Comptons les comparaisons du tri fusion sur 1 000 nombres mélangés, et comparons au nombre n(n − 1)/2 de comparaisons du tri par sélection. La ligne global compteur (hors programme) permet seulement à la fonction fusion de modifier la variable compteur créée en dehors d'elle ; on ne s'en sert ici que pour compter. random.shuffle mélange le tableau, et random.seed(1) fixe ce mélange pour qu'il soit le même à chaque exécution :

import random

compteur = 0

def fusion(a, b):
    global compteur
    i = 0
    j = 0
    r = []
    while i < len(a) and j < len(b):
        compteur = compteur + 1
        if a[i] <= b[j]:
            r.append(a[i])
            i = i + 1
        else:
            r.append(b[j])
            j = j + 1
    return r + a[i:] + b[j:]

def tri_fusion(t):
    if len(t) <= 1:
        return t
    m = len(t) // 2
    return fusion(tri_fusion(t[:m]), tri_fusion(t[m:]))

random.seed(1)
t = list(range(1000))
random.shuffle(t)
print(tri_fusion(t) == sorted(t), compteur)
print(1000 * 999 // 2)
True 8705
499500

Le tri est correct, avec 8 705 comparaisons (1 000 × log₂ 1 000 ≈ 9 966), contre 499 500.

Bloc 3 sur 7 · Exemple

Exemple guidé : compter les comparaisons d'une fusion

Énoncé. Cette fonction fusionne deux tableaux triés et compte dans c les comparaisons. Quel nombre affiche-t-elle ?

def fusion(a, b):
    i = 0
    j = 0
    c = 0
    r = []
    while i < len(a) and j < len(b):
        c = c + 1
        if a[i] <= b[j]:
            r.append(a[i])
            i = i + 1
        else:
            r.append(b[j])
            j = j + 1
    return c
print(fusion([4, 15, 31], [9, 12, 40, 52]))

Méthode : suivre i, j et c tour par tour.

Tableau : Tour, Comparaison, Élément pris, i, j, c
TourComparaisonÉlément prisijc
14 ⩽ 94101
215 > 99112
315 > 1212123
415 ⩽ 4015224
531 ⩽ 4031325

Après le tour 5, i = 3 = len(a) : le tableau a est épuisé, la boucle s'arrête. Il reste 40 et 52 dans b, mais ils ne sont plus comparés.

5

Bloc 4 sur 7 · Visualiser

Voir : le découpage puis les fusions

Tri fusion de [38, 27, 43, 3, 9, 82, 10] (coupure à m = len(t) // 2) :

Tableau : Étape, Tableaux
ÉtapeTableaux
Diviser[38, 27, 43] et [3, 9, 82, 10]
Diviser[38] ; [27, 43] ; [3, 9] ; [82, 10]
Diviser[38] ; [27] ; [43] ; [3] ; [9] ; [82] ; [10] (cas de base)
Fusionner[38] ; [27, 43] ; [3, 9] ; [10, 82]
Fusionner[27, 38, 43] et [3, 9, 10, 82]
Fusionner[3, 9, 10, 27, 38, 43, 82]

Continuez sur votre lancée

Vous avez découvert l’essentiel et un exemple. Retrouvez la suite du cours et les points à retenir, puis passez à l’entraînement guidé dans votre compte.

  • Les formules et les repères à retenir
  • Les erreurs fréquentes expliquées
  • Un exercice avec des aides et sa correction

L’accès gratuit comprend les cours et exercices de votre niveau choisi.

Sources

  • BO spécial n°8 du 25 juillet 2019 — programme de spécialité numérique et sciences informatiques de terminale générale, annexe (https://eduscol.education.fr/document/30010/download) — rubrique « Algorithmique », méthode « diviser pour régner »
  • T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein, Algorithmique (Introduction to Algorithms), 3e édition française, Dunod — chapitre 2, section 2.3 (méthode diviser pour régner, tri par fusion et son analyse)

Vous pensez avoir trouvé une erreur dans cette leçon ? Signalez-la par e-mail.

Explorer les autres cours

Accès rapide