Aller au contenu

Chapitre 8 - Partie 2 : Diviser pour régner


La méthode diviser pour régner (divide and conquer) résout un problème en trois phases :

  1. Diviser : découper le problème en sous-problèmes plus petits de même nature ;
  2. Régner (résoudre) : résoudre chaque sous-problème récursivement, jusqu'aux cas de base ;
  3. Combiner : assembler les solutions des sous-problèmes pour obtenir la solution du problème initial.

La recherche dichotomique (vue en première) est un cas particulier : on divise le tableau en deux, on ne résout qu'un seul sous-problème (la moitié contenant la valeur cherchée), et il n'y a pas d'étape de combinaison.


Le tri fusion (merge sort) trie un tableau en appliquant « diviser pour régner » :

  1. Diviser : couper le tableau en deux moitiés ;
  2. Régner : trier récursivement chaque moitié ;
  3. Combiner : fusionner les deux moitiés triées en un tableau trié.
Tableau initial : [7, 5, 3, 0, 1, 6, 4, 2]
Diviser : [7, 5, 3, 0] [1, 6, 4, 2]
[7, 5] [3, 0] [1, 6] [4, 2]
[7] [5] [3] [0] [1] [6] [4] [2]
Combiner : [5, 7] [0, 3] [1, 6] [2, 4]
[0, 3, 5, 7] [1, 2, 4, 6]
[0, 1, 2, 3, 4, 5, 6, 7]
def tri_fusion(tab):
if len(tab) <= 1:
return tab
milieu = len(tab) // 2
gauche = tri_fusion(tab[:milieu])
droite = tri_fusion(tab[milieu:])
return fusionner(gauche, droite)
def fusionner(g, d):
resultat = []
i, j = 0, 0
while i < len(g) and j < len(d):
if g[i] <= d[j]:
resultat.append(g[i])
i += 1
else:
resultat.append(d[j])
j += 1
resultat.extend(g[i:])
resultat.extend(d[j:])
return resultat
>>> tri_fusion([7, 5, 3, 0, 1, 6, 4, 2])
[0, 1, 2, 3, 4, 5, 6, 7]

Le tri fusion a une complexité en O(n log₂ n) dans tous les cas (meilleur, moyen et pire).

TriMeilleur casPire cas
Tri sélection (1re)O(n²)O(n²)
Tri insertion (1re)O(n)O(n²)
Tri fusionO(n log₂ n)O(n log₂ n)

Pourquoi O(n log₂ n) ? On divise le tableau en deux à chaque niveau (log₂ n niveaux de récursion) et à chaque niveau, la fusion parcourt tous les n éléments.

Le tri fusion est stable : deux éléments de même valeur conservent leur ordre relatif initial. C'est grâce au <= dans la comparaison de la fusion.


On veut faire pivoter une image carrée de 90° dans le sens horaire. L'approche « diviser pour régner » permet de le faire avec un coût en mémoire constant (sans créer de copie de l'image).

  1. Diviser : découper l'image en 4 blocs (A, B, C, D) ;
  2. Régner : appliquer récursivement la rotation à chaque bloc ;
  3. Combiner : permuter les 4 blocs pour réaliser la rotation.
Avant rotation : Après rotation :
┌─────┬─────┐ ┌─────┬─────┐
│ A │ B │ │ D │ A │
├─────┼─────┤ → ├─────┼─────┤
│ D │ C │ │ C │ B │
└─────┴─────┘ └─────┴─────┘
def rotation_matrice(matrice):
n = len(matrice)
if n <= 1:
return matrice
k = n // 2
# Diviser en 4 blocs
A = [ligne[:k] for ligne in matrice[:k]]
B = [ligne[k:] for ligne in matrice[:k]]
C = [ligne[k:] for ligne in matrice[k:]]
D = [ligne[:k] for ligne in matrice[k:]]
# Régner : rotation récursive de chaque bloc
A = rotation_matrice(A)
B = rotation_matrice(B)
C = rotation_matrice(C)
D = rotation_matrice(D)
# Combiner : permutation des blocs
resultat = []
for i in range(k):
resultat.append(D[i] + A[i])
for i in range(k):
resultat.append(C[i] + B[i])
return resultat
  • Temps : O(n²) pour une image n × n (chaque pixel est traité) ;
  • Mémoire : O(1) dans la version en place (on permute les pixels directement dans l'image sans copie complète).

Calculer xnx^n en réduisant le nombre de multiplications :

xn={1si n=0(xn/2)2si n est pairx×xn−1si n est impairx^n = \begin{cases} 1 & \text{si } n = 0 \\ (x^{n/2})^2 & \text{si } n \text{ est pair} \\ x \times x^{n-1} & \text{si } n \text{ est impair} \end{cases}

def puissance_rapide(x, n):
if n == 0:
return 1
if n % 2 == 0:
demi = puissance_rapide(x, n // 2)
return demi * demi
return x * puissance_rapide(x, n - 1)

Complexité : O(log₂ n) au lieu de O(n) pour la méthode naïve.

4.2 Recherche du maximum et du minimum simultanés

Section intitulée « 4.2 Recherche du maximum et du minimum simultanés »

Trouver le maximum et le minimum d'un tableau avec moins de comparaisons :

def min_max(tab, debut=0, fin=None):
if fin is None:
fin = len(tab) - 1
if debut == fin:
return tab[debut], tab[debut]
if fin == debut + 1:
if tab[debut] < tab[fin]:
return tab[debut], tab[fin]
return tab[fin], tab[debut]
milieu = (debut + fin) // 2
min1, max1 = min_max(tab, debut, milieu)
min2, max2 = min_max(tab, milieu + 1, fin)
return min(min1, min2), max(max1, max2)

ConditionDescription
DécomposableLe problème peut être découpé en sous-problèmes de même nature
IndépendanceLes sous-problèmes sont indépendants (sinon → programmation dynamique)
Combinaison efficaceLa fusion des solutions est rapide

ConceptDescription
Diviser pour régnerDiviser → Régner (récursion) → Combiner
Tri fusionDivise en 2, trie récursivement, fusionne. O(n log₂ n)
FusionCombiner deux tableaux triés en un seul. O(n)
Rotation d'imageDécouper en 4 blocs, tourner chacun, permuter
Exponentiation rapideO(log₂ n) multiplications au lieu de O(n)
Complexité typeO(n log n) quand on divise en 2 et combine en O(n)