Vous êtes sur la page 1sur 26

Chapitre 2

La stratégie diviser pour régner

2.1 Comment diviser pour régner ?

L'une des stratégies permettant d'appréhender la résolution des problèmes diciles en


algorithmique consiste à les diviser en plusieurs sous-problèmes de même nature que le
problème de départ. L'avantage est que chacun de ces sous-problèmes implique une quan-
tité moindre de données que le problème de départ. Une fois les sous-problèmes résolus, il
est alors possible de combiner les solutions des sous-problèmes an de déduire une solution
au problème initial. Conformément à cette démarche, la stratégie diviser pour régner
préconise de résoudre des problèmes par des algorithmes qui procèdent de la manière
suivante :
 Décomposer le problème initial en des sous-problèmes qui sont souvent de même
nature que le problème initial mais impliquant une quantité moindre de données.
 Appliquer, de manière récursive, la même stratégie de décomposition aux sous-
problèmes jusqu'à l'obtention de sous-problèmes élémentaires qu'il n'est pas né-
cessaire de décomposer davantage, et pour lesquels une solution algorithmique est
vite trouvée.
 À chaque niveau de la décomposition, combiner les solutions des sous-problèmes
pour obtenir une solution au (sous-)problème parent.

La mise en ÷uvre de la stratégie diviser pour régner fait, très souvent, recours au
mécanisme de la récursivité. Ainsi, la résolution du problème est conée à une routine
récursive. Un premier appel récursif est alors lancé avec, comme objectif, la résolution du
problème initial. Si le problème est jugé élémentaire alors une solution est vite calculée
et renvoyée comme résultat de l'appel. Sinon, le problème courant est décomposé en
plusieurs sous-problèmes, qui vont déclencher autant d'appel récursifs. Le rôle de chacun
de ces appels récursifs est de calculer une solution à l'un des sous-problèmes. L'ensemble
des solutions partielles ainsi obtenues est, par la suite, assemblé pour former une solution
au problème de départ. En appliquant cette stratégie, de manière récursive, sur les sous-
problèmes on arrive, au bout du compte, à récupérer une solution au problème initial.

Exemple 1. On voudrait étudier un tri qui soit plus ecace que les tris de complexité
quadratique tel que le tri à bulles, le tri par insertion ou le tri par sélection. Pour ce faire,
on choisit d'étudier le tri par fusion, qui illustre parfaitement l'application de la stratégie
diviser pour régner . Ainsi, pour trier un tableau à n éléments, on procède en le scindant

1
2 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

en deux moitiés, de taille égale, ou diérant de un si n est impaire, qu'on trie de manière
indépendante. Une fois, les deux moitiés du tableau triées, il est possible de les fusionner
en un seul tableau trié en un temps linéaire, O(n), comme il sera montré dans ce qui suit.
Pour trier les deux moitiés de tableau, on peut appliquer la même approche, de manière
récursive, et ainsi de suite, jusqu'à ce que l'on atteigne des tableaux de taille 1, qui sont
triés d'oce.
En résumé, le tri par fusion peut être réalisé par une procédure récursive qui décompose
le problème en deux sous-problèmes de taille quasiment égales puis combine les solutions
des deux sous-problèmes en un temps linéaire, O(n). De ceci, on déduit que la complexité
du tri par fusion peut être décrite par une fonction qui vérie la relation de récurrence
suite :
n
f (n) = 2f (d e) + O(n)
2
Un théorème, qui va être présenté dans la section suivante, permettra alors de dériver
l'ordre de la complexité du tri par fusion, qui est O(n log n).
L'Algorithme 2.1, contient deux procédures qui mettent en ÷uvre le principe du tri par
fusion. La première procédure, TriFusion, qui est récursive, applique la stratégie diviser
pour régner en décomposant le problème en deux puis en combinant les solutions des
deux sous-problèmes en faisant appel à la deuxième procédure Fusion. Cette dernière a
pour tâche de fusionner deux tableaux, qui sont supposés être triés, en temps linéaire. Un
exemple d'une telle fusion est le suivant :
Avant fusion

G= 0 3 4 5 7 9 D= 1 2 5 6 7 8

Après fusion

G= 0 1 2 3 4 5 D= 5 6 7 7 8 9

Pour terminer l'étude du tri par fusion, vérions que la procédure de fusion se déroule
bien en un temps linéaire. Pour ce faire, il sut d'examiner le code de cette procédure
pour constater que les trois boucles se répètent, au plus, (nD  debD) fois et que, dans le
n
pire des cas, cette diérence vaut d 2 e. Ceci justie que la fusion a une complexité linéaire.

Exercice 1. Soit un ensemble de n couples, chacun contenant un entier et une


couleur parmi l'ensemble {rouge, bleu, vert}. On suppose que les couples sont triés
sur la base des entiers qu'ils contiennent. Proposez un algorithme de complexité
linéaire qui les trie sur la base des couleurs (rouge puis bleu puis vert), tout en
gardant les couples de même couleur triés sur la base des entiers. Remarque : le tri
doit être eectué dans le même tableau.

2.2 Le master theorem


On s'intéresse aux algorithmes du type diviser pour régner qui opèrent en découpant un
n
problème de taille n en a sous-problèmes de taille d e, puis qui combinent les solutions
b
c
des sous-problèmes, en O(n ) étapes, pour des constantes a, b > 1, c ≥ 0. La complexité
2.2. LE MASTER THEOREM 3

