Diviser pour régner
Introduction
« Diviser pour régner » (divide and conquer) est une stratégie algorithmique générale qui structure beaucoup d'algorithmes efficaces : on découpe un problème en sous-problèmes plus petits du même type, on les résout récursivement, puis on combine les solutions pour construire celle du problème initial. Cette méthode est l'inverse naturelle de la récursion : ce qui descend en récursion se reconstruit en remontant.
La formule latine — divide et impera — date de l'Antiquité : Jules César appliquait à la conquête des Gaules la même logique que l'algorithmicien applique à un tableau de éléments. Un tournoi de tennis à élimination directe suit le même schéma : à chaque ronde, le nombre de joueurs encore en lice est divisé par deux, jusqu'au vainqueur — un arbre binaire à niveaux.
Le schéma général
Tout algorithme « diviser pour régner » suit trois étapes :
- Diviser — partitionner l'entrée en sous-entrées plus petites.
- Régner — résoudre chaque sous-entrée par appel récursif (ou directement si elle est suffisamment petite : c'est le cas de base).
- Combiner — assembler les sous-solutions en une solution globale.
Le coût d'un tel algorithme s'exprime par une relation de récurrence : si l'on découpe en sous-problèmes de taille , et que les étapes diviser + combiner coûtent , alors
L'intuition par l'arbre des appels
Avant de poser le théorème maître, regardons ce qui se passe à l'exécution. La récurrence du tri fusion engendre un arbre d'appels : à la racine, on traite un tableau de taille ; au niveau suivant, deux tableaux de taille ; puis quatre de taille ; jusqu'aux feuilles de taille .
- À chaque niveau, le travail total de fusion vaut — deux moitiés à , quatre quarts à , etc., somme stable.
- Le nombre de niveaux est , puisqu'à chaque descente on divise la taille par .
- Coût total : niveaux par niveau, soit .
Cette intuition est plus importante à retenir que la table abstraite ci-dessous : elle donne la bonne lecture mentale, le théorème maître en est la version mécanique.
- — majoration (au plus), déjà connue de Première.
- — minoration (au moins).
- — encadrement strict (entre et ).
Le théorème maître (forme appliquée)
Le théorème maître donne directement la complexité asymptotique de en comparant à :
| Cas | Condition sur | Conclusion |
|---|---|---|
| 1 | ||
| 2 | ||
| 3 |
En pratique, seul le cas 2 est à retenir : il couvre le tri fusion et la recherche dichotomique, et c'est celui que vous rencontrerez au baccalauréat. Les cas 1 et 3 relèvent de la culture générale — utiles si vous croisez des récurrences plus exotiques en études supérieures, hors-programme strict ici.
Tri fusion — l'exemple canonique
Le tri fusion (merge sort) est l'exemple type. On divise le tableau en deux moitiés, on trie chacune récursivement, puis on fusionne les deux moitiés triées en une seule.
def fusionner(gauche, droite):
"""Fusionne deux listes déjà triées en une liste triée."""
resultat = []
i, j = 0, 0
while i < len(gauche) and j < len(droite):
if gauche[i] <= droite[j]:
resultat.append(gauche[i]); i += 1
else:
resultat.append(droite[j]); j += 1
resultat.extend(gauche[i:])
resultat.extend(droite[j:])
return resultat
def tri_fusion(tab):
n = len(tab)
if n <= 1: # cas de base
return tab
milieu = n // 2
g = tri_fusion(tab[:milieu]) # régner sur la gauche
d = tri_fusion(tab[milieu:]) # régner sur la droite
return fusionner(g, d) # combiner
# >>> tri_fusion([5, 2, 8, 1, 4, 7, 3, 6])
# [1, 2, 3, 4, 5, 6, 7, 8]
Analyse de coût : on découpe en sous-problèmes de taille (donc ), la fusion linéaire coûte . On a , donc — c'est le cas 2 du théorème maître :
Le tri fusion garantit ce coût en pire cas, ce qui le rend supérieur au tri rapide pour des données potentiellement adverses.
Pour T(n) = 2 T(n/2) + O(n), la complexité est…
Un mot sur le coût en mémoire
Le tri fusion exposé ci-dessus crée une nouvelle liste à chaque appel — son coût mémoire est en . Des implémentations en place (qui ne créent pas de copies) existent mais sont plus délicates à écrire. C'est l'un des compromis classiques de l'algorithmique : gagner en mémoire au prix de la simplicité du code.
Autre exemple : rotation d'une image bitmap
Une image bitmap est un tableau 2D de pixels. La rotation d'un quart de tour se résout naïvement en allouant une seconde image et en y recopiant les pixels permutés — coût mémoire pour une image .
L'approche diviser pour régner divise l'image en quatre quadrants, fait pivoter chaque quadrant récursivement, puis échange les quadrants deux à deux en place. Le coût mémoire devient constant (en ignorant la pile d'appels), au prix d'un code plus subtil.
Recette pour reconnaître un problème « diviser pour régner »
Trois indices :
- Le problème se décompose naturellement en sous-problèmes du même type.
- La résolution d'un cas de base (taille 0 ou 1) est triviale.
- Recombiner les sous-solutions coûte beaucoup moins que le problème entier.
Le tri fusion garantit un coût en O(n log n) dans le pire cas.
Pour aller plus loin
D'autres algorithmes célèbres reposent sur cette méthode : la transformée de Fourier rapide (FFT, pour traiter le signal), l'algorithme de Karatsuba pour la multiplication d'entiers en au lieu de , ou encore la recherche dichotomique (cas particulier où ).