Procédure TriFusion(var T : tableau d'entier, deb, n : entier)


variables
m : entier
début
si deb < n alors
m := (deb+n) div 2
TriFusion(T,deb,m)
TriFusion(T, m+1,n)
Fusion(T,deb,m,m+1,n)
n
n
Procédure Fusion(var T : tableau d'entier, debG, nG, debD, nD : entier)
variables
A : tableau d'entier
i, j, k : entier
début
i := debG
j := debD
k := debG
tant que i <= nG et j <= nD faire
si T[i] <= T[j] alors
A[k] := T[i]
i := i + 1
sinon
A[k] := T[j]
j := j + 1
n
k := k + 1
n
tant que i <= nG faire
A[k] := T[i]
i:= i + 1
k := k + 1
n
tant que j <= nD faire
A[k] := T[j]
j := j + 1
k := k + 1
n
pour i:=debG à nD faire T[i] := A[i]

n
Algorithme 2.1 : Le tri par fusion.
4 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

de ces algorithmes peut être décrite par une fonction f qui vérie la relation de récurrence
suivante :


1 si n=1
f (n) = (2.1)
af (d nb e) + O(nc ) si n≥2
Dans ce qui suit, on énonce le théorème principal, (plus connu sous l'appellation anglaise
de master theorem ), qui permet de dériver l'ordre de grandeur asymptotique, dans le pire
des cas, des fonctions de complexité qui vérient la relation de récurrence (2.1). Ceci veut
dire que la complexité d'un bon nombre des algorithmes qui opèrent selon la stratégie
diviser pour régner peut être calculée en appliquant le théorème qui suit.

Theorem 1. Si f (n) = af (dn/be) + O(nc ), où a ≥ 1, b > 1 et c ≥ 0 sont des


constantes alors 
 O(nc ) si c > logb a
c
f (n) ∈ O(n logb n) si c = logb a
O(nlogb a ) si c < logb a

Preuve. Pour des raisons de simplicité supposons que n est une puissance de b. Ceci ne
blogb nc
fausse pas les résultats, car n vérie toujours n ≤ b.b , et b est une constante. La
première constatation est que l'indice de la récurrence diminue d'un facteur b, à chaque
étape. Ceci implique que la base de la récurrence, (qui correspond au cas n = 1), sera
atteinte après logb (n) étapes. D'autre part, et d'après (2.1), à chaque étape de la récur-
rence, le terme courant est multiplié par un facteur a. Ceci implique qu'à une étape k
quelconque de la récursion, le terme asymptotique O((n/bk )c ) sera multiplié par ak , ce
qui donne
n c
ak .O(( ))
bk
En sortant les termes constants, on obtient

a
( c )k .O(nc )
b

An d'obtenir f (n), on doit donc sommer les termes ( bac )k .O(nc ), pour k = 0, . . . , logb n,
on obtient alors :

a a a
+ ( c )2 + . . . + ( c )logb n .O(nc )

1+ c
b b b
Il s'agit d'une suite géométrique de raison a/bc et de terme initial O(nc ). D'après, le
Théorème 6 du Chapitre 1, on obtient
c c
 f (n) ∈ O(1)O(n ), si a/b < 1, et donc, si c > logb a.
c c
 f (n) ∈ O(logb n)O(n ) si a/b = 1, et donc, si c = logb a.
c log n c alogb n
 f (n) ∈ O((a/b ) b )O(n ) = O(
nc
)O(nc ) = O(alogb n )O(1) = O(nlogb a ) si
a/bc > 1, et donc, si c < logb a.

Enn, en utilisant le Lemme 4 du Chapitre 1, on obtient les appartenances énoncé par le


théorème.
2.2. LE MASTER THEOREM 5

Application : pour ce qui est de l'ordre de grandeur de la complexité du tri par fusion, il
peut être déduit en appliquant le master theorem. Il sut pour ce-là de trouver les bonnes
valeurs des constantes a, b et c. Selon la démarche du tri par fusion, on a
 a = 2, car à chaque étape, le problème courant donne lieu à deux sous-problèmes,
qui sont résolus par les deux appels récursifs.
 b = 2, car à chaque étape, le tableau est scindé en deux moitiés.
 c = 1, car la fusion de deux tableaux triés se déroulent en temps linéaire, O(n1 ).
La formule donne alors O(n log n).

Exemple 2. La puissance de la stratégie diviser pour régner peut être illustrée par une
méthode de multiplication récursive, qui est plus ecace que la méthode de multiplication
1 2
usuelle . Rappelons que cette dernière demande un nombre d'opérations élémentaires
égal au produit du nombre de chires dans le code décimal, (ou autre), de chacun des
deux nombres à multiplier. Donc, si chacun des deux nombres est codés sur n chires
2
alors la multiplication usuelle demande O(n ) opérations élémentaires. Une addition, par
contre, demande autant d'opérations élémentaires que de chires dans le code du plus
grand des deux entiers, donc O(n).
Supposons que a et b sont deux entiers codés, en binaire, sur n bits chacun. Pour des
raisons de simplicité, on suppose aussi que n est une puissance de 2. La première étape
de la méthode de multiplication récursive de a par b consiste à scinder chacun des codes
binaires de a et de b en deux moitiés, une moitié gauche et une moitié droite :
a= â ǎ b= b̂ b̌
On a, alors

a = â.2n/2 + ǎ et b = b̂.2n/2 + b̌
Il s'ensuit que

ab = (â.2n/2 + ǎ)(b̂.2n/2 + b̌)


= âb̂.2n + (âb̌ + ǎb̂).2n/2 + ǎb̌ (2.2)

Le produit ab sera calculé selon (2.2).


En constatant qu'une multiplication par une puissance de 2 peut se faire en opérant des
décalages du code binaire, le calcul de ab se résume à décaler n + n/2 bits et à calculer 3
additions et 4 produits. Ces derniers sont âb̂, âb̌, ǎb̂ et ǎb̌ et ils impliquent tous des entiers
codés sur n/2 bits. Les décalages et les additions peuvent se faire en temps linéaire, O(n).
Il reste donc les 4 produits mentionnés ci-dessus, qui sont plus complexes, car quadratique.
A ce stade, on constate qu'on a décomposé le problème initial, qui est celui du calcul de
ab, en 4 sous-problèmes de même nature, car se sont toujours des produits à calculer.
Néanmoins, les sous-produits sont plus simples que le produit initial, car â, ǎ, b̂ et b̌ sont
codés sur n/2 bits, chacun. De plus, puisque le problème initial et les 4 sous-problèmes sont
de même nature, le même module de traitement peut être utilisé pour résoudre les sous-
problèmes via 4 appels récursifs. Une fois calculés, les 4 sous-produits seront combinés
en eectuant les décalages et les additions selon (2.2), en temps linéaire, pour obtenir

1. celle qu'on apprend à l'école.


2. Lors d'une multiplication avec la méthode usuelle, une opération élémentaire se résume à la multi-
plication de deux chires.
6 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

le produit ab. La complexité de cette méthode de multiplication récursive peut donc être
exprimée par une fonction f qui vérie la relation de récurrence suivante :

f (n) = 4f (n/2) + O(n)

L'ordre de grandeur asymptotique de la fonction f , peut être déduit à l'aide du Théorème 1.


2
D'après ce théorème, on a f (n) ∈ O(n ). On constate donc que c'est le même ordre de
grandeur que la complexité de la multiplication usuelle. Donc, pour le moment, on n'a
rien gagné en terme de complexité, pourtant la méthode de multiplication récursive est
bien diérente de la méthode de multiplication usuelle.
Cependant, les calculs eectués par la méthode de multiplication récursive peuvent être
optimisés en utilisant l'identité suivante :

âb̌ + ǎb̂ = (â + ǎ)(b̂ + b̌) − âb̂ − ǎb̌ (2.3)

Donc, au lieu de calculer les quatre produits âb̂, âb̌, ǎb̂ et ǎb̌, on calculera le produit
(â + ǎ)(b̂ + b̌), qui est sur n/2 bits, et les deux produits âb̂ et ǎb̌, qui sont aussi sur n/2
bits. Pour résumer, en utilisant l'identité (2.3), on aura besoin de calculer 3 produits sur
n/2 bits, n + n/2 décalages et 6 additions sur n bits, à chaque appel récursif. L'équation
de récurrence que doit vérier la complexité de la méthode de multiplication récursive
modiée est donc la suivante :

f (n) = 3f (n/2) + O(n)

ce qui donne un ordre de grandeur asymptotique de O(n1.59 ). La méthode de multiplication


récursive modiée devient donc meilleure que la méthode de multiplication usuelle, qui,
2
rappelons le, a une complexité de O(n ).

Exercice 2. On se propose de calculer xn , où x est un réel et n un entier positif.


 Proposez une première fonction récursive qui utilise la relation de récurrence
suivante : 
 1 si n=0
xn = xn/2 xn/2 si n est pair et 6= 0
 bn/2c bn/2c
x x x si n est impair
 Calculez la complexité de la fonction proposée.
 Proposez une deuxième fonction récursive qui utilise la relation de récurrence
suivante :

 1 si n=0
n
x = x si n=1
 p n0
(x ) si n = pn0 , où p est le plus petit diviseur premier de n

 Calculez la complexité de la fonction proposée.

Exercice 3. Il s'agit de déterminer le nombre d'inversions dans un tableau T conte-


nant une permutation des entiers de 1 à n. On précise qu'une inversion peut être
déterminée par un couple (i, j) tel que i < j et T [i] > T [j]. Proposez un algorithme
super-linéaire permettant de réaliser cette tâche.
2.3. RÉSOLUTION DES RÉCURRENCES LINÉAIRES 7

Exercice 4. Soit une matrice m×n d'entiers où les éléments de chaque ligne sont
triés dans l'ordre croissant de gauche à droite et les éléments de chaque colonne
sont triés dans l'ordre croissant du haut vers le bas. Proposez un algorithme ecace
qui eectue la cherche d'un élément donné dans une telle matrice puis évaluez sa
complexité.

Exercice 5. (Algorithme de Strassen) On se propose de calculer le produit P de


deux matrices carrées A et B de dimension n × n. Pour ce faire, on fait recours à la
méthode de Strassen qui consiste, tout d'abord, à décomposer chacune des matrices
P, A et B en quatre sous-matrices comme suit :
    
r s a b e f
=
t u c d g h
On calcule, ensuite, les produits matriciels suivants :

p1 = a(f − h)
p2 = (a + b)h
p3 = (c + d)e
p4 = d(g − e)
p5 = (a + d)(e + h)
p6 = (b − d)(g + h)
p7 = (a − c)(e + f )

 Exprimez les sous-matrices r, s, t et u à l'aide de sommes et diérences des


matrices p1 , . . . , p 7 .
 En déduire un algorithme du type diviser pour régner qui eectue le produit
matriciel de P = AB .
 Déterminez l'ordre de complexité dans le pire des cas de l'algorithme proposé.

2.3 Résolution des récurrences linéaires

Bien que le théorème maître soit applicable pour le calcul de la complexité de nombreux
algorithmes qui opèrent selon la stratégie diviser pour régner, il ne pourra, hélas, pas
s'appliquer dans tous les cas. En eet, il existe bien des algorithmes qui opèrent selon la
stratégie diviser pour régner mais dont la complexité ne vérie pas l'équation (2.1). Par
conséquent, le théorème maître ne pourra pas s'appliquer pour de tels algorithmes.

Exemple 3. Partant d'un exemple concret. L'une des énigmes classiques, qui se résolvent
aisément en utilisant la stratégie diviser pour régner, est celle connue sous le nom des
tours de Hanoi. Il s'agit de faire déplacer un ensemble de n disques, tous de diamètres
diérents, d'un pieu de départ vers un pieu d'arrivée. La diculté de la tâche provient
du fait qu'il est interdit d'empiler un disque de diamètre plus grand sur un disque de
8 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

Procédure Hanoi(n : entier, A : entier, B : entier, C : entier)


début
si n >= 1 alors
Hanoi(n-1,A,C,B)
Écrire("Déplacer un disque de ",A,"vers ",C)
Hanoi(n-1,B,A,C)
n
n
Algorithme 2.2 : Une procédure récursive qui, malgré sa simplicité, résout le
problème complexe des tours de Hanoi.

diamètre plus petit. On dispose, néanmoins, d'un pieu supplémentaire qui va permettre de
contourner la diculté.
An de concevoir un algorithme qui permet de déterminer la séquence de déplacement
à eectuer pour résoudre l'énigme, on se propose d'appliquer, encore une fois, la stratégie
diviser pour régner. Ainsi, pour résoudre le problème pour n disques, on peut procéder
comme suit :
 On résout le problème pour n-1 disques en déplaçant les n-1 plus petits disques du
pieu A vers le pieu B ;
 on déplace le disque le plus large du pieu A vers le pieu C ;
 On résout, une seconde fois, le problème pour n-1 disques en déplaçant les n-1 plus
petits disques, qui sont désormais autour du pieu B, vers le pieu C.

Une simple traduction de cette démarche en langage algorithmique donne la procédure


récursive Hanoi, (voir Algorithme 2.2), qui construit et ache la séquence des déplace-
ments à eectuer pour faire passer la pile de n disques du pieu A vers le pieu C, tout en
respectant les règles de l'énigme. La concision de cette procédure est remarquable, si on
tient compte de la diculté de la résolution de l'énigme.
Passant, à présent à l'évaluation de la complexité de la procédure Hanoi. D'après le
pseudo-code, la fonction de complexité de cette procédure vérie l'équation de récurrence
suivante :

1 si n=1
f (n) = (2.4)
2f (n − 1) + 1 si n≥2

On constate que la complexité de la procédure Hanoi ne vérie pas l'équation de récurrence


décrite par (2.1). En fait, il s'agit d'une équation de récurrence linéaire, contrairement à
(2.1), qui n'est pas linéaire.

Étant donnée une suite de nombres (f (n))n∈N , une équation qui relie le nème terme
aux termes qui le précèdent est appelée équation récurrente. La résolution d'une équation
récurrente consiste à trouver une expression du nème terme en fonction du seul paramètre
n. Les équations récurrentes sont divisées en deux catégories : celles qui sont linéaires et
celles qui ne le sont pas. Dans ce qui suit, on étudie une catégorie restreinte d'équations
récurrentes linéaires.
2.3. RÉSOLUTION DES RÉCURRENCES LINÉAIRES 9

Dénition 1. Une suite de nombres (f (n))n∈N satisfait une relation de récurrence


linéaire d'ordre k , si et seulement si, il existe des constantes c0 , . . . , ck et d0 , . . . , dk−1
telles que

ck f (n + k) + ck−1 f (n + k − 1) + . . . + c0 f (n) = g(n) (2.5)

f (n0 ) = d0 , . . . , f (n0 + k − 1) = dk−1

où g(n) est une fonction quelconque en n.

Remarques :
 Dans la Dénition 1, les constantes d0 , . . . , dk−1 dénissent ce qu'on appelle les
conditions initiales, qui sont nécessaires au démarrage de toute récursion.
 Le nombre de constantes, k, dénissant les conditions initiales, est appelé l'ordre
de la récursion.
 Si g(n) = 0 alors la relation (2.5) est dite homogène, sinon, elle est non homogène.
 La relation de récurrence (2.1) supposée par le master theorem est non linéaire, à
n
cause du terme .
b

Exemple 4. La relation de récurrence vériée par la fonction de complexité de la procé-


dure Hanoi, (voir Equation (2.4)), est une relation de récurrence linéaires non homogène
d'ordre 1.

2.3.1 Résolution des récurrences homogènes

Une équation de récurrence linéaire est homogène si elle a la forme de l'équation (2.5),
avec g(n) = 0, donc, si elle a la forme

ck f (n + k) + ck−1 f (n + k − 1) + . . . + c0 f (n) = 0 (2.6)

f (n0 ) = d0 , . . . , f (n0 + k − 1) = dk−1

La résolution de telles équations s'appuie sur des équations impliquant des polynômes qui
sont dénis comme suit :

Dénition 2. L'équation caractéristique de la relation (2.6) est l'équation polyno-


miale suivantes :
ck xk + ck−1 xk−1 + . . . + c0 = 0 (2.7)

Remarques :
 Comme on peut le constater, l'équation caractéristique ne tient pas compte des
conditions initiales.
 Le degré du polynôme intervenant dans l'équation caractéristique d'une relation
de récurrence linéaire homogène correspond à l'ordre de la relation.

Exemple 5. La suite de Fibonacci est une suite entière qui peut être dénie comme suit :

 0 si n=0
f (n) = 1 si n=1 (2.8)
f (n − 1) + f (n − 2) sinon

10 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

Comme on peut le constater, le terme général de la suite de Fibonacci est donnée par une
équation de récurrence linéaire homogène d'ordre 2. Son équation caractéristique est la
suivante :
x2 − x − 1 = 0 (2.9)

Theorem 2. La solution générale de l'équation (2.6) a la forme suivante :

i −1
`
( m
)
X X
f (n) = rin ai,j nj (2.10)
i=1 j=0


 ` ≤ k désigne le nombre de racines distinctes de l'équation caractéristique
(2.7).
 ri désigne une racine de l'équation caractéristique (2.7).
 mi désigne la multiplicité de la racine ri .
 Les ai,j sont des constantes qui sont déterminées à partir des conditions
initiales.

Exemple 6. Reprenons le cas de la suite de Fibonacci. On peut vérier que l'équation dé-
nissant le terme général de cette suite est une équation de récurrence linéaire homogène.
Son équation caractéristique à été donnée dans (2.9). Cette équation admet deux racines
distinctes, qui sont :
√ √
1+ 5 1− 5
et
2 2
D'où la solution générale :
√ √
1 + 5 n 1 − 5 n
f (n) = a1 + a2
2 2
Enn, les constantes a1 et a2 sont déterminées, à partir des conditions initiale, qui donnent
le système à deux équations et deux inconnues suivant :

f (0) = a1 + a2 = 0
√ √
1 + 5 1 − 5
f (1) = a1 + a2 =1
2 2
La résolution de ce système donne les valeurs suivantes :

1 1
√ et −√
5 5
Le terme général de la suite de Fibonacci est donc égal à :
( √ √ )
1 1 + 5 n 1 − 5 n
f (n) = √ − (2.11)
5 2 2
2.3. RÉSOLUTION DES RÉCURRENCES LINÉAIRES 11

horizontal
vertical

Figure 2.1  Une grille 2 × 10 et des dominos 1 × 2 ou 2 × 1.

Figure 2.2  Un pavage possible d'une grille 2 × 10 par des dominos 1 × 2 ou 2 × 1.

Exemple 7. On voudrait paver une grille de dimension 2×n (voir Figure 2.1-gauche)
par des dominos verticaux ou horizontaux (voir Figure 2.1-droite). Un exemple d'un tel
pavage est montré dans la Figure 2.2.
Pour concevoir un algorithme qui peut générer tous les pavages possibles, on procède en
scindant le problème en deux sous-problèmes, car il y a deux façons de placer des dominos
dans la grille, qui sont les suivants :
 ou bien qu'on place un domino verticalement,
 ou bien qu'on en place deux horizontalement.
Signalons que la deuxième possibilité ne peut être appliquée que s'il reste assez d'espace
dans la grille, ce qui se traduit par n ≥ 2.
L'algorithme 2.3 traduit cette démarche, en achant tous les pavages possibles d'une
grille de taille 2 × n, où n est une donnée. Pour que les achages soient correctement
exécutés, on utilise une pile dans laquelle chaque pavage est mémorisé avant d'être af-
ché. L'algorithme achera la lettre v, (resp. h), pour chaque domino vertical, (resp.
horizontal), utilisé.
On peut aussi concevoir une fonction récursive qui calcule le nombre de pavages pour
un n donné (voir Algorithme 2.3). On peut constater que le nombre de pavages possibles,
pour un n donné, vérie la même relation de récurrence que celle vériée par la suite
de Fibonacci. On conclut donc que le nombre de pavage est donné par l'équation (2.11).
Enn, on peut facilement déduire, à partir de l'équation (2.11), que l'ordre de complexité
n
de l'algorithme 2.3 est O(c ), où c > 1 est une constante.

2.3.2 Résolution des récurrences non homogènes

Une équation de récurrence linéaire est dite non homogène si elle a la forme de (2.5),
avec g(n) 6= 0. An de résoudre de telles équations, on peut les transformer en des équa-
tions homogènes. La démarche décrite dans le paragraphe précédent est alors appliquée
pour obtenir la solution de l'équation transformée. Des transformations d'équations non
homogènes, plutôt simples, sont décrites dans les deux exemples suivant :

Exemple 8. Le terme général exprimant la complexité de la procédure Hanoi, donné par


l'Equation (2.4) est déni par une équation de récurrence linéaire non homogène suivante :

f (n) − 2f (n − 1) = 1
12 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

Procédure Pavage(n : entier, P : tPile)


début
si n = 0 alors
AcherPile(P)
sinon
Empiler(P,'v')
Pavage(n1,P)
Dépiler(P)
si n >= 2 alors
Empiler(P,'h')
Empiler(P,'h')
Pavage(n2,P)
Dépiler(P)
Dépiler(P)
n
n
n
Fonction NbPavage(n : entier) :entier
début
si n <= 2 alors
retourner n
n
retourner NbPavage(n1) + NbPavage(n2)
n
Algorithme 2.3 : Procédure achant tous les pavages d'une grille 2 × n par des
dominos 1 × 2 ou 2 × 1. Fonction comptant le nombre de pavages d'une grille 2 × n
par des dominos 1 × 2 ou 2 × 1.
2.3. RÉSOLUTION DES RÉCURRENCES LINÉAIRES 13

Cette relation est aussi vraie pour n + 1, et on a donc :

f (n + 1) − 2f (n) = 1

En soustrayant, membre à membre, les deux dernière équations, on obtient :

f (n + 1) − 3f (n) + 2f (n − 1) = 0

qui est bien une équation de récurrence linéaire homogène d'ordre 2. On a donc besoin des
conditions initiales suivantes :

f (0) = 0, f (1) = 1

En appliquant la méthode de résolution des équations homogènes, on obtient :

f (n) = 2n − 1

Exemple 9. Le présent exemple porte sur l'analyse de la complexité du tri par insertion.
Rappelons que la démarche générale de ce tri consiste à insérer, à chaque étape, le ième
éléments dans le sous-tableau commençant à la première case et de taille i − 1, qui est
supposé être déjà trié. Une version récursive de ce tri (voir Algorithme 2.4), permet de
mettre en évidence une relation de récurrence linéaire non homogène que vérie la fonction
de complexité de ce tri :
f (n) − f (n − 1) = n
qui est aussi vraie pour un tableau de n+1 éléments :

f (n + 1) − f (n) = n + 1

En soustrayant les deux équations, membre à membre, on obtient :

f (n + 1) − 2f (n) + f (n − 1) = 1

Ce qui donne, pour n+1 :

f (n + 2) − 2f (n + 1) + f (n) = 1

En soustrayant, encore une fois, les deux dernières équations, membre à membre, on
obtient :
f (n + 2) − 3f (n + 1) + 3f (n) − f (n − 1) = 0
Il s'agit là d'une équation de récurrence linéaire homogène d'ordre 3. On a donc besoin
des conditions initiales suivantes :

f (0) = 0, f (1) = 1, f (2) = 3

En appliquant la méthode de résolution des équations homogènes, on obtient :

n(n + 1)
f (n) =
2
14 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

Procédure TriInsertionRec(T : tableau d'entier, n : entier)


variables
i,v : entier
début
si n >= 2 alors
TriInsertionRec(T,n1)
v := T[n]
i := n
tant que i >= 2 et T[i-1] > v faire
T[i] := T[i1]
i := i1
n
T[i] := v
n
n
Algorithme 2.4 : Version récursive du tri par insertion.

An de homogénéiser (c-à-d annuler le terme g(n) de l'équation (2.5)) des équations
linéaires plus complexes, on fait recours à un opérateur noté E , qui est déni comme suit.

Dénition 3. L'opérateur d'avancement E est déni comme suit :



c si f (n) = c, ∀n ∈ N,
E(f (n)) =
f (n + 1) sinon

où c est une constante.

Par exemple, E(n2 ) = (n + 1)2 = n2 + 2n + 1.


Pour toute constante c, on déni l'opérateur de constante de même nom par :

c(f (n)) = cf (n)

On peut alors combiner l'opérateur d'avancement E avec lui même ou/et avec des opéra-
teurs de constantes, pour obtenir de nouveaux opérateurs. La combinaison d'opérateurs
peut s'exprimer comme la somme ou le produit de deux opérateurs et on a :

(E1 + E2 )(f (n)) = E1 (f (n)) + E2 (f (n))


(E1 × E2 )(f (n)) = E1 (E2 (f (n)))

où E1 et E2 sont deux opérateurs quelconques.

Exemple 10.
 (E − 1)(c) = 0, pour toute constante c.
 (E − 2)(2n ) = E(2n ) − 2(2n ) = 2n+1 − 2n+1 = 0.
 (E − 1)3 (n2 ) = (E − 1)2 (2n + 1) = (E − 1)(2) = 0.

Exemple 11. La complexité de l'algorithme de Gauss pour la résolution des systèmes


de n d'équations linéaires à n inconnues vérie la relation de récurrence linéaires non
2.3. RÉSOLUTION DES RÉCURRENCES LINÉAIRES 15

g(n) Opérateur
c E−1
Pk (n) (E − 1)k+1
αn E−α
αn Pk (n) (E − α)k+1

Table 2.1  Quelques opérateurs pour l'élimination de fonctions g(n) usuelles.

homogène suivante :

0 si n=0
f (n) =
f (n − 1) + n2 sinon

Pour calculer le terme général de cette fonction complexité, on procède à son homogénéi-
3
sation. On applique alors l'opérateur (E − 1) qui, d'après l'exemple précédant annule la
2 3 2 3
fonction g(n) = n , c-à-d, (E − 1) (n ) = 0. L'application de l'opérateur (E − 1) aux
deux membres de l'équation de récurrence, on obtient :

(E − 1)3 (f (n) − f (n − 1)) = (E − 1)2 (f (n + 1) − 2f (n) + f (n − 1))


= (E − 1)(f (n + 2) − 3f (n + 1) + 3f (n) − f (n − 1))
= f (n + 3) − 4f (n + 2) + 6f (n + 1) − 4f (n) + f (n − 1)
= (E − 1)3 (n2 )
= 0

Ce qui est équivalent à

f (n + 4) − 4f (n + 3) + 6f (n + 2) − 4f (n + 1) + f (n) = 0

Il s'agit d'une équation de récurrence linéaire homogène d'ordre 4. On a donc besoin des
conditions initiales suivantes :

f (0) = 0, f (1) = 1, f (2) = 5, f (3) = 14

En appliquant le Théorème 2, on obtient

n(n + 1)(2n + 1)
f (n) =
6

Nous terminons l'étude des équations de récurrence linéaire non homogène en donnant,
dans le Tableau 2.1, les opérateurs à appliquer pour éliminer les fonctions g(n) les plus
courantes quand il s'agit de fonctions exprimant des complexités. Dans le Tableau 2.1, c
désigne une constante, Pk (n) un polynôme de degré k et α un réel.
16 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

Exercice 6. Vous avez à choisir entrer trois algorithmes qui appliquent la straté-
gie diviser pour régner an de résoudre le même problème. Les trois algorithmes
opèrent de la manière suivante :
 L'algorithme A résout le problème en le décomposant en 5 sous-problèmes
ayant chacun la moitié de la taille et en combinant les solutions partielles en
temps linéaire.
 L'algorithme B résout le problème, de taille n, en le décomposant en 2 sous-
problèmes de taille n−1 et en combinant les solutions partielles en temps
constant.
 L'algorithme C résout le problème, de taille n, en le décomposant en 9 sous-
problèmes de taille n/3 et en combinant les solutions partielles en temps
quadratique.
Précisez l'ordre de grandeur asymptotique de la complexité de chacun des algo-
rithmes. Lequel des trois algorithmes choisiriez-vous pour résoudre le problème ?

Exercice 7. Le tri rapide (quick-sort), comme le tri par fusion, s'appuie sur la
stratégie diviser pour régner. Ainsi, pour trier les éléments d'un tableau T[deb..n]
dont les indices sont comprit entre un indice de début et un indice de n, le tri rapide
procède comme suit :
Le tableau T[deb..n] est scindé en deux sous-tableaux T[deb..m-1] et T[m..n], où
m est un entier comprit entre deb et n. Les éléments des deux sous-tableaux sont
réarrangés de telle sorte que :

tous les éléments de T[deb..m-1] sont inférieur ou égal à T[m] qui, à son tour,
doit être inférieur ou égal à tous les éléments de T[m+1..n].

Le tri des deux sous-tableaux T[deb..m-1] et T[m+1..n] sont, ensuite, assuré par
deux appels récursifs au même module de tri rapide. À la diérence du tri par fusion,
le tableau T[deb..n] se trouve trié du moment que les deux sous-tableaux sont triés.
Un point crucial du tri rapide est le choix de l'indice m au niveau duquel le tableau
T[deb..n] est scindé en deux. Pour des raisons de simplicité, on suppose que cet
indice correspond à la position nale de l'élément T[n], qui est alors désigné par
le pivot, dans le tableau T[deb..n].

1. Proposez une fonction Partition qui permet de réarranger un tableau


T[deb..n] de sorte que la condition du partitionnement décrite ci-dessus
soit vériée, et qui retourne la position nale de T[n] dans T[deb..n].

2. Proposez une procédure récursive qui met en ÷uvre le principe du tri rapide.

3. Discutez de la complexité du tri rapide dans le cas où


 à chaque étape de la récursion, le tableau à trier est bi-partitionné de telle
sorte que le rapport de taille entre les deux partitions soit une constante.
 le tableau est déjà trié.
2.4. LA MÉDIANE EN TEMPS LINÉAIRE 17

k
T[i] < m m T[j] > m

Figure 2.3  Etat du tableau après réarrangement par rapport à la médiane des médianes
(m).

2.4 La médiane en temps linéaire

Il s'agit de trouver le ième plus petit élément d'un tableau d'entiers T[1..n] donné pour
un i donné. Par exemple, si i = 1 alors il s'agit de trouver le plus petit élément du tableau
T et si i = n alors c'est le plus grand élément qu'il faut trouver. Enn, si i = (n + 1) div 2
alors il s'agit de trouver l'élément médian du tableau, c'est-à-dire celui qui occuperai la
case du milieu si le tableau été trié. Notons que si n est pair et que, par conséquent, le
tableau n'admet pas une case du milieu proprement dite alors on conviendra que l'élément
médian sera celui qui occupe la case (n div 2) = (n + 1) div 2. Ce choix sera désigné
par la médiane inférieure.
Une première démarche possible pour résoudre le problème consiste à trier le tableau
puis renvoyer comme résultat l'élément qui occupe la ième case du tableau trié. Cette ap-
proche a une complexité, dans le pire des cas, qui est égale à celle du tri utilisé, O(n log n)
donc, si on utilise un tri ecace. Mais peut-on faire mieux ? La réponse est oui, car l'identi-
cation du ième élément ne requiert pas le tri de tout le tableau. Cependant, ceci demande
une démarche plus sophistiquée qui est du type  diviser pour régner . Les grandes lignes
de cette démarche sont les suivantes :

1. Diviser les éléments en des groupes de 5 éléments, avec éventuellement un groupe


qui contient moins que 5 éléments dans le cas où n n'est pas un multiple de 5.

2. Sélectionner, en temps constant, l'élément médian de chaque groupe. Si l'éventuel


groupe qui contient moins que 5 éléments, en contient un nombre pair alors on
conviendra de choisir la médiane inférieure.

3. A l'aide d'un appel récursif, déterminer l'élément médian des dn/5e éléments mé-
dian sélectionnés dans l'étape précédente. Désignons par m la médiane (des mé-
dianes) obtenue.

4. Réarranger le tableau de telle sorte que la médiane des médianes (m) soit dans la
case qu'elle occuperait si le tableau été trié, c'est-à-dire que, après réarrangement,
toutes les éléments précédant m dans le tableau seront plus petit que m et tous
ceux qui succèdent à m dans le tableau seront plus grands que m (voir Figure 2.3).
Désignons par k l'indice de la case contenant m dans le tableau réarrangé.
5.  Si i = k alors retourner m, sinon
 si i < k alors retourner le résultat de l'appel récursif avec T [1..k − 1] et i, sinon
 retourner le résultat de l'appel récursif avec T [k + 1, n] et i − k .

La complexité de l'algorithme 2.5 peut être étalblie en inférant une relation de ré-
currence vériée par la fonction de complexité de l'algorithme. Une telle relation peut
être déduite en analysant chacune des étapes de l'algorithme. La division des élément
en des groupes de 5, (qui constitue la première étape de l'algorithme), peut se faire en
temps linéaire en déterminant une paire d'indices pour chacun des dn/5e sous-tableaux.
La deuxième étape de l'algorithme peut également être exécutée en temps linéaire en
18 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

triant les dn/5e sous-tableau de taille ne dépassant pas 5 et en sélectionnant les éléments
médians de chacun des sous-tableaux.
Pour l'étape suivante, qui contient un appel récursif, on constate que la taille du
tableau qui sera traité par l'appel récursif est bornée par dn/5e. Cette remarque sera
prise en compte lors de l'établissement de la relation de récurrence recherchée. Pour ce
qui est de réarranger le tableau, cette étape peut se faire en temps linéaire, O(n), comme
on peut le déduire du pseudo-code de la fonction Rearranger(), qui est détaillée dans
l'Algorithme 2.5.
La dernière étape de l'algorithme implique des appel récursifs sur une partie des don-
nées de départ. Il s'agit des éléments qui vont se trouver avant la médiane ou après, dont
il va falloir borner supérieurement le nombre an d'établir la relation de récurrence que
va vérier le fonction de complexité de l'algorithme. Chaque sous-tableau de taille 5 dont
la médiane est plus petite que m contient, au moins, 3 éléments plus petits que m. De
même, chaque sous-tableau de taille 5 dont la médiane est plus grande que m contient, au
n
moins, 3 éléments plus grands que m. De plus, il y a d e sous-tableaux de taille 5, parmi
5
1 n 1 n
lesquels, au moins, d d ee − 1 ont une médiane plus petite que m et, au moins d d ee ont
2 5 2 5
une médiane plus grande que m. Il s'ensuit que l'on peut borner supérieurement la taille
des sous-tableaux T [1..k − 1] et T [k + 1..n] qui interviennent dans les appels récursifs de
l'étape 5, par
3n 7n
n− +3 = +3
10 10
.
On peut, à présent, établir une relation de récurrence que doit vérier la fonction de
complexité de l'Algorithme 2.5 :

7n n
f (n) ≤ f ( + 3) + f (d e) + c0 n
10 5
où le terme c0 n sert à borner la fonction de complexité de toutes les étapes linéaires de
l'algorithme.
Par ailleurs, on peut constater que la relation explicitée ci-dessus n'a pas la forme de
la relation de récurrence du master theorem ni celle des équations de récurrence linéaire.
On va alors faire recourt à une autre technique de calcul pour déduire la complexité de
l'Algorithme 2.5. Tout d'abord, on peut facilement vérier que pour des données de taille
< 41, la complexité des calcul est constante. Le choix de la constante n0 = 41 sera justié
par la suite. On fait alors l'hypothèse que l'algorithme en question a une complexité
0 0
linéaire pour des tailles de données n telle que n < n et on montre que sa complexité est
0 0 0
linéaire pour des données de taille n. Admettant donc que f (n ) ≤ cn , pour tout n < n,
7n n
où c est une constante. Or, pour tout n ≥ 41, on a + 3 < n et d 5 e < n. Il s'ensuit,
10
d'après l'hypothèse ci-dessus, que

7n n 7n n
f (n) ≤ f ( + 3) + f (d e) + c0 n ≤ c( + 3) + c( + 1) + c0 n
10 5 10 5
Ceci implique que
−cn
f (n) ≤ cn + ( + 7c + c0 n)
10
Il sut donc que le terme ( −cn
10
+4c+c0 n) soit nul ou négatif pour déduire que f (n) ≤ cn et
10c0 n n
donc que f (n) est linéaire. On doit alors avoir c ≥ . Or, pour n ≥ 41, on a ≤ 41
n−40 n−40
2.5. L'ENVELOPPE CONVEXE EN TEMPS SUPER-LINÉAIRE 19

Elément plus grand m

Médiane plus grande

Figure 2.4  Illustration de la stratégie employée dans le calcul de la médiane en temps


linéaire.

10c0 n
(d'où le choix de n0 = 41),
ce qui veut dire que la quantité
n−40
est constante. On déduit
0
que f (c) ≤ cn, pour toute constante c ≥ 410c , ce qui implique que f (n) est bien d'ordre
linéaire.

2.5 L'enveloppe convexe en temps super-linéaire

Il s'agit de déterminer l'enveloppe convexe d'un ensemble de points dans le plan, un


problème classique de géométrie algorithmique. La donnée de ce problème est un ensemble
de n points du plan, chaque point étant déni par ses coordonnés cartésiennes. Le résultat
est l'enveloppe convexe des points en entrée.
La dénition de la notion d'enveloppe convexe passe par celle de polygone convexe.

Dénition 4.
 Un polygone du plan est dit convexe si chaque segment délimité par deux
points du polygone est entièrement dans le polygone.
 L' enveloppe convexe d'un ensemble de points du plan est le plus petit poly-
gone convexe contenant tous les points de l'ensemble.

La Figure 2.5 montre deux polygones, un qui est convexe et l'autre pas. Elle montre aussi
un ensemble de points avec leur enveloppe convexe.

Les sommets de l'enveloppe convexe d'un ensemble de points E sont forcement des
points de E, et il se trouve qu'une enveloppe convexe, qui est un polygone convexe, est
complètement déterminée par l'ensemble de ses sommets. Il s'ensuit que la détermination
de l'enveloppe convexe d'un ensemble de point revient à l'identication des sommets de
cette enveloppe. Les points de l'ensemble qui ne sont pas des sommets de l'enveloppe
convexe sont appelés points intérieurs. D'après un théorème bien connu, (the Carathéo-
rady theorem ), un point est intérieur, si et seulement si, il s'écrit comme une combinaison
3
convexe des autres points de l'ensemble. Cette dernière remarque donne l'idée d'un algo-

3. Une combinaison convexe est une combinaison linéaire impliquant exclusivement des coecients
positifs dont la somme vaut 1.
20 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

1 Fonction Select(T : tableau d'entier, deb, n, i : entier) :entier


2 variables
3 m,q,r,k : entier
4 M : tableau [1..maxTail] d'entier
5 début
6 si n  deb >= 4 alors
7 q := (ndeb+1) div 5
8 r:= (ndeb+1) mod 5
9 pour k:=0 à q1 faire Tri(T, deb+5*k, deb+5*k+4)
10 Tri(T, deb+5*q, n)
11 pour k:=0 à q1 faire M[k+1] := T[deb+5*k+2]
12 si r = 0 alors
13 m := Select(M, 1, q, (q+1) div 2)
14 sinon
15 M[q+1] := T[deb + 5*q + (r div 2)]
16 m := Select(M, 1, q+1, (q+2) div 2)
17 n
18 k := Rearranger(T, deb, n, m)
19 si i = kdeb+1 alors
20 retourner m
21 sinon
22 si i < kdeb+1 alors
23 retourner Select(T, deb, k1, i)
24 sinon
25 retourner Select(T, k+1, n, i+debk1)
26 n
27 n
28 sinon
29 Tri(tab,deb,n)
30 retourner T[deb+i-1]
31 n
32 n
33 Fonction Rearranger(var T : tableau d'entier, deb, n, pivot : entier)
34 variables
35 i, j, iPivot : entier
36 début
37 i := deb  1
38 pour j := deb à n faire
39 si T[j] < pivot alors
40 i := i+1
41 Permuter(T,i,j)
42 n
43 n
44 iPivot := Position(T, deb, n, pivot)
45 Permuter(T, i+1, iPivot)
46 retourner i+1
47n
Algorithme 2.5 : Algorithme de la sélection du i ème éléments d'un tableau.
2.5. L'ENVELOPPE CONVEXE EN TEMPS SUPER-LINÉAIRE 21

rithme naïf, (brute-force algorithm ), qui élimine tous les points intérieurs et ne garder que
les sommets, déterminant ainsi l'enveloppe convexe. Mais un tel algorithme est coûteux
en temps de calcul, car il serait question de résoudre autant de programme linéaire que
de points dans l'ensemble de départ. Or, la complexité de la résolution d'un programme
linéaire est plus que cubique.

L'algorithme de l'identication de l'enveloppe convexe que nous allons étudier est


un exemple classique de l'application de la stratégie  diviser pour régner . Il s'agit de
l'algorithme des deux ponts, un algorithme récursif qui opère selon la démarche suivante :
 A chaque étape de l'algorithme, l'ensemble des n points est scindé en deux sous-
n n
ensembles de tailles respectives d e et b c, selon un critère bien déterminé. Ce
2 2
n
critère est la valeur de l'abscisse. Ainsi, les d e points qui ont les plus petites
2
valeurs d'abscisse seront mis dans le sous-ensemble de gauche, le reste des points
seront aecté au sous-ensemble de droit.
 L'algorithme calcul alors l'enveloppe convexe de chacun des deux sous-ensembles,
séparément, en eectuant deux appels récursifs.
 L'algorithme rassemble, ensuite, les sommets des enveloppes convexes gauche et
droite, obtenues comme résultat des deux appels récursifs, tout en éliminant les
sommets qui sont des points intérieurs si l'on considère l'union des sommets des
deux enveloppes. Les sommets restant sont alors retournés comme résultat.

La détermination des points intérieurs, lors de la phase de fusion des enveloppes gauche
et droite, (la troisième étape), constitue la tâche la plus délicate. Elle est réalisée par une
technique qui a donné son nom à l'algorithme : algorithme des deux pont s. Il s'agit d'un
pont supérieur et d'un pont inférieur, qui sont en réalité deux droites. Chacune de ces
droites passe par un sommet de l'enveloppe gauche et un sommet de l'enveloppe droite.
Le pont supérieur est obtenu comme suit : Soit G le point le plus à droite de l'enveloppe
gauche et D le point le plus à gauche de l'enveloppe droite. Si le point qui succède à
G, en parcourant les sommets de l'enveloppe gauche suivant le sens trigonométrique,
est au dessus de la droite (G, D) alors G est remplacé par son successeur. De même,
si le point qui précède D, en parcourant les sommets de l'enveloppe droite suivant le
sens trigonométrique, est au dessus de la droite (G, D) alors D est remplacé par son
prédécesseur. On itère ces deux actions autant que possible. A la n du processus, la
droite (G, D) fournit le pont supérieur. La Fonction 2.6 met en langage algorithmique
cette démarche.
Pour avoir le pont inférieur, on procède de manière symétrique. Désignons par (G0 , D0 )
les deux points qui dénissent le pont inférieur. L'enveloppe convexe nale sera formée
0
par les sommets de l'enveloppe gauche compris entre G et G (inclus) et ceux de l'en-
0
veloppe droite compris entre D et D , toujours en suivant le sens trigonométrique (voir
Figure 2.5). L'algorithme des ponts pour le calcul de l'enveloppe convexe d'un ensemble
de points est détaillée dans l'Algorithme 2.7.

Évaluant, à présent, la complexité de l'algorithme des deux ponts. Comme étape préa-
lable, on procède au tri du tableau contenant les points de l'ensemble E, selon l'ordre
croissant des abscisses. Ceci peut se faire en O(n log n) par un tri fusion, par exemple.
L'algorithme des deux ponts est un algorithme récursif qui applique la stratégie diviser
pour régner en subdivisant le problème en deux, (deux appels récursifs), et les données
22 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

Figure 2.5  Deux polygnes, le premier est convexe (haut gauche) et l'autre pas. Enve-
loppe convexe d'un ensemble de points du plan (bas).

également en deux (le tableau T est scindé en deux moitiés). Pour fusionner les deux enve-
loppes convexes, solutions des deux sous-problèmes, l'algorithme des deux ponts procède
au calcul des deux ponts. Cette tâche s'exécute en O(n), car il faut parcourir les sommets
des deux enveloppes convexes, dont le nombre ne peut dépasser n. De plus, le teste de la
position d'un point par rapport à une droite, (au-dessus ou au-dessous), peut se faire en
temps constant en déterminant l'équation de la droite. Il en résulte de cette analyse que
la complexité de l'algorithme des deux pont vérie la récursion non linéaire suivante :

n
f (n) = 2f (d e) + O(n)
2
En appliquant le Théorème 1, on obtient une complexité en O(n log n) ; un temps super-
linéaire, donc.

Exercice 8. Etant donnée en ensemble de n points du plan, on voudrait déterminer


la paire de points la plus proche. Proposez un algorithme qui opère selon la stratégie
diviser pour régner an de déterminer une telle paire en O(n log n).

2.6 Conclusion

Le chapitre 2 à été dédié à l'étude de la stratégie de conception d'algorithme diviser


pour régner. Après une description du principe général de cette stratégie, nous l'avons
appliqué an de résoudre divers problèmes algorithmiques classiques. Une bonne partie du
chapitre a été consacrée à la présentation d'outils mathématiques permettant de calculer
la complexité des algorithmes conçus selon la stratégie diviser pour régner.
Voir Algorithmes 2.8, 2.9 et 2.10.
2.6. CONCLUSION 23

Figure 2.6  Illustration de la méthode des deux ponts. Le schéma montre les deux ponts
obtenus à la convergence de la technique de construction des ponts. L'étape de fusion des
deux enveloppes convexes entraîne l'élimination de trois points intérieurs.

Fonction PontSup(envG, envD : tListCircul) :(tPoint,tPoint)


// Les sommets des deux enveloppes sont stockés dans deux listes
// chaînées circulaires.
début
G := PlusADroit(envG)
D := PlusAGauche(envD)
stop := faux
tant que non stop faire
stop := vrai
tant que AuDessus(Succ(G),G,D) faire
stop := faux
G := Succ(G) ;
n
tant que AuDessus(Pred(G),G,D) faire
stop := faux
D := Pred(D) ;
n
n
retourner (G,D)
n
Algorithme 2.6 : Construction du pont supérieur.
24 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

Algorithme EnvConvDeuxPonts(T : tableau de tPoint, n : entier) : tListCircul


// T est un tableau de taille n contenant les points de E,
// qui supposé déjà trié par abscisse croissante.
// La sortie est une liste chainée circulaire contenant les sommets
// de l'enveloppe convexe.
début
si n <= 5 alors
L := BruteForceEnvConv(T,n)
sinon
TG := les d n2 e premiers éléments de T
TD := les b c derniers éléments de T
n
2
LG := EnvConvDeuxPonts(TG,d e)
n
2
LD := EnvConvDeuxPonts(TD,b c)
n
2
(G,D) := PontSup(LG,LD)
(G',D') := PontInf(LG,LD)
L := les éléments de LG compris entre G et G' plus les éléments de LD
compris entre D' et D
n
retourner L
n
Algorithme 2.7 : Construction du pont supérieur.
2.6. CONCLUSION 25

Procédure TriRapide(var T : tableau d'entier, deb, n : entier)


variables
m : entier
début
si deb < n alors
m := Partition(T,deb,n)
TriRapide(T,deb,m-1)
TriRapide(T, m+1,n)
n
n
Fonction Partition(var T : tableau d'entier, deb, n : entier)
variables
i, j : entier
début
i := deb  1
pour j := deb à n faire
si T[j] < T[n] alors
i := i+1
Permuter(T,i,j)
n
n
retourner i
n
Algorithme 2.8 : Le tri rapide.
26 CHAPITRE 2. LA STRATÉGIE DIVISER POUR RÉGNER

1 Fonction CompteInv(T : tableau d'entiers, deb, n : entiers) : entier


2 variables
3 nbInv, m, i, j : entiers
4 début
5 si (deb >= n) alors retourner 0
6 m := (deb+n) div 2
7 nbInv := CompteInv(T, deb, m) + CompteInv(T, m+1, n)
8 TriLin(T, deb, m)
9 TriLin(T, m+1, n)
10 i := deb
11 j := m+1
12 tant que (i<=m et j<=n) faire
13 si (T[i] > T[j]) alors
14 nbInv := nbInv + 1
15 j := j + 1
16 sinon
17 i := i + 1
18 n
19 n
20 retourner nbInv
21n
Algorithme 2.9 : Algorithme opérant en temps super-linéaire (O(n log n)) pour
compter le nombre d'inversion dans une permutation.

1 Fonction Dicho2D(elt : entier, M : matrice d'entiers, debi, ni, debj, nj :


entiers) : tCouple
// Cette version suppose que la matrice est indexée à partir de zéro
2 variables
3 i, j : entiers
4 début
5 si (debi > ni) ou (debj > nj) alors retourner (-1,-1)
6 i := (debi + ni) div 2
7 j := Dicho1D(elt, M[i], debj, nj)
8 si M[i][j] = elt alors retourner (i , j)
9 (i,j) := Dicho2D(elt, M, i + 1, ni, debj, j)
10 si (i <> -1 et M[i][j] = elt) alors retourner (i , j)
11 retourner Dicho2D(elt, M, debi, i - 1, j, nj)
12n
Algorithme 2.10 : Algorithme de recherche dichotomique dans une matrice dont
les lignes et les colonnes sont triés.

Vous aimerez peut-être aussi