Vous êtes sur la page 1sur 118

i

Université de Rennes I
ENSSAT Lannion
EII 2 et LSI 2

ALGORITHMIQUE AVANCÉE

Travaux Dirigés et Travaux Pratiques

Laurent Miclet et Patrick Bosc

8 mars 2007
ii

The Feynman Problem-Solving Algorithm :


1. write down the problem ;
2. think very hard ;
3. write down the answer.
Murray Gell-mann

Première version Mars 2004

Special credits to :
Nelly Barbot
Sabri Bayoudh
Marc Guyomard
Florent Nicard
Table des matières

0 Démonstrations, récurrences. 1
0.1 Rappels sur la démonstration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
0.1.1 Démonstration par l’absurde. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
0.1.2 Démonstration par récurrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
0.1.3 Démonstration par récurrence partielle . . . . . . . . . . . . . . . . . . . . . . . 3
0.1.4 Démonstration par récurrence totale . . . . . . . . . . . . . . . . . . . . . . . . 3
0.1.5 Démonstration par récurrence descendante . . . . . . . . . . . . . . . . . . . . 4
0.1.6 Démonstration par récurrence à plusieurs indices . . . . . . . . . . . . . . . . . 6
0.2 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

1 Complexité d’un algorithme 13


1.1 Rappels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.1 Ordres de grandeur de complexité : rappel des définitions. . . . . . . . . . . . . 13
1.1.2 Ordres de grandeur de complexité : remarques. . . . . . . . . . . . . . . . . . . 13
1.1.3 Temps de calcul pratique. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.2 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

2 Invariants, itération. 19
2.1 Rappels sur la logique de l’itération. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Exercices. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

3 Diviser et Diminuer pour Régner 21


3.1 Rappels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.1.1 Formule de récurrence 1 (Diviser n en en b parties) : . . . . . . . . . . . . . . 21
3.1.2 Formule de récurrence 2 (Diminuer n à n − 1) : . . . . . . . . . . . . . . . . . 22
3.2 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

4 Essais successifs 37
4.1 Rappels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.1.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.1.2 Algorithmes génériques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.2 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

5 Programmation par Séparation et Evaluation Progressive 51


5.1 Rappels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
5.1.1 Principe : comparaison avec Essais Successifs . . . . . . . . . . . . . . . . . . . 51
5.1.2 Algorithme générique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
5.1.3 Un point de vue plus général. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
5.2 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

iii
iv TABLE DES MATIÈRES
6 Programmation dynamique. 55
6.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
6.2 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
6.2.1 Séquences et automates. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
6.2.2 Optimisation combinatoire. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
6.2.3 Dénombrements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
6.2.4 Graphes : plus courts chemins et autres. . . . . . . . . . . . . . . . . . . . . . . 68

7 Algorithmes gloutons 71

8 Mélanges 75
8.1 Un problème d’optimisation particulier : le "sac à dos" . . . . . . . . . . . . . . . . . . 75
8.1.1 Trois problèmes légèrement différents. . . . . . . . . . . . . . . . . . . . . . . . 75
8.1.2 Trois solutions extrêmement différentes . . . . . . . . . . . . . . . . . . . . . . 77
8.2 Exercices. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
8.3 Travaux Pratiques. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

99 Exercices en cours de rédaction. 87

99 Exercice en test. 103

99 Exercice en test. 107


TABLE DES MATIÈRES v

Notations, conventions.
Notations.
Nombres.

N est l’ensemble des nombres entiers naturels : {0, 1, . . .}.


Q est l’ensemble des nombres rationnels et Q+ celui des rationnels strictement positifs.
R est l’ensemble des nombres réels et R+ celui des réels strictement positifs.
C est l’ensemble des nombres complexes.
La valeur entière du nombre réel a est notée ⌊a⌋ ; la notation ⌈a⌉ désigne le premier entier stric-
tement supérieur à a.

Vecteurs, matrices et tableaux.

Alphabets, séquences.

– On appelle alphabet Σ un ensemble fini de lettres dont le cardinal est notée |X|.
– Une séquence, ou chaîne, ou mot ) sur Σ est une suite finie de lettres de Σ. La longueur |u| d’une
séquence u est son nombre de lettres.
Par exemple pour l’alphabet Σ = {a, b, c} et les séquences u = acbb et v = cab, on a |u| = 4 et
|v| = 3.
– On appelle préfixe d’une séquence une séquence par laquelle elle commence et suffixe d’une
séquence une séquence par laquelle elle finit.
Par exemple, le préfixe de longueur 2 de u est ac, le suffixe de longueur 3 de v est cab.
– On note u[i] la lettre numéro i de u. Par exemple : u[3] = b.
Pour 1 ≤ i ≤ j ≤ |u|, on note u[i : j] = ui , ui+1 , . . . , uj un facteur 1 de u. Par exemple,
u[3 : 4] = bb.
– On note ǫ la séquence de longueur nulle.
– La concaténation des séquences u et v, qui consiste à créer une séquence de longueur |u| + |v|,
ayant u pour préfixe et v pour suffixe, se note sans (si nécessaire avec) signe d’opération : uv
(ou u.v).
La concaténation est associative, a ǫ pour élément neutre, mais n’est pas commutative : (uv)w =
u(vw) = uvw, ǫu = uǫ = u, mais en général uv 6= vu.
– w est une sous-séquence 2 de u si on peut trouver toutes les lettres de w dans u, dans le même
ordre.
Plus formellement : w est une sous-séquence de u s’il existe une suite d’entiers (i1 , . . . , i|w| ),
avec 1 ≤ i1 ≤ . . . ≤ i|w| ≤ i|u| et w = ui1 . . . ui|w| .
Un facteur est un type particulier de sous-séquence : par exemple, ab est une sous-séquence de
acbb, mais n’en est pas un facteur.

Logos.

Les exercices sont étiquetés selon leur niveau de difficulté par un seul ✍(facile), ou deux ✍✍(plus
difficile) ou trois ✍✍✍(très difficile, ou niveau recherche).

Algorithmes.
Les algorithmes de ce document sont écrits dans un pseudo-code banal dont voici un exemple :
1: Appel : Algo(T , 1, n, m, Decision) ; imprimer(decision)

1 En anglais : substring.
2 En anglais : subsequence
vi TABLE DES MATIÈRES
précondition : n ≥ 1
2: procedure Algo(T , i, j, m : in ; Decision : out)
3: début
4: si i = j et m = T (i) alors
5: Decision ← (m = T (i)) /% L %/’affectation se note ←
6: sinon
7: si m ≤ T (⌊ i+j
2 ⌋) alors
8: Algo(T , i, ⌊ i+j
2 ⌋, m, Decision)
9: sinon
10: Algo(T , ⌊ i+j
2 ⌋ + 1, j, m, Decision)
11: fin si
12: fin si
13: fin
La procédure récursive appelée Algo recherche par dichotomie si la valeur m est dans le tableau trié
T . La réponse est donnée par la variable booléenne Decision. Les faits que T est un tableau d’entiers
triés par ordre croissant, que m est un entier et que Decision est un booléen ne sont pas précisés dans
ce pseudo-code, mais dans l’énoncé de l’exercice qui le précède. Ceci pour alléger et pour ne présenter
que l’essentiel.
On remarque que les variables d’appel de la procedure sont qualifiées ici comme in ou out, ou si
besoin, in, out. La signification en est que les variables in sont nécessaires et ne sont pas modifiées
au cours de la procédure, les variables out sont calculées par la procédure. Cette convention peut être
allégée si cette information est inutile, en notant simplement :
procedure Algo(T , i, j, m, Decision)
Dans le cas d’une fonction, le résultat, s’il vaut r, est transmis sous la forme : resultat(r).
Chapitre 0

Démonstrations, récurrences.

0.1 Rappels sur la démonstration.


0.1.1 Démonstration par l’absurde.
Le raisonnement par l’absurde ou démonstration par l’absurde repose sur le principe logique
du tiers exclu, qui affirme qu’une assertion qui ne peut pas être fausse est forcément vraie.

Chercher à démontrer une proposition P par l’absurde se fait comme suit :


– supposer non P (c’est à dire supposer que P est fausse),
– constater une contradiction entre une implication de cette supposition et la né-
gation de P.
Dans ce cas, P ne peut pas être fausse et P est donc vraie. Si on ne constate pas de
contradiction, on ne peut rien dire sur P.

Par exemple, considérons la proposition P « il n’y a pas de plus petit nombre rationnel strictement
plus grand que 0 ». Dans un raisonnement par l’absurde, nous commençons par supposer la négation
de P, ce qui s’énonce : « il existe un plus petit nombre rationnel strictement positif », Appelons-le r.
Nous allons maintenant déduire une contradiction.
Soit s = r/2. Par construction, s est un nombre rationnel1 , strictement plus grand que 0 et
strictement plus petit que r. Mais cela est contradictoire avec la négation de P, qui affirme que r était
le plus petit nombre rationnel.
Ainsi nous pouvons conclure que la proposition d’origine P est nécessairement vraie : il n’existe
donc pas de plus petit nombre rationnel strictement plus grand que 0.

Un exemple : la démonstration de l’irrationnalité de la racine carrée de 2.


Nous voulons démontrer : il n’existe pas de rationnel positif dont le carré est 2, autrement dit : la
racine carrée de 2 est irrationnelle.
– Supposons qu’il existe un élément x = p/q de Q+ (ensemble des rationnels positifs) tel que
x2 = 2, avec p et q premiers entre eux (c’est à dire que p/q est une fraction irréductible). On
a : (p/q)2 = 2 donc p2 = 2q2
– Comme p2 = 2q2 on peut dire que p2 est un multiple de 2 ou encore que 2 divise p2 . Par
conséquentmanote 2 divise aussi p.
– Comme 2 divise p , il existe donc un entier naturel p ′ tel que p = 2p ′ et donc p2 = 4p ′2 c’est à
dire 2q2 = 4p ′2
– 2q2 = 4p ′2 implique q2 = 2p ′2 , donc q2 est un multiple de 2, ou encore 2 divise q2 , donc 2
divise q.
1 Si vous pensez que cette affirmation mérite une démonstration, donnez-la.

1
2 CHAPITRE 0. DÉMONSTRATIONS, RÉCURRENCES.

...

n0 n−1 n

Fig. 1 – Géométrie de la démonstration par récurrence. La propriété doit être vraie sur la zone grise
(initialisation à n0 ). La récurrence est alors : si le fait qu’elle soit vraie dans la zone hachurée (pour
n − 1) implique qu’elle est vraie dans la zone doublement hachurée (pour n) , alors elle est vraie pour
tout entier supérieur ou égal à n0 .

– 2 divise à la fois p et q, ce qui est contradictoire avec l’hypothèse que p et q sont premiers entre
eux.
En conclusion, il n’existe pas de rationnel positif dont le carré est 2.
Comme il n’existe pas non plus de rationnel négatif dont le carré est 2 (sinon l’opposé de ce nombre
serait un rationnel positif dont le carré serait 2), la racine carrée de 2 est irrationnelle2 .

0.1.2 Démonstration par récurrence


Cette démonstration (aussi appelée par « récurrence faible »ou « récurrence simple ») s’argumente
ainsi : si une propriété est vraie pour un certain n0 et si le fait qu’elle soit vraie pour un certain n
(supérieur à n0 ) implique qu’elle est vraie pour n+1, alors elle est vraie pour tout les entiers supérieurs
ou égaux à n0 .

Soit une propriété ou une formule P(n), dépendant de l’entier n ∈ N.


Si on peut démontrer les deux propriétés suivantes :
Initialisation. P(n0 ) est vraie pour un certain n0 ∈ N
Récurrence. ∀n ≥ n0 , P(n) vraie ⇒ P(n + 1) vraie
Alors on peut conclure :
Conclusion. P(n) est vraie pour tout entier n supérieur ou égal à n0 .

On appelle hypothèse de récurrence le fait de supposer vraie P(n), pour (essayer de) démontrer
P(n + 1). L’initialisation s’appelle aussi la base et l’étape de récurrence est aussi appelée induction.

Exemple

Démontrons par récurrence la formule :


n
X n(n + 1)
i=
2
i=1

Initialisation. Cette formule est vraie pour n0 = 1.


En effet, on a :
X1
i=1
i=1

et la formule, essayée pour n0 = 1, donne le bon résultat :


1
X 1(1 + 1)
=1
2
i=1

2 Cette démonstration est due à Euclide, environ 250 av. J.-C.


0.1. RAPPELS SUR LA DÉMONSTRATION. 3
Récurrence. Supposons la formule vraie pour un certain n et essayons de montrer qu’elle est vraie
pour n + 1. Autrement dit, nous essayons de prouver que l’hypothèse de récurrence

n
X n(n + 1)
i=
2
i=1

implique
n+1
X (n + 1)(n + 2)
i=
2
i=1

C’est en effet exact, puisque :

n+1
X Xn
i = ( i) + (n + 1)
i=1 i=1
n(n + 1)
= + (n + 1) par l’hypothèse de récurrence
2
n2 + n + 2n + 2
=
2
(n + 1)(n + 2)
=
2

Conclusion. La formule est vraie pour n = 1. De plus, si elle est vraie pour n, est vraie pour n + 1.
Par conséquent, cette démonstration par récurrence a prouvé qu’elle est vraie pour tout entier
strictement positif.

0.1.3 Démonstration par récurrence partielle

Soit une propriété P(n), dépendant de l’entier n ∈ N.


Si on peut démontrer les deux propriétés suivantes :
Initialisation. P(1) est vraie
Récurrence. ∀n ≥ 1 avec n = 2k , k ∈ N, k ≥ 1
P(n) vraie ⇒ P(2n) vraie
Alors on peut conclure :
Conclusion. P(n) est vraie pour tout n puissance entière de 2.

Cette variante peut aussi servir à démontrer une propriété sur les nombres pairs, ou tout sous-ensemble
infini de N constructible par induction. Quand on l’a appliquée, on peut éventuellement utiliser la
démonstration par récurrence descendante (voir le paragraphe 0.1.5).

0.1.4 Démonstration par récurrence totale


La démonstration par récurrence totale (ou forte, ou généralisée) paraît d’abord plus difficile à
appliquer que la récurrence classique. Elle est cependant commode et (à juste titre) très employée.
Elle affirme que si une propriété est vraie pour un certain n0 , et que si le fait qu’elle soit vraie
pour tous les entiers de n0 à un certain n implique qu’elle est vraie pour n + 1, alors elle est vraie
pour tout entier supérieur ou égal à n0 .
4 CHAPITRE 0. DÉMONSTRATIONS, RÉCURRENCES.

...

n0 n0 + 1 n−1 n

Fig. 2 – Illustration géométrique de la démonstration par récurrence totale. La propriété doit être
vraie sur la zone grise (initialisation pour n0 ). La récurrence est alors : si le fait qu’elle soit vraie
dans la zone hachurée (de n0 + 1 à n − 1) implique qu’elle est vraie dans la zone doublement hachurée
(pour n) , alors elle est vraie pour tout entier supérieur ou égal à n0 .

Soit une propriété P(n), dépendant de l’entier n ∈ N.


Si on peut démontrer les deux propriétés suivantes :
Initialisation. P(n0 ) est vraie (pour n0 ∈ N)
Récurrence. ∀n ≥ n0 , (∀k, n0 ≤ k ≤ n, P(k) vraie ) ⇒ P(n +
1) vraie
Alors on peut conclure :
Conclusion. P(n) est vraie pour tout n ≥ n0 .

Exemple

Démontrons par récurrence totale la propriété :


Tout nombre entier supérieur ou égal à 2 est le produit d’un3 ou de plusieurs nombres
premiers.

Initialisation. Cette propriété est vraie pour n = 2, puisque 2 est lui-même premier.
Récurrence. Supposons que tout entier strictement inférieur à n soit le produit d’un ou de plusieurs
nombres premiers.
Il y a deux cas exclusifs :
– Soit n est premier, et n lui-même est alors le produit d’un nombre premier.
– Soit n admet un diviseur d, différent de 1 et de n. On a donc n = dp, où d et p sont deux
entiers strictement inférieurs à n et supérieurs à 1. Chacun d’eux est donc le produit d’un
ou de plusieurs nombres premiers, et n est donc lui-même le produit de plusieurs nombres
premiers.
Conclusion. n est donc le produit d’un ou de plusieurs nombres premiers.

0.1.5 Démonstration par récurrence descendante


Soit une propriété ou une formule P(n), dépendant de l’entier n ∈ N.
Si on peut démontrer les deux propriétés suivantes :
Initialisation. P(n) est vraie pour un sous-ensemble infini de N
Récurrence. P(n) vraie implique P(n − 1) vraie
Alors on peut conclure :
Conclusion. P est vraie pour tout les entiers naturels.

3 Le « produit d’un nombre »est défini comme le nombre lui-même.


0.1. RAPPELS SUR LA DÉMONSTRATION. 5
Exemple

Cet exemple4 utilise à la fois la variante de la démonstration classique par récurrence (voir le
paragraphe 0.1.3) et la démonstration par récurrence descendante. Il s’agit de démontrer la fameuse
inégalité entre la moyenne arithmétique et la moyenne géométrique de n nombres réels positifs :
1 x1 + x2 + . . . + xn
(x1 x2 . . . xn ) n ≤ (0.1)
n
Dans un premier temps, on montre que cette propriété est vraie sur tous les entiers n qui sont une
puissance de 2. En effet, la démonstration est triviale pour n = 1 et elle est facile pour n = 2 (il suffit
de constater que l’inégalité x1 x2 ≤ ( x1 +x
2
2 2
) découle du fait qu’un carré est positif ou nul).
La variante de la démonstration classique par récurrence s’énonce alors : supposons la propriété
1 x1 + x2 + . . . + xn
(x1 x2 . . . xn ) n ≤ (0.2)
n

vraie pour n = 2k . Cette propriété est-elle alors vraie pour 2n = 2k+1 ?


C’est bien le cas : il suffit de réécrire l’équation 0.1 sous la forme

1
 1 1
 12
(x1 x2 . . . x2n ) 2n = (x1 x2 . . . xn ) n (xn+1 xn+2 . . . x2n ) n

1
et d’utiliser le fait que la propriété est vraie pour n = 2 en posant y1 = (x1 x2 . . . xn ) n et y2 =
1
(xn+1 xn+2 . . . x2n ) n On en déduit :
1 y1 + y2
(y1 y2 ) 2 ≤
2
ce qui démontre qu’elle est vraie pour 2n = 2k+1 .
On sait maintenant que la propriété est vraie pour tous les entiers du type n = 2k , avec k entier.
Il est temps d’utiliser le raisonnement par récurrence descendante.
Où en sommes nous ? La propriété à démontrer est vraie pour un sous-ensemble infini de N. Il nous
reste à essayer de montrer que si elle est vraie pour n’importe quel entier n, alors elle est vraie pour
n − 1. Pour ce faire, il est commode de définir un certain nombre z par :
x1 + x2 + . . . + xn−1
z=
n−1
Comme la propriété est supposée vraie pour tout ensemble de n nombres , elle est en particulier vraie
pour les nombres (x1 , x2 , . . . , xn−1 , z). Autrement dit :

1 x1 + x2 + . . . + xn−1 + z
(x1 x2 . . . xn−1 z) n ≤
n
Cette équation, grâce à la définition de z se ramène à :

1 (n − 1)z + z
(x1 x2 . . . xn−1 z) n ≤ =z
n
On en déduit :
(x1 x2 . . . xn−1 z) ≤ zn

et pour finir :
1 x1 + x2 + . . . + xn−1
(x1 x2 . . . xn−1 ) n−1 ≤ z =
n−1
ce qui est exactement la même chose que la propriété 0.1, mais pour n − 1 au lieu de n.
Par récurrence descendante, la propriété 0.1 est donc vraie pour tout entier n.
4 Sa paternité a été attribuée à Cauchy ([MAN89]).
6 CHAPITRE 0. DÉMONSTRATIONS, RÉCURRENCES.
..
.

..
.
m+1
m

1 ...

1 n n+1

Fig. 3 – Illustration géométrique d’une démonstration par récurrence totale à deux indices. La pro-
priété doit être vraie sur les zones infinies grises (initialisation). La récurrence est alors : si le fait
qu’elle soit vraie dans la zone finie hachurée implique qu’elle est vraie dans la zone finie doublement
hachurée, alors elle est vraie partout.

0.1.6 Démonstration par récurrence à plusieurs indices


Quand une propriété dépend de deux (ou plus de deux) entiers et non plus d’un seul, la démonstra-
tion par récurrence prend littéralement une nouvelle dimension ; nous présentons certains cas à deux
indices qui seront utiles par la suite. Donnons pour commencer un schéma possible de démonstration
par récurrence totale à deux indices.

Soit une propriété ou une formule P(n, m), dépendant de deux entiers n et m ∈ N.
On cherche à la démontrer par récurrence. Un schéma possible est le suivant : Si
on peut démontrer les deux propriétés suivantes :
Initialisation.
– P(i, 1) est vraie pour tout i
– P(1, j) est vraie pour tout j
Récurrence. P(i, j) est vraie pour tous les couples (i, j) tels que 1 ≤
i ≤ n et 1 ≤ j ≤ m
implique
– P(n + 1, j) est vraie pour 1 ≤ j ≤ m
– P(i, m + 1) est vraie pour 1 ≤ i ≤ n
– P(n + 1, m + 1) est vraie
Alors on peut conclure :
Conclusion. P(n, m) est vraie pour tout les couples d’entiers naturels.

Ce schéma de démonstration peut s’illustrer géometriquement par la figure 3.


D’autres schémas sont évidemment possibles. L’affaire consiste à s’assurer que la propriété que l’on
veut démontrer est vraie pour chaque couple d’entiers en « remplissant » le plan sans faire d’impasse
sur aucun de ses points. Graphiquement, le schéma de la figure 4 est aussi correct5 :
5 Dans la dimension horizontale, il s’agit d’une récurrence totale et dans la dimension verticale d’une récurrence

simple.
0.1. RAPPELS SUR LA DÉMONSTRATION. 7

..
.

m+1 ...

m ...

1 ...

1 n

Fig. 4 – Une autre démonstration par récurrence à deux indices. La propriété doit être vraie sur
la zone infinie grise (initialisation). La récurrence est alors : si le fait qu’elle soit vraie dans la zone
infinie hachurée implique qu’elle est vraie dans la zone infinie doublement hachurée, alors elle est vraie
partout.

Une formulation générale.

Les deux schémas précédents sont des cas particuliers d’une récurrence à deux indices plus générale,
qui nécessite une notion supplémentaire : l’introduction d’un ordre partiel dans le plan entier.
Définissons une relation d’ordre strict ≺ sur l’ensemble N × N des couples d’entiers par :
Si a < c et b ≤ d ou si a ≤ c et b < d alors (a, b) ≺ (c, d).
Cette relation est illustrée sur la figure 5. La démonstration générale par récurrence totale à deux
indices s’énonce alors ainsi (son illustration est dur la figure 6) :

Soit une propriété ou une formule P(n, m), dépendant de deux entiers n et m ∈ N.
On cherche à la démontrer par récurrence totale. Un schéma général, est le suivant :
Si on peut démontrer les deux propriétés suivantes :
Initialisation.
– P(i, 1) est vraie pour tout i
– P(1, j) est vraie pour tout j
Récurrence. P(i, j) est vraie pour tous les couples (i, j) tels que (i, j) ≺
(n, m)
implique
– P(n, m) est vraie
Alors on peut conclure :
Conclusion. P(n, m) est vraie pour tout les couples d’entiers naturels.

Une variante possible est de ne pas faire une récurrence totale, mais une récurrence simple à deux
indices, illustrée à la figure 7

Exemple

Démontrons par récurrence6 totale double la propriété suivante :


Pour tous les n et m entiers :
√ n+m
nm ≤
2
6 Il existe une démonstration directe très simple. Cet exemple, un peu artificiel, a cependant son petit mérite péda-

gogique.
8 CHAPITRE 0. DÉMONSTRATIONS, RÉCURRENCES.

(2, 4) (5, 4)
(7, 3)

(1, 1)

Fig. 5 – Une relation d’ordre dans le plan. Les couples d’entiers (1, 1) et (2, 4) sont « inférieurs » au
couple (5, 4). Le couple (7, 3) est « supérieur » au couple (1, 1) mais n’est en relation d’ordre ni avec
..
le couple (2, 4) ni avec le couple
. (5, 4).

(n, m)

...

Fig. 6 – Une illustration géométrique de la démonstration par récurrence double totale. La propriété
doit être vraie sur les zones infinies grises (initialisation). La récurrence est alors : si le fait qu’elle
soit vraie dans la zone finie hachurée (l’ensemble des couples d’entiers inférieurs au couple d’entiers
..
(n, m)) implique qu’elle est vraie
. pour le couple d’entiers (n, m), alors elle est vraie partout.

(n, m)

...

Fig. 7 – Une illustration géométrique de la démonstration par récurrence simple à deux indices.
0.2. EXERCICES 9

Nous utilisons le premier schéma de démonstration


√ illustré à la figure 3. L’initialisation consiste d’abord
à démontrer que pour tout i on a : i ≤ i+1 2 , ce que l’on constate en élevant les deux membres au
carré. On fait ensuite de même pour l’autre partie de l’initialisation.
Supposons maintenant cette propriété vraie pour 1 ≤ i ≤ n et 1 ≤ j ≤ m.
Il faut démontrer que cette hypothèse de récurrence implique que la propriété est vraie sur la zone
doublement hachurée.
Montrons-la d’abord pour le couple de valeurs m + 1 et n + 1. Il faut donc prouver que :
p (n + 1) + (m + 1)
(n + 1)(m + 1) ≤
2
Donc il faut prouver que :

((n + 1) + (m + 1))2 ((n + m) + 2)2


(n + 1)(m + 1) ≤ =
4 4
Donc que :

(n + m)2 + 4(n + m) + 4 (n + m)2


nm + (n + m + 1) ≤ = + (n + m + 1)
4 4
Donc que :
(n + m)2
nm ≤
4
ce qui est vrai par hypothèse de récurrence. p
Il faut maintenant faire la preuve que l’hypothèse de récurrence entraîne que (n + 1)(j) ≤
(n+1)+(j) (i+1)+(m)
p
2 pour 1 ≤ j ≤ m et que (i + 1)(m) ≤ 2 pour 1 ≤ i ≤ n ; ces résultats sont
obtenus par un calcul analogue au précédent.
Par conséquent, la propriété est vraie pour tous les couples d’entiers.

Commentaires

0.2 Exercices
Exercice 0.1 (♥ Démonstration par récurrence : 1.).
Démontrer par récurrence simple que :

∀n ∈ N, n! ≥ 2n−1

La solution est page ??

Exercice 0.2 (♥ Démonstration par récurrence : 2.).


Question 1. Démontrer par récurrence simple puis par récurrence totale, que toute somme de six
centimes ou plus peut être obtenue avec des pièces de deux et de sept centimes.
Question 2. Démontrer par récurrence simple puis par récurrence totale que toute somme de
douze centimes ou plus peut être obtenue avec des pièces de trois et de sept centimes. Pourquoi
seulement au-dessus de douze ?

La solution est page ??

Exercice 0.3 (♥ Fausse démonstration par récurrence : 1.).


On se propose de démontrer par récurrence la propriété P suivante :

Tout couple d’entiers naturels est constitué de deux entiers égaux.

La récurrence se fait sur le maximum des deux nombres a et b que nous allons noter max(a, b).
Initialisation. Si max(a, b) = 0, alors il est clair que a = b = 0.
10 CHAPITRE 0. DÉMONSTRATIONS, RÉCURRENCES.
Récurrence. Supposons la propriété P vraie quand le maximum de a et b est p. L’hypothèse de
récurrence est donc :
si max(a, b) = p alors a = b = p.
Montrons que P est vraie quand le maximum de a et b est (p + 1).
Prenons un couple (a, b) tel que max(a, b) = p + 1. Le maximum de (a − 1) et de (b − 1) est
donc p.
Par hypothèse de récurrence, nous avons : a − 1 = b − 1 = p. D’où : a − 1 + 1 = b − 1 + 1 = p + 1
et finalement a = b = p + 1.
Où est l’erreur ?
La solution est page ??
Exercice 0.4 (♥ Fausse démonstration par récurrence : 2.).

On se propose de démontrer par récurrence la propriété P suivante :

n points quelconques du plan sont toujours alignés.

La récurrence se fait simplement sur le nombre n.


Initialisation. Pour n = 2 P est vraie, puisque 2 points sont toujours alignés.
Récurrence. Supposons la propriété (P) vraie pour p points : p points sont alignés. Montrons qu’alors
p + 1 points sont alignés.
Prenons p + 1 points nommés A1 , A2 , A3 , ... Ap+1 . Par hypothèse de récurrence les p premiers
points A1 , A2 , A3 , ... Ap sont alignés sur une droite que nous appellerons (d1 ). Et toujours avec
cette hypothèse de récurrence les p derniers points A2 A3 ... Ap+1 sont alignés sur une droite
que nous nommerons (d2 ).
Mais les deux droites (d1 ) et (d2 ) ont en commun les deux points A2 et A3 . Ces deux droites
sont donc forcément confondues. On a (d1 ) = (d2 ) = (A2 A3 ).
Les p + 1 points sont donc alignés.
Trouvez la faille.

La solution est page ??

Exercice 0.5 (♥ Fausse démonstration par récurrence totale).


On se propose de démontrer que, pour n entier supérieur ou égal à 1 :
s r q p
n = 1 + (n − 1) 1 + n 1 + (n + 1) 1 + (n + 2) . . .

On admettra pour commencer que cette expression a un sens, c’est à dire qu’elle converge quand
n augmente indéfiniment (ce qui est vrai).
La démonstration par récurrence se fait alors ainsi :
p p
Initialisation. Pour n = 1, la formule devient n = 1 + (n − 1)(. . .) = 1 + 0(. . .) = 1 ; elle est
donc vraie.
Récurrence. Supposons-là vraie pour tout j, 1 ≤ j ≤ n et démontrons-là pour n + 1.
Il faut donc démontrer :
s r q p
n + 1 = 1 + n 1 + (n + 1) 1 + (n + 2) 1 + (n + 3) . . .

En élevant au carré la formule pour n, on obtient :


r q
2
p
n = 1 + (n − 1) 1 + n 1 + (n + 1) 1 + (n + 2) . . .
0.2. EXERCICES 11

D’où : r
n2 − 1
q p
=n+1= 1+n 1 + (n + 1) 1 + (n + 2) . . .
n−1
C’est bien ce que nous voulions démontrer.
Question 1 : Où est l’erreur de raisonnement ?
Question 2 : Cette formule est pourtant vraie. Comment pourrait-on la démontrer ?

La solution est page ??


12 CHAPITRE 0. DÉMONSTRATIONS, RÉCURRENCES.
Chapitre 1

Complexité d’un algorithme

1.1 Rappels
1.1.1 Ordres de grandeur de complexité : rappel des définitions.
f : N → R+
O(f) = {t : N → R+ tel que : ∃C ∈ R+ , C 6= 0, ∃n0 ∈ N : ∀n ≥ n0 , t(n) ≤ Cf(n)}
Ω(f) = {t : N → R+ tel que : ∃C ∈ R+ , C =6 0, ∃n0 ∈ N : ∀n ≥ n0 , t(n) ≥ Cf(n)}
t ∈ O(f) et t ∈ Ω(f) ⇔ t ∈ Θ(f)

1.1.2 Ordres de grandeur de complexité : remarques.


1. Un ordre de grandeur de complexité n’est pas une limite. Par exemple, n2 (2 + sin(n)) ∈ Θ(n2 )
mais n’a pas de limite quand n croît vers l’infini. De plus, les fonctions traitées pas les ordres de
complexité ne sont pas de R → R mais de N → R+ .
2. Rappelons quelques définitions classiques1 pour des fonctions f et g : R → R.
f(x)
(a) On dit que f est négligeable devant g au voisinage de a quand : lim g(x) = 0. On note :
x→ a
f ∈ oa (g). Par convention, o+∞ est noté simplement par o.
f(x)
(b) On dit que g et f sont équivalentes en a si g(x) → 1 quand x → a. On note : f ∼a g. Par
convention, ∼+∞ est noté simplement par ∼.
(c) On dit que g domine f au voisinage de a s’il existe une constante M > 0 et un voisinage de a
dans lequel |f(x)| ≤ M|g(x)|. On note : f ∈ Oa (g). Par convention, O+∞ est noté simplement par O.
Cette nouvelle notation O est cohérente avec celle définie plus haut, qui en est une restriction aux
fonctions de N dans R+ et qui prend toujours a infini.
La définition de Oa est plus faible que celle de oa , car f ∈ oa (g) ⇒ f ∈ Oa (g).
3. On écrit souvent par abus de langage : f = o(g) au lieu de f ∈ o(g), f = O(g) au lieu de f ∈ O(g)
et f = Θ(g) au lieu de f ∈ Θ(g). Ceci permet par exemple d’écrire la formule de Taylor-Young sous la
forme :
Xn k
(x − a) (k)
f(x) = f (a) + o ((x − a)n ) .
k!
k=0

Cet abus de langage n’est en général pas nécessaire aux informaticiens.

1.1.3 Temps de calcul pratique.


Chaque case du tableau suivant indique le nombre approximatif de données que peut traiter un
algorithme dont la complexité exacte est en colonne, dans le temps donné en ligne, pour un temps
élémentaire d’instruction de 1ms. Par exemple, en 1 heure, un algorithme en n3 peut traiter un
problème de taille 1500.
1 Voir le polycopié ENSSAT Analyse : support de cours (Chapitre 2). de P. Struillou et N. Barbot.

13
14 CHAPITRE 1. COMPLEXITÉ D’UN ALGORITHME
log10 (n) n n2 n3 2n n!
6
1 seconde 1010 106 103 102 19 10
7
1 minute 106.10 6.107 8.103 4.102 25 11
8
1 heure 104.10 4.108 2.104 1500 31 13
10
1 jour 109.10 9.1010 105 4400 36 16

1.2 Exercices
Exercice 1.1 (♥ Ordre de grandeur : 1.).
Dire si les affirmations suivantes sont vraies, pour toute fonction f : N → R∗ :
Question 1 : 2n+1 ∈ O(2n )
Question 2 : (n + 1)! ∈ O(n!)
Question 3 : f(n) ∈ O(n) ⇒ [f(n)]2 ∈ O(n2 )
Question 4 : f(n) ∈ O(n) ⇒ 2f(n) ∈ O(2n )
Question 5 : nn ∈ O(2n )

La solution est page ??

Exercice 1.2 (♥ Ordre de grandeur : 2.).


Montrer que :
Si O(f(n)) = O(g(n)) ⇔ [f(n) ∈ O(g(n)) et g(n) ∈ O(f(n))]
Alors O(f(n) + g(n)) = O (max(f(n), g(n)))

La solution est page ??

Exercice 1.3 (♥ Ordre de grandeur : polynômes.).


Prouver en utilisant le résultat précédent que n3 − 3n2 + 6n − 8 ∈ O(n3 )

La solution est page ??

Exercice 1.4 (♥ Ordre de grandeur : polynômes (version complète).).


Prouver que, pour ap > 0, f(n) = ap np + ap−1 np−1 + . . . + a1 n + a0 ∈ Θ(np )

La solution est page ??

Exercice 1.5 (♥ Ordre de grandeur : un autre.).


Prouver que pour k ∈ N, on a 1k + 2k + . . . + nk + ∈ Θ(nk+1 )

La solution est page ??

Exercice 1.6 (♥ Ordre de grandeur : paradoxe ?). Trouver l’erreur dans le raisonnement
suivant :
n
n(n + 1) X
= i
2
i=1

Or
n
X
i ∈ O(1 + 2 + . . . + n)
i=1

et
O(1 + 2 + . . . + n) = O(max(1, 2, . . . , n)) = O(n)
Donc
n(n + 1)
∈ O(n)
2
1.2. EXERCICES 15

La solution est page ??

Exercice 1.7 (♥ Complexité du calcul récursif de la suite de Fibonacci.).

On note F(n) le terme courant de la suite de Fibonacci définie pour n strictement positif.
Par définition :
F(1) = 1
F(2) = 1
F(n) = F(n − 1) + F(n − 2) pour n ≥ 2
On peut aussi montrer que
" √ !n √ !n #
1 1+ 5 1− 5
F(n) = √ −
5 2 2

Question 1 : Donner une raison pour laquelle on n’utilise pas la formule ci-dessus pour calculer
F(n)
Question 2 : Donner une procédure récursive Fibo(n) calquée sur la définition récurrente pour
calculer F(n).
Question 3 : Donner l’arbre des appels récursifs pour n = 6
Question 4 : Calculer le nombre d’appels récursifs engendrés par l’appel Fibo(n).

La solution est page ??

Exercice 1.8 (♥ Calculs exacts de complexité pour trouver les éléments minimal et
maximal.).

Un algorithme naïf
L’algorithme suivant permet de trouver la valeur de l’élément maximal et l’élément minimal
dans un tableau d’entiers S[1..n].
précondition : n ≥ 1
1: Procédure Maxmin1(S, n : in ; max, min : out)
2: début
3: min ← S[1] ;
4: max ← S[1] ;
5: i ← 1 ;
6: tant que i < n faire
7: i ← i+1 ;
8: si S[i] > max alors
9: max ← S[i]
10: fin si
11: si S[i] < min alors
12: min ← S[i]
13: fin si
14: fin tant que
15: fin

Question 1 : Quelle est sa complexité exacte en nombre de comparaisons ?


Un algorithme moins naïf
Dans l’algorithme précédent certaines comparaisons peuvent être évitées en remarquant que S[i]
ne peut pas être supérieur à max s’il est inférieur à min.
Question 2 : Ecrire cet algorithme.
Question 3 : Quelle est sa complexité minimale et maximale en nombre de comparaisons ? Dé-
crire les situations où ces extréma sont atteints.
16 CHAPITRE 1. COMPLEXITÉ D’UN ALGORITHME
Question 4 : Que peut-on dire sur sa complexité moyenne ?
Un algorithme optimal On peut ruser un peu plus en prenant les éléments par paires : on compare
les éléments d’une paire entre eux, puis le plus petit au minimum courant et le plus grand au
maximum courant.
Question 5 : Ecrire cet algorithme. Distinguer pour l’initialisation le cas où n est pair ou impair.
Question 6 : Donner le nombre de comparaisons exact.
Question 7 : Pourquoi ne pas prendre les éléments par triplets ?
Un algorithme diviser pour régner
L’algorithme suivant traite le même problème par la méthode « diviser pour régner ».
1: Procédure Maxmin2(S, p, q : in ; fmin, fmax : out)
2: /* Cette procédure renvoie le minimum et le maximum de la partie S[p..q] du tableau S */
3: début
4: si q=p alors
5: fmax ← S[p] ; fmin ← S[p]
6: sinon
7: m= ⌊ p+q
2 ⌋;
8: Maxmin2(T, p, m, gmin, gmax) ;
9: Maxmin2(T, m+1, q, hmin, hmax) ;
10: fmax ← maximum(gmax, hmax) ;
11: fmin ← minimum(gmin, hmin)
12: fin si
13: fin
Question 8 : Montrer par récurrence que cet algorithme est valide.
Question 9 : Quelle est sa complexité exacte en nombre de comparaisons quand n est une
puissance de 2 ? (N.B. : Ne pas oublier les comparaisons faites par les fonctions maximum et
minimum). Conclusion sur l’utilité de Diviser pour Régner danss ce problème ?
Question 10 : Donner son déroulement sur le tableau T =(22, 13, -5, -8, 15, 60, 17, 31, 47).

La solution est page ??

Exercice 1.9 (♥ La plus longue coupe équilibrée.).

Il s’agit de trouver le plus grand sous-tableau d’un tableau binaire B de taille n qui soit équilibré
en 0 et en 1. L’algorithme naïf est est en O(n3 ), un algorithme plus rusé en O(n2 ) et l’algorithme
optimal est en O(n). Ce dernier n’est pas facile à trouver...

La solution est page ??

Exercice 1.10 (♥ Division du périmètre d’un polygone.).


Il s’agit de trouver la paire de sommets d’un polygone qui le divise son périmètre le plus exactement
possible. L’algorithme naïf est est en O(n3 ), un algorithme plus rusé en O(n2 ) et l’algorithme optimal
est en O(n). Il n’est pas facile à trouver...

La solution est page ??

Exercice 1.11 (♥ Un calcul de complexité en moyenne.).


L’algorithme ci-dessous prend en donnée un tableau T [1, n] d’entiers tous différents et un entier x. Si
x est dans T , il donne en résultat l’indice où x se trouve. Sinon, il produit la valeur n + 1.
1: Procédure Recherche(x : in)
2: début
3: T [n + 1] ← x ; i ← 1
4: tant que T [i] 6= x faire
1.2. EXERCICES 17

5: i←i+1
6: fin tant que
7: résultat(i)
8: fin
Quelle est la complexité exacte minimale, maximale et moyenne de cet algorithme, en nombre de
comparaisons ?
Pour la complexité en moyenne, il faut faire une hypothèse probabiliste. On supposera que les
entiers du tableau sont tirés équiprobablement sans remise entre 1 et N, avec N ≥ n, et que x est tiré
équiprobablement entre 1 et N.

La solution est page ??

Exercice 1.12 (♥ Variation sur les ordres de grandeurs).

Question 1 : Trouver un contre-exemple à l’affirmation suivante :


Si f ∈ Θ(s) et g ∈ Θ(s) alors f − g ∈ Θ(s).
Question 2 : Trouver un contre-exemple à l’affirmation suivante :
Si f ∈ O(s) et g ∈ O(r) alors f − g ∈ O(s − r).

La solution est page ??

Exercice 1.13 (♥ Autre variation).

Question 1 : Trouver deux fonctions f et g, monotones croissantes, telles que f 6∈ O(g) et g 6∈ O(f).
Indication : faire croître rapidement f sur les nombres pairs, lentement sur les nombres impairs, et
vice versa.

La solution est page ??


18 CHAPITRE 1. COMPLEXITÉ D’UN ALGORITHME
Chapitre 2

Invariants, itération.

2.1 Rappels sur la logique de l’itération.


2.2 Exercices.

19
20 CHAPITRE 2. INVARIANTS, ITÉRATION.
Chapitre 3

Diviser et Diminuer pour Régner

3.1 Rappels
3.1.1 Formule de récurrence 1 (Diviser n en en b parties) :
T (n) = a.T ( n
b ) + f(n)
T (1) = d ∈ Θ[1]

Solution générale pour n puissance entière de b :

j=logb (n)−1
X n
T (n) = Θ[nlogb (a) ] + aj .f( )
bj
j=0

Quelques solutions particulières pour n quelconque :


Cas 1 : f(n) ∈ Θ[1]
a = 1 =⇒ T (n) ∈ Θ[Log(n)]

a > 1 =⇒ T (n) ∈ Θ[nlogb (a) ]


Cas 2 : f(n) ∈ Θ[Log(n)]
a=b =⇒ T (n) ∈ Θ[n]
Cas 3 : f(n) ∈ Θ(n)
a < b =⇒ T (n) ∈ Θ[n]

a = b =⇒ T (n) ∈ Θ[n.Log(n)]

a > b =⇒ T (n) ∈ Θ[nlogb (a) ]

Cas 4 : f(n) ∈ Θ[nα .(Log(n))δ ]


a < bα =⇒ T (n) ∈ Θ[nα .(Log(n))δ ]

a = bα =⇒ T (n) ∈ Θ[nα .(Log(n))δ+1 ]

a > bα =⇒ T (n) ∈ Θ[nlogb (a) ]

21
22 CHAPITRE 3. DIVISER ET DIMINUER POUR RÉGNER
3.1.2 Formule de récurrence 2 (Diminuer n à n − 1) :
T (n) = a.T (n − 1) + c.nk
T (1) = d ∈ Θ[1]

Solution générale :

n−2
X
T (n) = d.an−1 + ai .(n − i)k
i=0

Quelques solutions particulières :


k = 0 a = 1 =⇒ T (n) ∈ Θ[n]

k = 0 a > 1 =⇒ T (n) ∈ Θ[an ]

k = 1 a = 1 =⇒ T (n) ∈ Θ[n2 ]
3.2. EXERCICES 23

3.2 Exercices
Exercice 3.1 (♥ Recherche dichotomique dans un tableau trié.).
On considère un tableau d’entiers trié par ordre croissant, de taille n. On cherche à savoir si l’entier
m s’y trouve.
Question 1 : Ecrire un programme de recherche dichotomique qui compare m avec l’élément
milieu du tableau (ce terme est à définir précisément), choisit un des demi-tableaux et recommence
récursivement. Calculer son nombre exact de comparaisons pour n puissance de deux.
Question 2 : Ecrire un programme sur le même principe qui divise le tableau en trois. Calculer
son nombre maximal de comparaisons pour n puissance de trois. Conclusion ?
Question 3 : (Répondre informellement.) Peut-on améliorer la recherche dichotomique en faisant
des hypothèses statistiques sur les éléments du tableau. Indication : comment cherchez-vous un mot
dans un dictionnaire ?

Anonyme

Exercice 3.2 (♥ Suite récurrente.). On considère la suite récurrente à deux indices définie par :
a0,j = j + 1 pour j ≥ 0,
ai,0 = 2ai−1,0 + ai−1,1 pour i > 0,
ai,j = ai−1,j−1 + 2ai−1,j + ai−1,j+1 autrement.
Question 1 : Donner les valeurs de ai,j pour 0 ≤ i ≤ 3 et 0 ≤ j ≤ 3.
Question 2 : Donner un programme de calcul de cette suite.

Exercice 3.3 (♥ Le tracé de triangles.).


On veut tracer la figure suivante :

Chaque petit triangle est isocèle (45, 45, et 90 degrés), angle droit en bas, de hauteur d ; sa base
(horizontale) a donc pour longueur 2d. La figure ci-dessus représente 6 « couches »de tels triangles.
La couche du bas comporte 1 triangle, celle de dessus 2, ainsi de suite jusqu’à 6. Comme on le voit, un
assemblage de n couches forme un grand triangle de même forme, mais n fois plus grand. Une telle
figure est dite de taille n.
Pour un triangle élémentaire, si le sommet du bas a pour coordonnées (x, y), les autres ont pour
coordonnées (x − d , y + d) et (x + d , y + d).
On dispose d’un traceur capable de répondre aux commandes suivantes : placer(x , y), qui place la
« plume »au dessus du point de coordonnées (x , y) et tracer(x , y) qui trace une ligne droite depuis
l’endroit où la plume est placée jusqu’au point de coordonnées (x , y), où la plume se trouve alors
placée. On veut que le traceur démarre sur la pointe du bas, à laquelle on donnera les coordonnées
(0 , 0) et termine au même endroit.
24 CHAPITRE 3. DIVISER ET DIMINUER POUR RÉGNER
Préliminaire
Question 1 : Quel est le nombre minimum de triangles de taille 1 qu’il faut tracer pour tracer un
triangle de taille n ?

Une méthode longue


On remarque que le tracé de la figure de taille n se ramène à tracer le côté gauche du triangle du
bas, puis une figure de taille n − 1, puis le côté horizontal du triangle du bas, puis une seconde figure
de taille n − 1, puis le côté droit du triangle du bas.
Question 2 : Ecrire le programme correspondant.
Question 3 : Quel est le nombre de triangles de taille 1 qui sont effectivement tracés ?
Question 4 : Exécuter le programme à la main sur une figure de taille 5.

Une méthode optimale


On remarque que le tracé de la figure de taille n se ramène à tracer le côté gauche du triangle du
bas, puis une figure de taille n − 1, puis le côté horizontal du triangle du bas, puis un « bandeau »de
taille n − 1, puis le côté droit du triangle du bas.
Un bandeau de taille 4 est la figure suivante :

Question 5 : Ecrire le programme correspondant. Vérifier sur une figure de taille 6 qu’il éxécute
un tracé minimum.
Question 6 : Calculer sa complexité exacte et démontrer qu’il exécute un tracé de longueur mi-
nimum.

Exercice 3.4 (♥ La bâtière.).

On considère un tableau à deux dimensions de valeurs entières positives telles que les valeurs d’une
même ligne et celles d’une même colonne sont ordonnées, non strictement. Un tel tableau est appelé
bâtière.
Exemple : le tableau ci-dessous est une bâtière à 4 lignes et 5 colonnes.
2 14 25 30 69
3 15 28 30 81
7 15 32 43 100
20 28 36 58 101

Diviser pour régner (1, n − 1)


On étudie la recherche d’une valeur v fixée dans une bâtière B. Pour simplifier, on supposera que
v figure au moins une fois dans B.
Une première solution consiste en un balayage séquentiel de B par ligne ou par colonne jusqu’à
trouver v.
3.2. EXERCICES 25

Question 1 : Décrire le principe de cette première stratégie en terme de méthode « diviser pour
règner »
Question 2 : Quelle en est la classe de complexité au pire (en terme de nombre de comparaisons)
si m est le nombre de lignes et n le nombre de colonnes de B.

Diviser pour régner (n/2, n/2)

On envisage maintenant une solution dans le cas particulier où la bâtière est un carré de côté
n = 2k .
On distingue les deux valeurs :
x = B[ n n n n
2 , 2 ], y = B[ 2 + 1, 2 + 1]
Question 3 : Montrer que si la valeur recherchée v est telle que : v > x, on peut éliminer une
partie (à préciser) de la bâtière pour poursuivre la recherche. Préciser ce qu’il est possible de faire
quand v < y.
Question 4 : En déduire un modèle de résolution de type « diviser pour régner »en réduction
logarithmique : Pb(n) = aPb(n/b) + f(n).
Question 5 : Etablir l’ordre de complexité au pire de cette méthode (en terme de nombre de
comparaisons).
Question 6 : Comment adapter cette stratégie au cas d’une bâtière quelconque ?

De plus en plus fort

On considère maintenant la recherche d’une valeur v dans une bâtière B de dimension quelconque
(m lignes, n colonnes) en distinguant la valeur z = B[1, n].
Question 7 : Comment peut-on poursuivre la recherche selon que (v < z) ou (v > z) ?
Question 8 : En déduire un modèle de résolution de type « diminuer pour régner »de la forme :
Pb(m, n) = Pb(m′ , n′ ) + test(z)
en précisant les valeurs de m′ et n′ .
Question 9 : Ecrire en pseudo-code la procédure récursive correspondante, qui a pour en-tête :
procedure bâtière(ldeb, cfin)
et est appelée du programme principal par :
bâtière(1, n)
Elle doit rendre dans un doublet de type place une localisation de la valeur recherchée (v) dans la
bâtière B[1 : m, 1 : n].
Question 10 : Etablir la classe de complexité au pire de cette dernière méthode (en terme de
nombre de comparaisons) et conclure sur la démarche adoptée pour résoudre cet exercice.

La solution est page ??.

Exercice 3.5 (♥ Double tri.).


Soit un tableau T [1 : n , 1 : n] dont les éléments sont des entiers relatifs. Ce tableau possède la
propriété de double tri, c’est à dire que toutes les colonnes sont composées d’éléments strictement
croissants, et toutes les lignes d’éléments strictement croissants.
Autrement dit, pour 1 ≤ i < n et 1 ≤ j < n, on a : T [i, j] < T [i + 1, j] et T [i, j] < T [i, j + 1]
Le problème est de compter le nombre de zéros dans T .
Question 1 : On suppose que n = 2k , avec k entier. Donner un algorithme Diviser pour Régner
qui coupe T en quatre. Quelle est sa complexité ?
Question 2 : Donner un algorithme de complexité inférieure.
Question 3 : Que se passe-t’il dans le cas où les lignes et les colonnes ne sont pas forcément
strictement croissantes ?

La solution est page ??


26 CHAPITRE 3. DIVISER ET DIMINUER POUR RÉGNER
Exercice 3.6 (♥ Multiplication de deux nombres complexes.).

Question 1 : Montrer comment multiplier les deux nombres complexes a+bi et c+di en n’utilisant
que trois multiplications réelles (les additions et les soustractions ne comptent pas). L’algorithme a
pour entrées a, b, c et d et pour sorties la composante réelle ac − bd et la composante imaginaire
ad + bc.

Ce n’est pas vraiment du DpR, mais ça aide pour les suivants.

Exercice 3.7 (♥ Multiplication de grands nombres.).

On s’intéresse à des nombres écrits en base b ; on peut donc les décomposer de manière unique
sous la forme : N = n0 + n1 b1 + ... + nq bq , où les ni sont des chiffres compris au sens large entre 0 et
b−1 (avec la restriction : nq n’est pas nul). On mesure la complexité des algorithmes selon l’opération
élémentaire de la multiplication de deux chiffres.
Question 1 : Quelle est la complexité de la multiplication de deux nombres N et M quand on la
fait « à la main » ? Vérifier sur un exemple simple (M et N à quatre chiffres).
Question 2 : On suppose que N et M ont le même nombre de chiffres et que ce nombre est une
puissance de deux . Décrire l’algorithme élémentaire « diviser pour régner »de multiplication de N et
M. Quelle est sa complexité ? Reprendre le même exemple.
Question 3 : En partant de cet algorithme, trouver une variante qui réduise la complexité en
utilisant l’identité :
ad + bc = (a + b)(c + d) − (ac) − (bd)
Reprendre le même exemple.
Question 4 : Que faire quand N etM ont un nombre différent et quelconque de chiffres ? L’effica-
cité de la variante précédente est-elle conservée ?
Question 5 : Pourquoi ce exercice s’appelle-t’il : « multiplication de grands nombres » ?

Exercice 3.8 (♥ Multiplication de polynômes.).


On cherche à multiplier deux polynômes A(x) = an xn + an−1 xn−1 + . . . + a1 x + a0 et B(x) =
bm xm + bm−1 xm−1 + . . . + b1 x + b0 . L’opération élémentaire pour mesurer la complexité est la
multiplication des coefficients comme ai par bj . La multiplication de xi par xj ne compte pas.
Question 1 : On suppose que n = m = 2k . Décrire l’algorithme élémentaire « diviser pour ré-
gner »de multiplication de A et B. Quelle est sa complexité ?
Question 2 : En partant de cet algorithme, trouver une variante qui réduise la complexité en
utilisant l’identité :
ad + bc = (a + b)(c + d) − (ac) − (bd)
Question 3 : Que faire quand n etm sont quelconques ? L’efficacité de la variante précédente est-
elle conservée ?

Semblable à l’exercice 3.7, multiplication de grands nombres.

Exercice 3.9 (♥ Suite de Fibonacci.).


On note F(n) le terme courant de la suite de Fibonacci.
Question 1 : Montrer que le calcul de la suite de Fibonacci
 se ramène
 à celui de la résolution
F(n)
d’une équation matricielle récurrente. On notera V(n) = un vecteur colonne à deux
F(n − 1)
éléments et F une matrice carrée (2 × 2). On cherche F telle que :
V(2) = constante
V(n) = F × V(n − 1)
Donner la matrice F.
3.2. EXERCICES 27

Question 2 : Montrer que la solution de cette équation de récurrence s’écrit V(n) = Fn−2 × V(2).
Question 3 : En déduire un algorithme de type « diviser pour régner »calculant F(n) pour tout
n, de complexité log2 (n) en terme de nombre de multiplication de matrices (2 × 2).
Question 4 : Donner un minorant et un majorant (en explicitant les cas où ils sont atteints) de
la complexité exacte de cette procédure en terme de nombres de multiplications de matrices (2 × 2).
Question 5 : Montrer par un cas particulier que cet algorithme d’élévation à la puissance n n’est
pas optimal. Prendre par exemple : n = 15.

Exercice 3.10 (♥ Suite de Fibonacci, autre technique.).

Soit la suite de Fibonacci :

F0 = F1 = 1 et Fn = Fn−1 + Fn−2 pour n ≥ 2


Question 1 : Montrer par récurrence sur p que :

∀ n , p ≥ 1 : Fn+p = Fn Fp + Fn−1 Fp−1

Question 2 : Appliquer pour p = n, p = n − 1 et p = n + 1 pour en déduire F2n , F2n−1 et F2n+1


en fonction de Fn et de Fn−1 .  
Fn−1
Question 3 : En notant Tn le couple de valeurs , en déduire que T2n et T2n+1 se
Fn
calculent en fonction des éléments de Tn , donc de Tn .
Question 4 : En déduire un algorithme diviser pour régner pour calculer Tn , donc Fn pour tout
n et donner son pseudo-code. Calculer son ordre de grandeur de complexité.

Cette technique est donnée dans [Que00], pages 10 et 68.

Exercice 3.11 (♥ Les deux points les plus proches.).

On cherche un algorithme du type « diviser pour régner »(DpR) pour résoudre le problème suivant :
on a n points dans un plan. Quels sont les deux plus proches et à quelle distance sont-ils ?
Plus formellement : soit l’espace R2 , muni de la métrique euclidienne notée ∆ . Soit S un en-
semble fini de n points dans R2 (on note son cardinal |S| = n). On cherche la valeur D et les
deux éléments y et z de S tels que : D = ∆(y, z) = Minimum∆(x1 , x2 ), pour x1 ∈ S, x2 ∈ S
et x1 6= x2 . En notant abs(x) l’abscisse d’un point x et ord(x) son ordonnée, on a : ∆(u, v) =
1
(abs(u) − abs(v))2 + (ord(u) − ord(v))2 2
Question 1 : Quelle est l’ordre de complexité (opération élémentaire : le nombre de calculs de la
distance ∆ ) de l’algorithme naïf de calcul de D, y et z ?
On trie les n points par ordonnée croissante.
Les données sont rangées dans un tableau T [1 : 2, 1 : n], où T [1, i] est l’abscisse du point numéro i
et T [2, i] son ordonnée, avec T [2, i] ≤ T [2, i + 1].
Soit un réel m et S1 = {x ∈ S|abs(x) ≤ m} et S2 = {x ∈ S|abs(x) > m}.
Le shéma du programme DpR est alors le suivant :
1: Procedure PlusProches(S, x, y, D)
2: début
3: si |S| = 2 alors
4: Soit u et v les deux points de S.
5: x ← u ; y ← v ; D ← ∆(u, v) ;
6: sinon
7: Choisir m ;
8: PlusProches(S1 , p1 , p2 , D1 ) ;
28 CHAPITRE 3. DIVISER ET DIMINUER POUR RÉGNER
9: PlusProches(S2 , q1 , q2 , D2 ) ;
10: d ← minimum(D1 , D2 ) ;
11: si d = D1 alors
12: w ← p1 ; z ← p2
13: sinon
14: w ← q1 ; z ← q2
15: fin si
16: Rassembler
17: fin si
18: fin
Question 2 : Le calcul de D2 étant toujours l’opération élémentaire, quel doit être l’ordre de
complexité de Rassembler pour que le programme soit meilleur que le programme naïf, en supposant
que S11 et S2 sont de même taille ? Quel est son ordre de complexité si Rassembler est en O(n) ? En
O(1) ?
Question 3 : Pourquoi Rassembler ne peut-il pas s’écrire simplement :
D ← d; x ← w; y ← z
Donner un exemple.
Soit P1 la partie du plan située entre la droite d’équation x = m et celle d’équation x = m − d ; soit
P2 celle située entre la droite d’équation x = m et celle d’équation x = m + d .
Question 4 : Montrer que si deux points r1 ∈ S1 et r2 ∈ S2 sont tels que : ∆(r1 , r2 ) ≤ d, alors
r1 ∈ P1 et r2 ∈ P2 .
Question 5 : Soit r1 un point de P1 . Montrer que la zone du plan où peut se trouver un point
r2 ∈ P2 tel que ∆(r1 , r2 ) ≤ d est incluse dans le rectangle de la figure suivante :

r1 • 2d

d
Question 6 : Montrer que ce rectangle contient au plus 6 points de S.
Question 7 : Donner le schéma d’une procédure Rassembler utilisant le tableau T et les valeurs
m et d. Montrer que son ordre de complexité est en O(n).
NB : Il est recommandé de créer deux tableaux triés par ordonnées, l’un contenant les points de P1 ,
l’autre ceux de P2 .
Question 8 : Donner un schéma pour la procédure Choisir qui ne change pas l’ordre de complexité
obtenu.
Question 9 : Une telle procédure DpR est-elle réalisable en dimension 3 ? Pourquoi ?

Exercice 3.12 (♥ Pavage d’un carré par des triminos.).

Pavage particulier d’un carré


On considère un échiquier carré de côté n = 2m avec m > 0 et les quatre motifs ci-après composés
d’un trimino (un carré 2 × 2 auquel une case a été enlevée) :
3.2. EXERCICES 29

On se pose le problème de recouvrir intégralement l’échiquier, à l’exception d’une case spéciale


donnée de coordonnées (x, y), à l’aide des motifs précédents, de sorte que chaque case soit recouverte
par un seul motif.

Exemple. On a un échiquier de côté 8 = 23 et la case spéciale est en (2, 6). Les motifs ont été
distingués uniquement par souci d’identification.

Le but de l’exercice est de concevoir une solution de type « diviser pour régner »au problème posé.

On va tout d’abord valider l’hypothèse de récurrence suivante : on sait recouvrir tout échiquier de
côté n = 2m dans lequel une case spéciale a été retirée.

Question 1 : Vérifier que cette hypothèse est valide pour m = 1.

On suppose l’hypothèse vérifiée au rang m et on demande de décrire une procédure de recouvrement


de l’échiquier de côté 2m+1 . On peut placer arbitrairement dans l’un des sous-échiquiers de côté 2m
et on fera apparaître un (des) motif(s) de recouvrement.
30 CHAPITRE 3. DIVISER ET DIMINUER POUR RÉGNER

Question 2 : On suppose l’hypothèse vérifiée au rang m et on demande de décrire une procédure


de recouvrement de l’échiquier de côté 2m+1 . On pourra positionner la case spéciale arbitrairement
dans l’un des quatre sous-échiquiers de côté 2m et on fera apparaître un (des) motif(s) de recouvrement.
Question 3 : Exprimer la division du problème initial Recouvre(2m ) en faisant apparaître des
problèmes de même nature et la pose de motif(s). On précisera les problèmes élémentaires
Question 4 : Établir que la complexité temporelle exacte de la procédure associée est égale à
(n2 − 1)/3 si on prend la pose de motif comme opération élémentaire.
Question 5 : Donner une condition nécessaire (relative à n, le côté de l’échiquier) pour que le
problème posé ait une solution (indépendamment de la façon de le résoudre).
Question 6 : Quand cette condition est satisfaite, peut-on appliquer la méthode de type « diviser
pour régner »proposée auparavant ?

Anonyme

Exercice 3.13 (♥ Distance entre séquences : algorithme de Hirshberg).


Voir [KLE06], pages 284 et suivantes. Bien que cet exercice soit situé dans le texte avant celui intitulé
Distance entre séquences : algorithme de Wagner et Fisher. (exercice 99.1, page 107), on ne peut
pas le résoudre avant lui. Il est donc nécessaire de traiter le second d’abord. Toutes les notations sont
identiques.
Cet algorithme calcule le coût de la trace optimale entre deux séquences, comme celui de Wagner
et Fisher. Il peut également reconstituer la (ou les) trace(s) de coût minimal. La différence est qu’il
est construit sur une technique Diviser pour Régner, tout en utilisant le principe de Programmation
Dynamique. L’avantage proclamé de cette méthode est le gain de place. Hirshberg l’ayant imaginé
dans les années 70, cet argument avait plus de force que de nos jours (encore que si l’on traite des
séquences biologiques de milliers de lettres, il a encore son utilité de ce point de vue). Mais c’est surtout
un exemple extraordinaire, à notre connaissance unique, de combinaison entre ces deux techniques si
différentes.
La question 8 de l’exercice 99.1 est la clé de la construction de cet algorithme. Elle demande de
montrer (ce que l’on admettra ici) que la trace optimale entre les phrases miroir u et v se déduit
directement de celle entre u et v, si elle est unique.
On ne va pas donner ici la réponse à cette question, mais explorer cette idée un peu différemment
avant de construire l’algorithme Diviser pour Régner.
Soit les phrases u et v, de longueur respective n et m. Nous supposerons la trace optimale unique,
pour simplifier. Cette contrainte se relâche sans difficulté.
3.2. EXERCICES 31

La trace optimale entre u et v correspond au chemin optimal dans le graphe défini dans l’exercice
99.1 entre les nœuds (0/0) et (n/m). Celle entre u et v correspond au chemin optimal entre les nœuds
(n/m) et (0/0), c’est à dire le même, mais emprunté en sens inverse (appelons-le chemin optimal
inverse). Notons que l’opération d’inversion d’un chemin est involutive, c’est à dire qu’inverser deux
fois un chemin le transforme tout simplement en lui-même. Nous allons généraliser cette propriété
dans la première question.
Question 1 : Nous nous intéressons ici aux chemins du graphe qui vont du nœud (0/0) au nœud
(n/m) en passant par un certain nœud (i/j).
Montrer que le chemin optimal (supposé unique) entre les nœuds (0/0) et (n/m), sous la contrainte
de passer par (i/j) peut se décomposer en deux parties :
– Le chemin optimal entre (0/0) et (i/j).
– L’inverse du chemin optimal inverse entre (n/m) et (i/j).
Question 2 : Notons L(i/j) la longueur du chemin optimal entre (0/0) et (i/j) et L(i/j) la longueur
du chemin optimal inverse entre (n/m) et (i/j). Montrer que tout j, on a :

L(n/m) = Min L(i/j) + L(i/j)
i

Question 3 : En assignant à j la valeur ⌊ m 2 ⌋, montrer que la formule précédente permet de trouver


un nœud du chemin optimal qui divise v en deux moitiés. On supposera qu’il existe deux procédures
WFAvant(x, y) et WFArriere(x, y) qui calculent la distance d’édition par l’algorithme de Wagner et
Fisher, l’une entre x et y, l’autre entre x et y.
Question 4 : En déduire l’algorithme Diviser pour Régner de Hirshberg qui utilise ces procédures.
Question 5 : Donner sa formule de récurrence de complexité (l’opération élémentaire est la com-
paraison).
Question 6 : Résoudre la formule de récurrence dans le cas où m = n. Que pensez-vous du cas
général ?
Question 7 : On prend l’exemple : u = namely et v = meanly, pour la distance sur l’alphabet
définie de la façon suivante :
– pour toute lettre α : δ[α, ǫ] = δ[ǫ, α] = 2 ;
– si α et β sont deux consonnes ou sont deux voyelles : δ[α, β] = δ[β, α2] = 1 ;
– si α est une consonne et β une voyelle : δ[α, β] = δ[β, α] = 3 ;
Calculer la première valeur de q que produit l’algorithme de Hirshberg.
En quoi cet algorithme permet-il d’économiser de la place ? Il faut se reporter à la question 5 de
l’exercice 99.1, qui donne la version « linéaire »WFL de l’algorithme de Wagner et Fisher (dont la
taille est en O(Max(n, m) au lieu de O(nm) pour WF), permettant de calculer le coût de la trace
optimale, mais pas de reconstituer cette trace.
Question 8 : Montrer que l’algorithme de Hirshberg permet de reconstituer la trace en espace
linéaire, s’il utilise WFLAvant et WFLArriere au lieu de WFAvant et WFArriere.

La solution est page ??

Exercice 3.14 (♥ Transformée de Fourier rapide (FFT)).

Une transformée de Fourier discrète, ou DFT , est une transformation linéaire de CN dans CN .
Soit x = {xn }, 0 ≤ n ≤ N − 1 une suite de nombres complexes (xn ∈ C). Pour un certain entier k,
la valeur Xk de la DFT de la suite x se définit par :

N−1
X 2πi
Xk = xn e− N nk , 0≤k≤N−1 (3.1)
n=0

Avec e la base du logarithme naturel et i le nombre complexe bien connu.


32 CHAPITRE 3. DIVISER ET DIMINUER POUR RÉGNER
On peut calculer Xk pour k de 0 à N − 1 et obtenir la totalité X de la DFT de x par le produit
matriciel X = Vx, ou :

X0 x0
   

 X1 


 x1 

 X2  =

V
 x2 
.. ..
 
   
 .   . 
XN−1 xN−1

avec :

1 1 1 ··· 1
 
1
 w1 w2 ··· wN−1 

V =
1 w2 w4 ··· w2(N−1) 
.
 
 ..


1 wN−1 w2(N−1) ··· w(N−1)(N−1)

2iπ
Dans ce tableau, on a remplacé e N par le symbole w. Il sera bientôt explicité que w est la première
racine N-ième de l’unité.
La définition de la DFT a la propriété de permettre de recalculer x à partir de X, car la matrice
V est inversible. La formule explicite est la suivante :

N−1
1 X 2πi
xn = Xk e N nk , 0≤k≤N−1 (3.2)
N
n=0

On va donner maintenant le principe d’un algorithme Diviser pour Régner pour calculer X, la DFT
de x, en se fondant sur les propriétés des racines N-ièmes de l’unité. On va supposer pour simplifier
que N est une puissance de 2.
Pour voir sur un exemple, examinons une transformée discrète sur N = 8 points. La matrice V est
la suivante :
 
1 1 1 1 1 1 1 1
1
 w w2 w3 w4 w5 w6 w7 
1
 w2 w4 w6 w8 w10 w12 w14 

1 w3 w6 w9 w12 w15 w18 w21 
 
V =
1 w4 w8 w12 w16 w20 w24 w28 
 
w5 w10 w15 w20 w25 w30 w35 
 
1
w6 w12 w18 w24 w30 w36 w42 
 
1
1 w7 w14 w21 w28 w35 w42 w49
√ √

Dans cet exemple, w = e 4 . Cette valeur est aussi égale à 22 + i 22 .
Il n’y a pas en réalité tellement de valeurs différentes dans cette matrice, en raison des propriétés des
racines complexes de l’unité : c’est ce qui va permettre de simplifier le calcul. La figure 3.1 représente
dans le plan complexe les racines N-ièmes de l’unité pour N = 8. Les racines sont situées de manière
equidistante sur le cercle complexe unité, ce qui permet de déduire les propriétés suivantes :

w2k+N = w2k
wk+N/2 = −wk
√ √
2 2
De la sorte, la matrice précédente peut s’écrire avec seulement quatre valeurs : 1, i, w = 2 +i 2
3.2. EXERCICES 33

Fig. 3.1 – Racines N-ièmes de l’unité dans le plan complexe avec N=8.

√ √
2 2
et w = 2 −i 2

 
1 1 1 1 1 1 1 1
1 w i w -1 −w -i −w
 

1 -1 1 -1
 

 i −i i −i 

V =

 1 w -i w -1 −w i −w
1 −1 1 −1 1 −1 1 −1 
 

1 −w i −w −1 w −i w
 

 
 1 −i −1 i 1 −i −1 i 
1 −w −i −w −1 w i w

De plus, il est facile de déduire des propriétés précédentes que les carrés des N racines N-ièmes de
l’unité sont les N N
2 racines 2 -ièmes de l’unité.

C’est ainsi que dans l’exemple précédent les valeurs encadrées forment la matrice d’une DFT sur 4
points :

1 1 1 1 1 1 1 1
   
1 w4
 w24 w34 
 =
1 i −1 −i 
 
1 w2 w44 w64  1 −1 1 −1
4
1 w34 w64 w94 1 −i −1 i

Le principe du calcul rapide de la Tranformée de Fourier Discrète par l’algorithme FFT s’appuie sur
cette propriété des carrés des racines de l’unité. Le principe est de décomposer un problème de DFT
sur N points en deux sous-problèmes de DFT sur N 2 points.

L’astuce est de décomposer la suite d’entrée x en deux sous-suites, l’une comportant les valeurs
d’indice pair, l’autre comportant les valeurs d’indice impair :

  N
xe = x0 x2 ··· x2p ··· xN−2 , 0≤p≤ −1
2
  N
xo = x1 x3 ··· x2p+1 ··· xN−1 , 0≤p≤ −1
2

De la même façon, on note Xe la transformée de xe et Xo la transformée de xo .


34 CHAPITRE 3. DIVISER ET DIMINUER POUR RÉGNER
La récurrence générale de l’algorithme FFT peut maintenant s’expliciter :
N−1
X
Xk = xn wnk
n=0
N/2−1 N/2−1
X X
= xep w2pk + xop w(2p+1)k
p=0 p=0
N/2−1 N/2−1
X X
= xep (wpk )2 +w k
xop (wpk )2
p=0 p=0

On a par définition :

N/2−1
X N
Xek = xep (wpk )2 , 0≤k≤ −1
2
p=0
N/2−1
X N
Xok = xop (wpk )2 , 0≤k≤ −1
2
p=0

A partir de la décomposition paire/impaire et des propriétés précédentes, il est maintenant facile


de conclure que :

N
Xk = Xek + wk Xok 0≤k≤ −1
2
N
Xk+ N = Xek − wk Xok 0≤k≤ −1
2 2
Question 1 : Donner l’algorithme FFT permettant de calculer la DFT X d’une suite x = {xn }, 0≤
n ≤ N − 1 pour N puissance de 2.
Question 2 : Quelle est sa complexité (l’opération élémentaire est la multiplication) ?

La solution est page ??

Exercice 3.15 (♥ Produit de convolution). Etant donné deux vecteurs de taille (n − 1) :


a = (a0 , a1 , . . . , an−1 ) et b = (b0 , b1 , . . . , bn−1 ), on définit leur produit de convolution comme un
vecteur a ⋆ b de taille (2n − 1) dont la coordonnée de rang k s’écrit :
X
ai bj
i+j=k
0≤i≤n−1
0≤j≤n−1

Pn−1 Pn−1
Question 1 : Soit les polynômes A(y) = i=0 ak yk et B(y) = i=0 bk yk . Montrer que le
coefficient de rang k du polynôme C(y) = A(y)B(y) n’est autre que la coordonnée de rank k du
produit de convolution des vecteurs a et b.
Question 2 : En déduire une méthode pour calculer le vecteur a ⋆ b en un nombre de multiplica-
tions en O(nLog(n)).
Question 3 : Montrer que ce calcul peut être étendu au cas où la taille des deux vecteurs (et donc
le degré des polynômes) sont différents.

La solution est page ??

Exercice 3.16 (♥ Titre).


En électrostatique, la loi de Coulomb exprime la force F1→ 2 électrique exercée par une charge électrique
3.2. EXERCICES 35

q1 placée en un point M1 sur une charge q2 placée en un point M2 . Cette loi s’exprime sous forme
vectorielle par la formule suivante :


→ q1 q2 −r

12
F 1→ 2 = −r ||2 ||→
−r ||
4πǫ0 ||→ 12 12

−r = −
où →
−−−→
M1 M2 est le vecteur qui relie le premier corps au deuxième. ǫ0 est une constante.
12
Considérons un ensemble de n charges q1 , . . . , qn disposées régulièrement sur une ligne droite.


Notons F •i la somme des forces exercées par les (n − 1) autres charges sur la charge qi .
Question 1 : Montrer que l’on a la relation suivante :

X X

→ Cq q
i j Cq q
i j
|| F •i || =

2

i<j (j − i) (j − i)2
i>j

où C est une constante.




Question 2 : Donner une méthode pour calculer l’ensemble (pour i = 1, n) des valeurs F •i en un
nombre de multiplications en O(nLog(n))

La solution est page ??


36 CHAPITRE 3. DIVISER ET DIMINUER POUR RÉGNER
Chapitre 4

Essais successifs

4.1 Rappels
4.1.1 Principe
La programmation par essais successifs (ou backtracking) est une méthode d’énumération sans
répétition de toutes les solutions possibles d’un problème donné. Si on cherche la meilleure solution (à
supposer que chacune puisse être notée), ce n’est qu’après avoir tout essayé qu’on pourra la connaître.
Ici, on considère que le problème se décrit sous la forme d’un vecteur X, dont chaque composante
xi prend ses valeurs dans un domaine Si , de taille finie. Si le vecteur X est de taille n et si (un cas
classique), la taille de chaque domaine est exactement 2, le nombre de solutions possibles est 2n .
Par exemple, étant donné un ensemble de n éléments, on cherche à énumérer tous ses sous-
ensembles pour trouver le meilleur, au sens d’un critère qui n’importe pas encore ici. Il s’agit donc
d’énumérer sans répétition les 2n sous-ensembles, pour chacun de calculer sa valeur, et de retenir
finalement le meilleur.
Un autre cas classique est celui de la meilleure permutation des éléments d’un ensemble. La com-
binatoire est plus importante, puisqu’il s’agit d’examiner n! possibilités. On verra que la méthode des
essais successifs permet aussi de résoudre ce genre de problème.
Heureusement, il n’est pas toujours nécessaire d’examiner toutes les possibilités : certaines peuvent
être éliminées avant leur construction complète. Mais il faut pour cela prendre garde à organiser
l’énumération de façon à faire demi-tour dès que l’on s’aperçoit que l’on est dans un cul-de-sac.
La technique de programmation par essais successifs est récursive. La récursion porte sur l’indice
du vecteur X et développe l’arbre de toutes les possibilités en profondeur d’abord. Typiquement, l’énu-
mération de tous les sous ensembles d’un ensemble E à quatre élements consiste à produire tous les vec-
teurs binaires dans l’ordre suivant : (0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 1, 0), (0, 0, 1, 1), . . . , (1, 1, 1, 0, ), (1, 1, 1, 1).
Grâce à la procédure récursive, on construit un arbre dont les feuilles sont de gauche à droite les
16 vecteurs binaires ci-dessus, et dont les nœuds sont des solutions partielles, qui s’écrivent avec les
feuilles dans l’ordre de création : (∗, ∗, ∗, ∗), (0, ∗, ∗, ∗), (0, 0, ∗, ∗), (0, 0, 0, ∗), (0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 1, ∗), . . .
Sous forme graphique, les nœuds et les feuilles sont construits dans l’ordre indiqué dans la figure
4.1, correspondant à la recherche profondeur d’abord 1 .
L’élagage permet, en testant chaque solution partielle, d’éviter quand c’est possible de développer
les solutions qui se trouvent en-dessous dans l’arbre , soit parce qu’on sait que la solution partielle ne
mènera jamais à une solution, soit parce que elle mènera peut-être à des solutions, mais dont on sait
déjà qu’elle ne seront pas optimales.

1 On pourrait faire des essais successifs en largeur d’abord en gardant le caractère récursif de l’algorithme.

37
38 CHAPITRE 4. ESSAIS SUCCESSIFS

**** 1

0*** 2 1*** 17

00** 3 01** 10

000* 4 001* 7 010* 11 011* 14

0000 5 0001 6 0010 8 0011 9 0100 12 0101 13 0110 15 0111 16

Fig. 4.1 – Arbre de récursion à moitié complet, avec l’ordre de construction des nœuds en profondeur
d’abord.

4.1.2 Algorithmes génériques

1: Procedure ToutesSolutions(i :in ) 1: Procedure SolutionOptimale(i : in)


2: début 2: début
3: Calculer le domaine Si de xi ; 3: Calculer le domaine Si de xi ;
4: pour xi parcourant Si faire 4: pour xi parcourant Si faire
5: si Satisfaisant(xi ) alors 5: si Satisfaisant(xi ) alors
6: Enregistrer(xi ) ; 6: Enregistrer(xi )
7: si SolutionTrouvée alors 7: si SolutionTrouvée alors
8: EcrireSolution 8: si SolutionMeilleure alors
9: sinon 9: Conserver
10: si SolutionEncorePossible alors 10: fin si
11: ToutesSolutions(i+1) 11: sinon
12: fin si 12: si SolutionEncorePossible alors
13: fin si 13: SolutionOptimale(i+1)
14: Défaire(xi ) /% le contraire de Enregis- 14: fin si
trer %/ 15: fin si
15: fin si 16: Défaire(xi ) /% le contraire de Enregis-
16: fin pour trer %/
17: fin 17: fin si
18: fin pour
19: fin
4.2. EXERCICES 39

4.2 Exercices
Exercice 4.1 (♥ Les n reines.).
Jouez-vous aux échecs ? Savez-vous comment mettre 8 reines sur un échiquier de sorte qu’aucune ne
soit en prise par une autre ? Rappellons qu’une reine, au jeu d’échecs, met en prise toute autre pièce
située sur la même colonne, la même ligne ou l’une de ses deux diagonales (à condition qu’elle ne
soit pas masquée par une pièce intermédiaire). On peut poser ce problème de manière plus générale :
comment mettre n reines sur un échiquier de taille n × n de sorte qu’aucune ne soit en prise par une
autre ? Par exemple, pour n = 4, une solution est la suivante :

(4, 2)


(3, 4)


(2, 1)


(1, 3)


Il est facile de constater qu’une solution, si elle existe, doit comporter une reine et une seule par ligne.
Une solution peut donc se représenter par un tableau X[1 : n], tel que X[i] = j signifie que la reine sur
la ligne i est sur la colonne j. La solution ci-dessus peut donc s’écrire : (3 , 1 , 4 , 2).
On va d’abord générer tous les vecteurs X dont les éléments sont les permutations de {1, 2, 3, 4}, que
l’on peut appeller des solutions potentielles. Une solution potentielle représente donc un ensemble
de reines qui ne sont en prise ni par les lignes ni par les colonnes. Parmi elles il y aura peut-être des
vraies solutions, on le saura quand on aura vérifié en plus qu’il n’y a pas de prise par les diagonales.
Question 1 : Ecrire un algorithme itératif effectuant 44 comparaisons qui trouve toutes les solu-
tions potentielles au problème des 4 reines.
Question 2 : Ecrire un algorithme itératif effectuant 4! comparaisons résolvant le même problème.
Question 3 : Ecrire un algorithme (récursif) par essais successifs résolvant le même problème.
Quel est son avantage par rapport aux précédents ?
Question 4 : Ecrire un algorithme par essais successifs qui intègre la contrainte de non-prise par
les diagonales et se moque bien de la taille de l’échiquier.
Question 5 : Décrire l’arbre de récursion et ses élagages pour le cas n = 4.

Anonyme

Exercice 4.2 (♥ Le parcours d’un cavalier.).


On se pose le problème suivants : en combien de coups minimum un cavalier peut-il, sur un jeu d’échecs
où il est seul, aller de la case (m , n) à la case (k , l) ? Les indices m, n, k, et l varient de 1 à 8. Le
cavalier respecte ses règles de déplacement : s’il est sur la case (m , n), il peut aller sur les huit cases :
(m − 2 , n − 1) (m − 2 , n + 1)
(m − 1 , n − 2) (m − 1 , n + 2)
(m + 1 , n − 2) (m + 1 , n + 2)
(m + 2 , n − 1) (m + 2 , n + 1)
à condition évidemment de ne pas sortir de l’échiquier. La figure ci-dessous représente les déplace-
ments possibles du cavalier en case (4 , 4).
40 CHAPITRE 4. ESSAIS SUCCESSIFS

❡ ❡ ❡ ❡ ❡ ❡ ❡ (8, 8)

❡ ❡

❡ ❡

❡ ❡

❡ ❡

(1, 1) ❡

La figure suivante montre deux parcours pour aller de la case (4 , 4) à la case (4 , 3) ou l’inverse ,
l’un de longueur 4, l’autre (optimal) de longueur 3.

❡ ❡ ❡ ❡ ❡ ❡ ❡ ❡

❡ ❡

❡ ❡

❡ ❡

❡ ❡

❡ ❡

L’élément du vecteur de la récursion sera une position du cavalier, sous la forme d’un couple
(X[i].abs , X[i].ord) qui décrit l’abscisse et l’ordonnée de la position.
Question 1 : Donner l’algorithme qui décrit le parcours optimal du cavalier.

Anonyme

Exercice 4.3 (♥ Le voyageur de commerce.).


Un voyageur de commerce doit visiter n villes. On suppose qu’il y a une route entre chaque paire de
villes, avec une certaine longueur connue à l’avance. Le voyageur part d’une certaine ville et doit y
revenir.
Question 1 : Dans quel ordre le voyageur doit-il visiter les villes pour minimiser le la longueur
totale du chemin parcouru ?

Anonyme

Exercice 4.4 (♥ Circuits eulériens.).

Un graphe G = (N, V) est composé d’un ensemble N fini de nœuds et d’un ensemble V ⊂ N × N
d’arcs. Chaque arc peut donc être vu comme un lien d’un nœud vers un autre.
Sur l’exemple de la figure, on a :
N = {A, B, C}
V = {(A, B), (A, C), (B, A), (B, B), (B, C), (C, A), (C, B)}
4.2. EXERCICES 41

A C

Etant donné un nœud q, l’ensemble des nœuds s tels qu’il existe un arc (q, s) est l’ensemble des
nœuds successeurs de q. On le note succ(q). L’ensemble des nœuds p tels qu’il existe un arc (p, q)
est l’ensemble des nœuds prédécesseurs de A. On le note pred(q).
Sur le graphe de la figure : pred(B) = {A, B, C} et succ(C) = {A, B}
Un graphe est dit eulérien si chaque nœud possède le même nombre de successeurs et de pré-
décesseurs et que ce nombre n’est nul pour aucun nœud. Par exemple, le graphe de la figure est
eulérien, puisque A, B et C ont respectivement deux, trois et deux successeurs et deux, trois et deux
prédécesseurs.
On appelle circuit dans un graphe une suite d’arcs

(n1 , n2 ), (n2 , n3 ) . . . , (ni , ni+1 ), (ni+1 , ni+2 ), . . . (np , n1 )

On l’écrit plus simplement :


n1 , n2 , . . . , ni , . . . np , n1
Un circuit est composé de p arcs et passe par p nœuds.
Un circuit est dit eulérien pour le graphe G s’il utilise une fois et une seule tous les arcs de G. Il
a été démontré par Euler en 1736 qu’un graphe possède (au moins) un circuit eulérien si et seulement
si il est eulérien.
Par exemple, sur le graphe de la figure, les circuits suivants sont eulériens :

ABBCACBA

ABCACBBA

Question 1 : Donner tous les circuits eulériens du graphe de la figure.


Question 2 : Donner en pseudo-code un algorithme par essais successifs permettant de lister tous
les circuits eulériens d’un graphe eulérien. On supposera que les structures de données suivantes sont
accessibles : l’ensemble N des nœuds du graphe, l’ensemble V des arcs et pour chaque nœud s, les
ensembles succ(s) et pred(s).
Question 3 : Expliquer en détail comment sont construites les « briques » de l’algorithme :
domaine, SolutionEncorePossible, etc.

Anonyme

Exercice 4.5 (♥ Chemins hamiltoniens : les dominos.).


On considère le jeu suivant : six dominos portent chacun un mot de quatre lettres, tiré d’un dictionnaire
de mots valides. On peut juxtaposer deux dominos si les deux dernières lettres du premier forment un
mot de quatre lettres avec les deux premières lettres du second domino. Par exemple, le domino SEME
peut être mis après le domino REMI, puisque MISE est un mot (dans notre exemple, le dictionnaire
est celui du frnançais, on ne tient pas compte des accents, on accepte les noms propres).
42 CHAPITRE 4. ESSAIS SUCCESSIFS

On représente un tel jeu par un graphe : les six dominos sont représentés dans les nœuds du graphe
et un arc représente la possibilité de placer deux dominos l’un après l’autre.
Le jeu consiste à créer une chaîne de six dominos. Une solution est, par exemple :
BETE TELE VECU REMI SEME LESE
On affecte un numéro à chaque domino :
1 2 3 4 5 6
BETE SEME VECU LESE TELE REMI
Le but de cet exercice est d’écrire un programme qui donne toutes les solutions à ce problème.
D’une manière plus générale, il s’agit de trouver tous les chemins dans un graphe qui passent une
fois et une seule par tous les nœuds. Un tel chemin est dit hamiltonien.
Question 1 : Tracer le graphe du jeu
Question 2 : On impose de commencer par le domino de numéro 1 (BETE) puis d’aller au domino
numéro 5 (TELE). Trouver toutes les solutions en développant un arbre.
Question 3 : Donner un programme qui trouve tous les chemins hamiltoniens dans un graphe.
Anonyme
Exercice 4.6 (♥ Isomorphisme de graphes).

Un graphe orienté G est constitué d’un ensemble fini S de sommets et d’un ensemble A d’arcs,
avec A sous-ensemble de S × S . Deux graphes G = (S, A) et H = (T, B) sont isomorphes s’il existe
une relation bijective entre S et T qui fasse correspondre bijectivement A et B. Par exemple, les deux
graphes G1 et G2 donnés sur la figure sont isomorphes pour la bijection :
1 2 3 4 5
c a b e d
L’arité positive (resp : négative) d’un noeud est le nombre d’arcs qui en partent (resp : qui y arrivent).
Dans l’exemple, le noeud 1 du sommet G1 a pour arité positive 3, pour arité négative 1, comme le
sommet c du graphe G2 .
On s’intéresse à l’écriture d’un algorithme dont l’entrée est un couple de graphes (G, H) ayant le
même nombre de sommets et la sortie un entier indiquant le nombre d’isomorphismes possibles entre
G et H.

1 a

2 3 b c

4 5 d e
Le graphe G1 . Le graphe G2 .

Question 1 : Donner tous les isomorphismes possibles entre G1 et G2 . Conclusion ?


Question 2 : Donner le schéma d’un algorithme de base du type « essais successifs »pour résoudre
ce exercice. Spécifier la représentation vectorielle du exercice et l’instanciation des procédures de
l’algorithme général. Dans cette première version, la procédure Satisfaisant rendra toujours la valeur
VRAI et on ne fera pas intervenir les arités des sommets. Décrire soigneusement ce que doit faire la
procédure SolutionEncorePossible. Quelle est la complexité au pire de l’algorithme ?
4.2. EXERCICES 43

Question 3 : Décrire une procédure Satisfaisant utilisant l’arité des noeuds de G et H.


Question 4 :

Anonyme. ARCHIVES ENSSAT 1996-97.

Exercice 4.7 (♥ Coloriage d’un graphe).


Un graphe non orienté G = (N, V) est composé d’un ensemble N fini de nœuds et d’un ensemble
V ⊂ N × N d’arêtes. Chaque arête peut être vue comme un lien symétrique entre deux nœuds .

a
e

c d

Sur l’exemple de la figure, on a :


N = {a, b, c, d, e}

V = {(a, b), (a, c), (b, c), (b, e), (c, d), (d, e)}
On peut représenter un graphe avec n nœuds par sa matrice d’adjacence G[i, j], pour i et j = 1, n,
avec G[i, j] = 1 s’il existe un arête entre les nœuds i et j et avec G[i, j] = 0 sinon. Pour ce type de
graphe non orienté, G[i, j] = G[j, i]. De plus on suppose ici qu’il n’y a pas d’arête en boucle sur un
nœud : G[i, i] = 0 pour i = 1, n.
Dans notre exemple, on a :
0 1 1 0 0
 
1 0 1 0 1
 
G= 1 1 0 1 0

0 0 1 0 1
0 1 0 1 0
Un coloriage d’un graphe non orienté consiste à affecter à chaque nœud une couleur parmi m, de
sorte que deux arêtes de la même couleur ne soient pas reliés par un arête. Par exemple, avec trois
couleurs notées 1, 2 et 3, on peut colorier le graphe précédent de cette manière :

b 2

1 a
e 3

3 c d 1
44 CHAPITRE 4. ESSAIS SUCCESSIFS

Ce coloriage peut être représenté par le vecteur X[1 : 5] = (1, 2, 3, 1, 3), dont la première composante
est la couleur du premier nœud a, la seconde est la couleur du second nœud b, et ainsi de suite pour
les cinq nœuds .
Question 1 : Donner un autre coloriage de ce graphe qui conserve la couleur des nœuds a et b.
Question 2 : On veut écrire un algorithme par Essais Successifs qui écrit tous les coloriages
différents à m couleurs d’un graphe quelconque, ayant n nœuds , dont on connaît la matrice d’adjacence
G.
4.2. EXERCICES 45

Le schéma de l’algorithme sera le suivant (noter que Domaine est un appel de procédure) :
1: Procedure Coloriage(i)
2: début
3: Si ← Domaine(x) ;
4: pour x parcourant Si faire
5: si Satisfaisant(x) alors
6: Enregistrer(x) ;
7: si SolutionTrouvée alors
8: EcrireSolution
9: sinon
10: si SolutionEncorePossible alors
11: Coloriage(i + 1)
12: fin si
13: fin si
14: Défaire(x) /% le contraire de Enregistrer %/
15: fin si
16: fin pour
17: fin
a) Que représente x ?
b) Expliquer comment définir Domaine(x) et Satisfaisant ?
c) Montrer que les autres paramètres du programme (SolutionTrouvée, SolutionEncorePossible)
sont très simples.
d) Ecrire les procédures Coloriage, Domaine et Satisfaisant (les entiers n et m, le vecteur X et la
matrice G sont des variables globales).
e) Dessiner la partie de l’arbre de récursion sur le graphe exemple où le nœud a prend la couleur 1 et
b la couleur 2. Préciser l’ordre des appels récursifs.
f) Combien y a-t’il de coloriages différents à trois couleurs pour le graphe exemple ?

Anonyme

Exercice 4.8 (♥ Partition de tableau.).


On considère un tableau T [1 : n] d’entiers positifs, triés par ordre croissant. n est un nombre pair
strictement positif.
Question 1 : On désire partitionner T en deux sous-tableaux de dimension n 2 , nommés T1 et T2
tels que :
n n
X
2 X2

S1 = T1 (i) S2 = T2 (i) avec S1 ≤ S2 et S2 − S1 maximal


i=1 i=1

Donner le principe de construction de T1 et T2 . Quelle est la complexité de la procédure ?


Question 2 : On désire maintenant partitionner T en deux sous-tableaux de taille n/2, appelés T3
et T4 tels que :
n n
X
2 X
2

S3 = T3 (i) S4 = T4 (i) avec S3 ≤ S4 et S4 − S3 minimal


i=1 i=1
Pn
En notant S = i=1 T (i), montrer que l’exercice posé est équivalent à :

S − 2S3 minimal

Question 3 : Proposer une solution par une procédure à essais successifs n’utilisant aucun élagage,
où la solution est un tableau binaire T3 de taille n/2. T3 (i) = 1 signifie que T (i) est affecté à T3 .
Question 4 : On n’a pas utilisé dans la question précédente le fait que T est trié. Peut-on proposer
des élagages améliorant la procédure précédente en tenant compte de cette propriété ?
46 CHAPITRE 4. ESSAIS SUCCESSIFS

Remarque : Ce problème est un cas particulier de sac à dos 0/1 (voir le chapitre 8.1.1, page
75). Par conséquent, il existe aussi un algorithme de programmation dynamique pour le résoudre,
de complexité temporelle en Θ(nS). Cet algorithme est développé pour le problème voisin du sous-
ensemble de somme donnée dans l’exercice 99.17, page 95.

Anonyme

Exercice 4.9 (♥ Les élections présidentielles à l’américaine.).

Les élections présidentielles américaines se déroulent en gros de la façon suivante : dans chacun
des 50 états, les électeurs votent pour un des deux candidats, démocrate ou républicain. Si c’est le
candidat républicain qui dépasse l’autre en nombre de voix dans cet état, l’état enverra à Washington
des « grands électeurs », tous républicains. Si c’est le candidat démocrate qui gagne dans cet état,
l’état enverra à Washington le même nombre de grands électeurs, tous démocrates. Le nombre des
grands électeurs dépend de la taille de l’état.
Pour finir, les grands électeurs se retrouvent à Washington et votent conformément à leur étiquette.
Le président élu est celui qui a le plus de voix de grands électeurs.
En pratique, sur un total de 538 grands électeurs, la Californie en a 54. Le Texas en compte 32,
l’état de New York 33, la Floride 25, la Pennsylvanie 23, l’Illinois 22, etc. Les autres chiffres sont plus
faibles.
Question 1 : Donner un algorithme pour énumérer toutes les configurations où les deux candidats
se retrouvent avec le même nombre de voix de grands électeurs. Cet algorithme doit fonctionner pour
les Etats-Unis et pour tous les pays où se pratique ce type d’élections. Evidemment, on supposera
connaître le nombre d’états et le nombre de grands électeurs par état.
Question 2 : Montrer que le nombre de telles configurations est pair.
Question 3 : Donner une modification de l’algorithme qui n’en produit que la moitié.
Question 4 : Proposer une modification très simple de la constitution américaine pour que l’élec-
tion soit toujours effective, c’est à dire que les deux candidats ne puissent pas avoir le même nombre
de voix de grands électeurs.
Remarque : Ce problème est un cas particulier de sac à dos 0/1 (voir le chapitre 8.1.1, page 75). Par
conséquent, il existe aussi un algorithme de programmation dynamique pour le résoudre, de complexité
temporelle en Θ(nN), où N est le nombre total de grands électeurs. Cet algorithme est développé pour
le problème voisin du sous-ensemble de somme donnée dans l’exercice 99.17, page 95.

Anonyme

Exercice 4.10 (♥ Crypto-arithmétique.).

On cherche à résoudre des problèmes qui se présentent sous la forme d’une opération arithmétique
sur des lettres. Le but est d’affecter un chiffre (0 à 9) à une lettre, sous la condition que le même
chiffre ne soit pas affecté à deux lettres, et qu’une lettre vaille toujours le même chiffre. Le résultat de
cette affectation doit fournir une opération arithmétiquement correcte en base 10.
Par exemple le problème de crypto-arithmétique :

NEUF + UN + UN = ONZE

que l’on peut aussi écrire :

N E U F
+
U N
+
U N
O N Z E
4.2. EXERCICES 47

a une solution que l’on peut représenter par :

(N = 1 , E = 9 , U = 8 , O = 2 , Z = 4 , F = 7)

parce que :
1987 + 81 + 81 = 2149
Les trois exemples suivants montrent des affectations incorrectes :
– La proposition 1988 + 81 + 81 = 2150 suppose : U = F = 8
– La proposition 1986 + 82 + 82 = 2150 suppose que 1 et 2 sont affectés à N et 0 et 9 à E.
– Quant à l’affectation (N = 2 , E = 9 , U = 8 , O = 3 , Z = 4 , F = 7), qui correspond à
2987 + 82 + 82 = 3242, elle est arithmétiquement fausse.
On supposera qu’il existe une procédure og CalculExactfg qui, appelée pour une affectation des
lettres, rend VRAI si l’opération est arithmétiquement correcte et FAUX sinon.
Question 1 : Donner le principe d’un algorithme permettant de trouver toutes les solutions à tout
problème de crypto-arithmétique. On emploiera la méthodologie « essais successifs », sans chercher
d’élagages.
Question 2 : Définir les paramètres du programme : le vecteur de la récursion, sa taille, le domaine
de chaque composante, les procédures og Satisfaisantfg , og SolTrouvéfg , og Enregistrerfg et
og Défairefg .
Question 3 : Ecrire le pseudo-code.
Question 4 : Pour un problème particulier comme celui donné ci-dessus, comment pourrait-on
utiliser la procédure og SolEncorePossiblefg pour faire des élagages ? Vous paraît-il facile de trouver
une technique générale pour l’utiliser ?

Anonyme

Exercice 4.11 (♥ Détermination de tous les déplacements d’un cavalier aux échecs.).

Question 1 : On s’intéresse aux parcours par un cavalier partant de la case (xd , yd ) et devant se
rendre à la case d’arrivée (xa , ya ).
Question 2 : Faire l’analyse du problème posé en spécifiant les paramètres de la solution générique
Question 3 : Donner le code d’un algorithme associé à cette analyse

Anonyme

Exercice 4.12 (♥ Les anagrammes.).

On cherche à trouver tous les anagrammes d’un mot donné, c’est à dire tous les mots qui s’écrivent
avec exactement les mêmes lettres, mais dans un ordre différent. Par exemple, le mot DINAR est un
anagramme du mot RADIN. Par extension, on peut dire qu’un mot est un anagramme de lui-même et
donc que le mot RADIN possède deux anagrammes, DINAR et RADIN (dans l’ordre alphabétique).
Un cas simple.
On supposera d’abord que toutes les lettres du mot de départ sont différentes, comme dans
l’exemple précédent.
La liste des mots est donnée par un dictionnaire D. On suppose qu’il existe une procédure cher-
cher (mot, D) qui répond VRAI si le mot mot est dans D, FAUX sinon. On ne s’intéresse pas à la
manière dont fonctionne cette procédure.
La structure de données utilisée pour représenter un mot est un vecteur, dont la première compo-
sante est la première lettre du mot, la seconde composante la seconde lettre, et ainsi de suite.
On commence par fabriquer un ensemble L, composé de toutes les lettres du mot de départ, sous
la forme d’une liste dans l’ordre alphabétique. Dans notre exemple : L = {A, D, I, N, R}.
On veut écrire un programme, par un schéma Essais Successifs (Toutes Solutions), qui produise
tous les anagrammes du mot de départ une fois et une seule, y compris ce mot de départ.
48 CHAPITRE 4. ESSAIS SUCCESSIFS

Question 1 : Définir le vecteur de récursion et le domaine de chaque composante, en utilisant un


ensemble DéjàPris dans lequel se trouvent les lettres qui composent la solution partielle.
Question 2 : Ecrire le pseudo-code de la procédure récursive et de son appel dans le programme
principal.
Question 3 : Dans quel ordre le programme écrit-il les anagrammes ? Pourquoi ?
Question 4 : Quelle est la complexité au pire de la procédure, en nombre d’appels à chercher ?
Le cas général.
On enlève maintenant la contrainte que toutes les lettres du mot de départ sont différentes. On
part de mots qui peuvent avoir au moins une lettre qui arrive deux fois, comme RATER. Dans ce cas,
L n’est plus un ensemble, mais un sac de lettres. Dans cet exemple : L = {A(1), E(1), R(2), T (1)}.
Question 5 : Comment aménager l’algorithme précédent pour écrire
– Tous les anagrammes avec les répétitions dues à la multiplicité des lettres ? Par exemple, à partir
de RATER, la liste de ces anagrammes sera ARRET , ARRET , RATER, RATER, TARER, TARER (on
suppose pour cet exemple que le dictionnaire ne tient pas compte des accents).
– Tous les anagrammes une fois et une seule chaque anagramme ? Dans ce cas, à partir de RATER, la
liste des anagrammes doit être ARRET , RATER, TARER.
Un autre algorithme.
Voici un autre algorithme, complètement différent, qui n’utilise pas la méthode Essais Successifs.
Pour chaque mot du dictionnaire, on regarde si le sac de lettres qui le compose est égal au
sac de lettres du mot dont on cherche les anagrammes.
Question 6 : Quelle est la complexité de cette méthode en nombre de comparaisons ? Conclusion ?

Archives ENSSAT EII2 03-04

Exercice 4.13 (♥ Carrés latins.).


Un carré latin d’ordre n est un tableau carré dans lequel les cellules contiennent les n éléments
d’un ensemble S, qui sont disposés de telle manière qu’ils apparaissent une et une seule fois dans
chaque ligne et dans chaque colonne. Chacune des lignes et des colonnes est donc constituée par une
permutation des n éléments.
Par exemple, pour n = 4 et S = {1, 2, 3, 4}, on a (parmi 576) les trois carrés latins suivants :

1 3 4 2 1 2 3 4 2 1 3 4
     
 3 1 2 4   2 4 1 3   1 3 4 2 
     
 4 2 1 3   3 1 4 2   4 2 1 3 
2 4 3 1 4 3 2 1 3 4 2 1

Le premier possède une particularité : chacune des deux diagonales est entièrement composée
d’éléments identiques. Un tel carré latin est dit antidiagonal.
Le second possède une autre particularité : les éléments de S apparaissent dans un ordre imposé
(ici, l’ordre numérique croissant) sur la première ligne et sur la première colonne. Un tel carré latin
est dit normalisé. Il y a 4 carrés latins normalisés et 18 antidiagonaux d’ordre 4.
Le dernier n’a aucune propriété particulière.
Question 1 : Donner le pseudo-code d’un programme permettant d’écrire tous les carrés latins
normalisés à un ordre n donné.
Question 2 : Démontrer qu’il n’y a pas de carré latin antidiagonal d’ordre impair. Donner le
pseudo-code d’un programme permettant d’écrire tous les carrés latins antidiagonaux à un ordre n
donné.
Question 3 : Donner le pseudo-code d’un programme permettant d’écrire un carré latin antidia-
gonal et normalisé à un ordre n donné.

LM
4.2. EXERCICES 49

Exercice 4.14 (♥ Le jeu de Sudoku.).


Le but du jeu est de remplir un carré de neuf cases de côté, subdivisé en autant de carrés identiques,
appelées régions, de façon à ce que chaque ligne, chaque colonne et chaque région contienne une fois
et une seule les chiffres de 1 à 9. Au début du jeu, un certain nombre de chiffres sont déjà en place
(dévoilés). Par exemple, il s’agit de terminer de remplir la grille suivante :

5 3 7
6 1 9 5
9 8 6
8 6 3
4 5 3 1
7 2 6
6 2 8
4 1 9 5
8 7 9

Question 1 : Donner un programme de type « essais successifs »qui compte toutes les solutions
quand la grille de départ est vide (NB : le résultat est environ 7.1021 ; quel serait le temps de calcul
de ce programme ?).
Question 2 : Donner un programme de type « essais successifs »qui donne toutes les solutions à
une grille dans laquelle un certain nombre de chiffres sont dévoilés.

Voir Wikipedia à l’entrée : Sudoku.


50
****

1*** 2*** ... ...

12** 13** 14** 21** 22** 23** 24**

✂ ✂ 141* 142* 143* 144* ✂ ✄ ✂ 241* 242* 243* 244*

✄ ✂ ✄ ✄ ✂ ✄

CHAPITRE 4. ESSAIS SUCCESSIFS


1421 1422 1423 1424 2411 2412 2413 2414

✕ ✕ ✖ ✕ ✕ ✕ ✰ ✕
Chapitre 5

Programmation par Séparation et


Evaluation Progressive

5.1 Rappels
5.1.1 Principe : comparaison avec Essais Successifs
La méthode PSEP (Programmation par Séparation et Evaluation Progressive) ou Branch and
Bound est comparable à la méthode des essais successifs, en ce qu’elle recherche par énumération
sans répétition, toutes les solutions d’un problème donné, ou (plus souvent) la meilleure solution à
ce problème. Elle construit aussi un arbre des solutions partielles et procède autant que possible à des
élagages.
La principale différence est que l’ordre de développement des nœuds et des feuilles n’est pas fixé
(rappelons qu’il est profondeur d’abord pour les essais successifs). PSEP choisit la solution partielle
la plus prometteuse, sur un critère à définir pour chaque problème, et la développe en calculant ses
successeurs dans l’arbre.
Cette technique conduit à une programmation itérative.
Toujours dans l’optique de comparaison avec les essais successifs, on cherche donc la meilleure
valeur de vecteur X, dont chaque coordonnée xi peut prendre ses valeurs (s’instancier) dans un domaine
fini Si . Comme dans la technique des essais successifs, on construit implicitement un arbre dont les
nœuds internes sont des solutions partielles et les feuilles sont des solutions avec une certaine valeur.
On cherche la solution de plus grande valeur. On part d’un vecteur X dont aucune coordonnée n’est
instanciée (la racine de l’arbre, voir les figures 4.1 et 5.1).

5.1.2 Algorithme générique


Le mot nœud désigne dans l’algorithme soit un nœud interne, soit une feuille. Les fils d’un nœud
interne sont les nœuds obtenus en instanciant (dans le domaine qui lui correspond) la première co-
ordonnée de X non instanciée. Le principe est de maintenir une liste de nœuds vivants, c’est à dire
pouvant mener à une solution meilleure que la solution courante SolCourante. Cette liste s’appelle
OPEN. La solution est initialisée par SolVide, qui signifie qu’on a pas trouvé de solution.
Cet algorithme mènera par exemple à une construction comme sur la figure 5.1. dans laquelle les
nœuds qui ne peuvent pas mener à une solution ne sont pas développés. L’ordre de développement
(c’est à dire de choix dans OPEN) est indiqué.
Il est important de remarquer que, de la manière dont les nœuds sont développés, il n’y a pas de
risque de mettre dans OPEN un nœud qui y est déjà, ou qui y a déjà été.

51
52 CHAPITRE 5. PROGRAMMATION PAR SÉPARATION ET EVALUATION PROGRESSIVE
1: Procedure PSEP()
2: début
3: OPEN ← racine
4: SolCourante ← SolVide
5: tant que OPEN n’est pas vide faire
6: Choisir dans OPEN le nœud le plus prometteur : n ;
7: Enlever n de OPEN
8: pour tous les fils nj de n faire
9: si nj est une solution alors
10: si nj est meilleure que SolCourante alors
11: SolCourante ← nj
12: fin si
13: sinon
14: si nj peut mener à des solutions alors
15: ajouter nj à OPEN
16: fin si
17: fin si
18: fin pour
19: fin tant que
20: fin

**** 1

0*** 2 1*** 8

00** 5 01** 3

000* 11 001* 6 010* 10 011* 4

0010 7 0011 13 0110 12 0111 9

Fig. 5.1 – Un exemple d’arbre que peut construire PSEP. L’ordre de développement, c’est à dire de
choix dans OPEN, est indiqué pour chaque nœud.
5.2. EXERCICES 53

Dans cet exemple, la suite des listes OPEN créées au cours des itérations est la suivante (le nœud
le meilleur est en gras) :
1 ****
2 0*** 1***
3 1*** 00** 01**
4 1*** 00** 010* 011*
5 1*** 00** 010* 0110 0111
6 1*** 000* 001* 010* 0110 0111
7 1*** 000* 0010 0011 010* 0110 0111
8 1*** 000* 0011 010* 0110 0111
9 000* 0011 010* 0110 0111
10 000* 0011 010* 0110
11 000* 0011 0110
12 0011 0110
13 0011
14

5.1.3 Un point de vue plus général.

5.2 Exercices
Exercice 5.1 (♥ Titre.).

Question 1 :
Question 2 :
Question 3 :

Anonyme

Exercice 5.2 (♥ Le taquin.).


Le taquin est un jeu dont le principe est de reconstituer un objet dans sa configuration standard,
à partir d’une situation aléatoire. L’objet est une grille de taille 5 × 5, composée de petites tuiles
numérotées de 1 à 15 qui peuvent glisser les une par rapport aux autres, en utilisant une place vide.
Par exemple, à partir la configuration de départ du dessus, on peut en un coup atteindre les quatre
configurations suivantes.

1 3 4 15
2 5 12
7 6 11 14
8 9 10 13

1 4 15 1 3 4 15 1 3 4 15 1 3 4 15
2 3 5 12 2 5 12 2 5 12 2 6 5 12
7 6 11 14 7 6 11 14 7 6 11 14 7 11 14
8 9 10 13 8 9 10 13 8 9 10 13 8 9 10 13

Le but du jeu est de rejoindre la position finale, celle-ci :

1 2 3 4
5 6 7 8
9 10 11 12
13 14 15
54 CHAPITRE 5. PROGRAMMATION PAR SÉPARATION ET EVALUATION PROGRESSIVE
en un nombre de coups minimum.
Il est à noter que parmi les 16! ≃ 211012 positions de départ possibles, seulement la moitié permet
de rejoindre la position finale. Pour savoir si la partie a une solution, un calcul préliminaire est possible.
Donnons-le par l’exemple, sur la position de départ D :

1 3 4 15
2 5 12
7 6 11 14
8 9 10 13

Supposons que les cases de D sont numérotées comme dans la position d’arrivée (la case vide portant
le numéro 16). Dans la configuration de départ D, on calcule les valeurs P(i), pour i = 1, 16, où p(i)
est le numéro de la case où se trouve le chiffre i. Par exemple P(13) = 16, P(3) = 2.
On calcule ensuite la valeur L(i), qui est égale au nombre de cases de D de numéro j, avecj < i
telles que P(j) > P(i). Par exemple, L(1) = 0, L(4) = 1, L(12) = 6.
P16
Pour finir, on calcule S = B + i=1 L(i), avec B = 1 si la case vide est sur une des positions
marquées 1 du tableau ci-dessous, 0 sinon.

0 1 0 1
1 0 1 0
0 1 0 1
1 0 1 0

Si S est pair, on peut atteindre le but à partir de D. Sinon, ce n’est pas la peine d’essayer.
Le but de cet exercice est d’adapter la méthode PSEP pour trouver une suite de coups optimale
qui mène à la configuration finale à partir de n’importe quelle configuration de départ.
Les éléments que l’on va mettre dans OPEN sont des configurations du jeu, notées n, associées
avec un certain coût c(n) qui mesure le nombre minimal (ou provisoirement connu comme tel) de
coups joués à partir de la situation de départ pour arriver à cette configuration.
Le principe de base est celui de PSEP : on choisit le meilleur élément dans OPEN (celui de coût
minimal) et on le remplace par ses fils.
Question 1 : Qu’appelle-t’on ici le fils d’une situation de jeu ?
Question 2 : Dans PSEP classique, il n’y a pas de risque de mettre dans OPEN un nœud qui y est
déjà, ou qui y a déjà été. Ce n’est pas le cas ici. Pourquoi ? Que faire pour remédier à ce problème ?
Question 3 : Montrer que l’algorithme conduit à un développement de type largeur d’abord.
Question 4 : On appelle h(n) une valeur qui estime inférieurement la distance en nombre de coups
P16
de la configuration n à la configuration finale. Par exemple h(n) = i=1 h(i), où h(i) est la distance
de Manhattan entre la case numéro i dans n et la case numéro i dans la configuration finale. Que se
passe-t’il si on utilise c(n) + h(n) au lieu de c(n) comme valeur dans OPEN ? (Réponse intuitive).

Sam Lloyd ([HOR78]). Cet exercice est une introduction informelle à l’algorithme A∗ ([RUS95]).
Chapitre 6

Programmation dynamique.

6.1 Présentation
Comme la méthode diviser pour régner, la programmation dynamique permet de résoudre des
problèmes d’optimisations en combinant les solutions de sous-problèmes. En revanche, cette méthode
est plus avantageuse lorsque les sous-problèmes ne sont pas indépendants, c’est à dire lorsqu’ils ont des
sous-sous-problèmes en commun. Ici le terme de "programmation"1 dénote une méthode tabulaire qui
consiste à construire une table mémorisant la solution de chaque sous-problème, un sous-problème est
alors résolu au plus une seule fois. Les problèmes à résoudre doivent cependant satisfaire le principe
d’optimalité de Bellman : “une2 solution optimale d’un problème peut s’obtenir à partir des solutions
optimales de sous-problèmes”. Un exemple classique consiste à calculer le plus court chemin dans un
graphe. On montre par l’absurde qu’un chemin est de longueur optimale si et seulment si il est composé
de sous-chemins de longueur optimale. Soit un graphe G et un chemin A − B − C − D de longueur
optimale (voir la Figure 6.1). Le coût de ce chemin est la somme des coûts des chemins A − B, B − C
et C − D. Supposons qu’il existe dans le graphe G un chemin de B à C de coût moindre (en pointillé
sur la figure) que le chemin de B à C choisi, alors il existe un chemin plus court de A à D.

A B C D

Fig. 6.1 – Application du principe d’optimalité à la recherche du plus court chemin dans un graphe.

Cependant, le principe d’optimalité ne s’applique toujours comme par exemple dans le cas du calcul
du plus long chemin sans cycle dans un graphe : soit le graphe de la Figure 6.2, le plus long chemin
sans cycle de A à C est A − B − C, or le plus long chemin sans cycle de B à C est B − A − C.
L’écriture d’un algorithme de programmation dynamique consiste, en général, à exprimer le pro-
blème en problèmes de taille inférieure et à établir une relation de récurrence entre ces derniers qui
conduit à une définition de la table, cette dernière pouvant avoir un nombre quelconque de dimen-
sions. Le remplissage de la table peut se faire alors de deux façons : récursivement, ce qui est trivial
grâce à la définition de la table, ou de façon itérative. À la base, les algorithmes de programmation
dynamique calculent des tables contenant les coûts des solutions optimales des sous-problèmes et non
1 Historiquement, Richard Bellman, alors qu’il était chercheur à la Rand Corporation, aurait choisi le nom de “pro-

grammation dynamique” pour satisfaire son supérieur qui ne supportait pas les mots “recherche” et “mathématiques”.
(D’après un article de J.-C. Culioli dans la Recherche 2003).
2 Notons qu’un problème n’a pas nécessairement une solution optimale unique.

55
56 CHAPITRE 6. PROGRAMMATION DYNAMIQUE.

A B C

Fig. 6.2 – Exemple où le principe d’optimalité ne s’applique pas : le plus long chemin sans cycle de
A à C est de longueur 2 (A − B − C) or le plus long chemin de A à B n’est pas de longueur 1 mais de
longueur 2 (A − C − B).

les solutions elle-mêmes. Il sera alors nécessaire en outre, soit de produire une méthode qui calcule
une solution à partir des coûts des sous-solutions, soit d’enrichir la table afin de retrouver aisément la
solution optimale produite.
Parmi les applications classiques de la programmation dynamique on compte :
– l’algorithme de Floyd-Warshall, qui calcule le plus court chemin dans un graphe,
– la construction d’arbres binaires optimaux,
– la triangulation d’un polygone,
– le calcul de la distance entre deux chaînes,
– les problèmes d’alignement de séquences,
– etc .
Pour terminer, on notera que l’efficacité de la programmation dynamique peut être améliorée
grâce à la technique de l’évaluation paresseuse où toute valeur est calculée à la demande (c’est à dire
uniquement si on en a besoin).

6.2 Exercices
6.2.1 Séquences et automates.
Exercice 6.1 (♥ La plus longue sous-séquence ascendante).
Les objets sur lesquels on travaille sont des séquences de nombres entiers de longueur strictement
supérieure à 1. Par exemple, une telle séquence (de longueur 9) est : T = (11, 5, 2, 8, 7, 3, 1, 6, 4). On
note de manière générale S = (s1 , . . . , si , . . . , sn ) une séquence de longueur n > 1.
On appelle sous-séquence croissante (SSC) de S une séquence de longueur inférieure ou égale à
n, dont :
– les éléments sont pris de gauche à droite dans S ;
– les éléments croissent strictement de gauche à droite.
Par exemple, (2, 3, 4) et (1, 6) sont des SSC de T = (11, 5, 2, 8, 7, 3, 1, 6, 4). La seconde est particulière,
puisque ses éléments se suivent dans T . On dit qu’elle est une sous-séquence croissante contiguë
(SSCC) de T .
Le but de cet exercice est de trouver la longueur des plus longues SSCC et SSC d’une séquence
quelconque S, notées LSSCC(S) et LSSC(S).
Par exemple, les plus longues SSCC de T sont (2, 8) et (1, 6), donc LSSCC(T ) = 2. Les plus longues
SSC de T sont (2, 3, 4) et (2, 3, 6), donc LSSC(T ) = 3.
L’opération élémentaire de mesure de la complexité est la comparaison.
Question 1 : Montrer que, pour toute séquence S, on a : LSSC(S) ≥ LSSCC(S).
Question 2 : Donner le principe d’un algorithme en Θ(n) pour calculer LSSCC(S).
Question 3 : Pourquoi un algorithme comme le précédent ne permet-il pas de calculer LSSC(S)
avec un algorithme en Θ(n) ?
Question 4 : Pour calculer LSSC(S), on va remplir un tableau à deux lignes et n colonnes. La
première ligne contient si (l’élément de rang i de S), la seconde li , qui est par définition la longueur
de la plus longue sous-séquence de S dont le dernier élément est si . Dans notre exemple :
6.2. EXERCICES 57

i 1 2 3 4 5 6 7 8 9
si 11 5 2 8 7 3 1 6 4
li 1 1 1 2 2 2 1 3 3
Montrer et expliquer la relation de récurrence suivante. Quelle valeur faut-il donner au terme Max
quand on n’a aucun j tel que 0 < j < i pour lequel sj < si ?
l1 = 1
li = 1 + Max lj
0<j<i
sj <si

Question 5 : Ecrire le programme qui calcule LSSC(S) pour toute séquence S de longueur n. Quel
est son ordre de grandeur de complexité ?

La solution est page ??.

Exercice 6.2 (♥ La plus longue sous-séquence commune.).


Définitions
On utilise l’alphabet des 26 lettres du français. On appelle séquence sur cet alphabet toute suite
finie de lettres, par exemple s1 = befddzhh, s2 = kpedzmh, s3 = altitude, s4 = piteux.
On appelle longueur d’une séquence le nombre de ses lettres. Par exemple, la longueur de s1 vaut
8.
On note x(i) la ième lettre de la séquence x. Par exemple, s3 (4) = i.
On note par convention ǫ la séquence de longueur nulle.
On appelle préfixe, noté x[1..i], d’une séquence le début x = x(1) . . . x(i) de cette séquence. Par
exemple bef et befd sont des préfixes de s1 , mais pas bed.
On appelle sous-séquence d’une séquence x une séquence obtenue en enlevant des lettres à x. Par
exemple eh est une sous-séquence de s1 et de s2 . On dit dans ce cas que eh est une sous-séquence
commune à s1 et s2 .

Le problème
Etant donné deux séquences x et y, le problème est de trouver la longueur lssc(x, y) de leur(s)
plus longue(s) sous-séquence(s) commune(s).
Par exemple, lssc(s3 , s4 ) = 3 car ite et itu sont leurs plus longues sous-séquences communes.
Soit |x| = m la longueur de x et |y| = n la longueur de y. On va remplir un tableau LSSC(i, j), avec
i = 0, |x| et j = 0, |y|, dont l’élément courant est la longueur de la plus longue sous-séquence commune
au préfixe de longueur i de x et au préfixe de longueur j de y.
Par exemple, si x = s3 = altitude et y = s4 = piteux, LSSC(6, 1) = 0 et LSSC(5, 2) = 1.

Question 1 : Notons z = z(1) . . . z(k) une plus longue sous-séquence commune à x et y. Montrer
que
(a) Si x(m) = y(n) alors z(k) = x(m) = y(n) et z[1..k − 1] est la plus longue sous-séquence commune
à x[1..m − 1] et y[1..n − 1]
(b) Si x(m) 6= y(n) et z(k) 6= x(m) alors z est une plus longue sous-séquence commune à x[1..m − 1]
et y
(c) Si x(m) 6= y(n) et z(k) 6= y(n) alors z est une plus longue sous-séquence commune à x et y[1..m−1]
Question 2 : En déduire la formule de récurrence générale qui permet de calculer LSSC(i, j) en
fonction de LSSC(i−1, j−1), LSSC(i−1, j) et LSSC(i, j−1) (ne pas oublier de donner son initialisation).
Question 3 : Ecrire le pseudo-code de programmation dynamique qui, étant donné deux séquences,
calcule la longueur de leur(s) plus longue(s) sous-séquence(s) commune(s).
Comment retrouver la (ou une des) plus longue(s) sous-séquence(s) commune(s) en mémorisant des
informations complémentaires dans cet algorithme ?
Comment les retrouver toutes ?
Traiter en exemple les séquences x = abcbdab et y = bdcaba.
58 CHAPITRE 6. PROGRAMMATION DYNAMIQUE.

La solution est page ??

Exercice 6.3 (♥ Distance entre séquences : algorithme de Wagner et Fisher.).


Définitions
On se donne un alphabet X, c’est-à-dire un ensemble fini de lettres dont la taille est notée |X| ;
soit par exemple l’alphabet X = {a, b, c}. Une séquence (ou chaîne, ou mot ) sur X est une suite finie
de lettres de X. La longueur |u| d’une séquence u est son nombre de lettres. Soit par exemple les
séquences u = acbb et v = cab. On a |u| = 4. On appelle préfixe d’une séquence une séquence par
laquelle elle commence. Par exemple, le préfixe de longueur 2 de u est ac.
On note u[i] la lettre numéro i de u. Par exemple : u[3] = b
On note ǫ la séquence de longueur nulle. Cette notion sera utile ici à deux choses : d’abord, le préfixe
de longueur 0 de toute séquence est donc ǫ. Ensuite, étant donnée une séquence u = u1 u2 ...un , on peut
y insérer autant de ǫ que l’on veut sans en changer la signification. On appellera « superséquence »de
u la séquence u avec de telles insertions. Par exemple, si u = bcaa, une superséquence de u est :
ǫbcǫǫaa. Par abus de langage, on dira que la longueur de cette surperséquence est 7.
Soit deux séquences u = u1 u2 ...un et v = v1 v2 ...vm et deux superséquences de u et de v de même
longueur. On appelle « alignement » entre u et v la mise en correspondance lettre à lettre des deux
superséquences. On exige de plus qu’un alignement n’associe pas deux ǫ.
Par exemple, entre les mots u = bcaa et v = acbca, on peut créer l’alignement :

ǫ ǫ b c a a
| | | | | |
a c b ǫ c a

Une formulation alternative de l’alignement entre deux séquences est celle de « trace », dans laquelle
on utilise les séquences sans y insérer de caractère ǫ. Dans l’exemple précédent, la trace correspondante
est :

b c a a

a c b c a

Une trace doit être telle que deux traits d’association entre lettres ne se croisent jamais. Sous cette
contrainte, on peut construire un3 alignement équivalent à une trace et construire de façon unique un
alignement à partir d’une trace.
Un alignement ou une trace peut s’interpréter comme une suite d’opérations élémentaires d’édi-
tion entre séquences : insertions, suppressions et transformations de lettres pour former la seconde
séquence à partir de la première. Dans l’exemple précédent, l’alignement s’interprète comme la suite
de transformations, entre autres possibilités :

1. insertion de a 4. suppression de c
2. insertion de c 5. transformation de a en c
3. transformation de b en b 6. transformation de a en a

Pour donner une valeurs aux coûts des insertions, des suppressions et des transformations, on
définit une matrice δ de nombres réels positifs ou nuls de taille |X| + 1 × |X| + 1. C’est une matrice qui
définit une distance : elle symétrique, de diagonale nulle et vérifiant l’inégalité triangulaire. La valeur
d’un élément de cette matrice peut s’interpréter comme le coût de transformer une lettre en l’autre
ou comme le coût de suppression et d’insertion pour chaque lettre.
Par exemple, sur un alphabet de trois lettres, une telle matrice pourrait être :
3 Parfois plusieurs, mais ils ont la même interprétation.
6.2. EXERCICES 59

δ ǫ a b c
ǫ 0 1 1 1
a 1 0 1.5 1.2
b 1 1.5 0 1.7
c 1 1.2 1.7 0
Dans cet exemple, le coût de suppression de a est 1 (δ(a, ǫ) = 1), le coût d’insertion de b est 1
(δ(ǫ, b) = 1), le coût de transformation de a en c est 1.2 (δ(a, c) = 1.2).
On appelle le coût d’un alignement la somme des coûts élémentaires des opérations qui la consti-
tuent. Dans l’exemple précédent, le coût de l’alignement est donc : 1 + 1 + 0 + 1 + 1.2 + 0 = 5.2
Un autre alignement entre les mots u = bcaa et v = acbca est par exemple, pour un coût de
1.5 + 0 + 1.5 + 1 + 0 = 4 :
b c a ǫ a
| | | | |
a c b c a
Le problème.
Le problème est de trouver le coût d’un alignement optimal, c’est à dire le moins coûteux, entre
deux séquences u et v.
On définit pour cela une matrice ∆(0 : n , 0 : m), de taille (n + 1) × (m + 1), telle que ∆(i, j) est le
coût de l’alignement optimal entre le préfixe de longueur i de u et le préfixe de longueur j de v. On
cherche donc la valeur ∆(|u| + 1, |v| + 1) = ∆(n + 1, m + 1).
Questions.
Question 1 : Comment calculer ∆(0, j) pour j = 1, |v| et ∆(i, 0) pour i = 1, |u| ? Quelle valeur
donner à ∆(0, 0) ?
Question 2 : Cette question vise à établir une relation de récurrence qui calcule ∆(i, j) à partir
de ∆(i, j − 1), ∆(i − 1, j), ∆(i − 1, j − 1), δ(u[i], ǫ), δ(ǫ, v[j]) et δ(u[i], v[j]).
Pour cela, on remarque qu’un alignement peut s’interpréter comme un chemin dans un graphe. Cette
interprétation est illustrée ci-dessous sur l’exemple d’alignement :
ǫ ǫ b c a a
| | | | | |
a c b ǫ c a
Un chemin dans le graphe entre le nœud étiqueté (0/0) et le nœud étiqueté (4/5) représente un
alignement entre les séquences u = bcaa et v = acbca. L’alignement précédent est marquée en gras.

a 4/0 4/1 4/2 4/3 4/4 4/5

a 3/0 3/1 3/2 3/3 3/4 3/5

c 2/0 2/1 2/2 2/3 2/4 2/5

b 1/0 1/1 1/2 1/3 1/4 1/5

0/0 0/1 0/2 0/3 0/4 0/5

a c b c a

(a) Définir dans le cas général un tel graphe pour deux séquences quelconques u et v.
(b) Montrer, en donnant des longueurs calculées à partir de δ aux arcs du graphe, que’un alignement
optimal entre deux mots correspond au calcul d’un plus court chemin dans ce graphe.
(c) Compte tenu de la forme particulière du graphe, donner une relation de récurrence pour calculer
la longueur d’un plus court chemin entre le nœud (0/0) et tout autre nœud de ce graphe.
60 CHAPITRE 6. PROGRAMMATION DYNAMIQUE.

(d) En déduire la relation de récurrence du calcul du coût d’un alignement optimal en remplissant la
matrice ∆(0 : n , 0 : m). Le coût de l’alignement optimal doit être la valeur de ∆(n, m).
Question 3 : Combien y a-t’il d’alignements différents entre deux mots u et v ?
Question 4 : Donner la procédure de Wagner et Fisher WF qui calcule le coût d’un des alignements
de coût minimal entre deux séquences quelconques sur un alphabet Σ, connaissant la matrice δ, avec
une complexité en temps O(nm) (l’opération élémentaire est la comparaison) et en même complexité
pour la taille O(nm) (rappelons que n = |u| et m = |v|).
Question 5 : Montrer que si l’on peut réduire la complexitéé en taille à O(Max(n, m)) est suffi-
sante. Ecrire ce programme (appelé Wagner et Fisher « linéaire », ou WFL).
Question 6 : On prend l’alphabet du français, avec :
– pour toute lettre α : δ[α, ǫ] = δ[ǫ, α] = 2 ;
– si α et β sont deux consonnes ou sont deux voyelles : δ[α, β] = δ[β, α2] = 1 ;
– si α est une consonne et β une voyelle : δ[α, β] = δ[β, α] = 3 ;
Donner la matrice ∆ pour les séquences malin et coquine.
Question 7 : Comment est-il possible de reconstituer un alignement optimal à partir de ∆ ? Dans
l’exemple précédent, cet alignement est unique. Est-ce cas en général (indication :essayer u = rien et
v = est).
Question 8 : Montrer que l’alignement optimal entre les phrases miroir u et v se déduit directe-
ment de celui entre u et v, s’il est unique. Et s’il ne l’est pas ?
Question 9 : Montrer que, puisque δ définit une distance, alors ∆ définit aussi une distance (d’où
le titre de cet exercice).

La solution est page ??

Exercice 6.4 (♥ Dissemblance entre séquences.).

Présentation.
On se donne un alphabet X, c’est-à-dire un ensemble fini de lettres dont la taille est notée |X| ; soit
par exemple l’alphabet X = {a, b, c}. Une chaîne (ou séquence) sur X est une suite finie de lettres de
X telles que les séquences u = acbb et v = cab. On dispose d’une distance d sur X qui se représente
par une matrice de taille |X| × |X|, symétrique, de diagonale nulle et vérifiant l’inégalité triangulaire.
On cherche à calculer à partir de d une association optimale entre deux chaînes sur X.
On appelle « association »entre deux chaînes u = u1 u2 ...un et v = v1 v2 ...vm une suite de couples
(i, j), où i indice une lettre de u et j une lettre de v. Une association entre u et v doit respecter les
contraintes suivantes :
– aucune lettre ne peut être détruite ou insérée : à chaque lettre de u doit en correspondre au
moins une dans v et réciproquement.
– si plusieurs lettres de u (respectivement v) correspondent à une lettre de v (respectivement u),
elles doivent être contigües.
Par exemple, entre les mots u = bcaa et v = acbca, on peut établir, parmi beaucoup d’autres, la
correspondance :

(b, a), (b, c), (b, b), (c, b), (c, c), (a, a)(a, a)
qui se décrit sur les indices :

(1, 1), (1, 2), (1, 3), (2, 3), (2, 4), (3, 5), (4, 5)
ou par la figure qui suit.
De manière plus formelle, une association est une suite de couples d’indices telle que :
1. le premier terme de la suite est le couple (1, 1) ;
2. le dernier terme de la suite est le couple (n, m) ;
3. le terme (i, j) ne peut être suivi que de l’un des trois termes : (i, j + 1), (i + 1, j) ou (i + 1, j + 1).
6.2. EXERCICES 61
b c a a

a c b c a

A chaque couple (i, j) composant une association correspond une valeur de la matrice d : la distance
entre les lettres de rang i et j dans X. Le coût de l’association est défini comme la somme de la valeur
de tous ses couples.
Soit la matrice d suivante :
a b c
a 0 2 1.5
b 2 0 1
c 1.5 1 0
Par exemple, entre les mots u = bcaa et v = acbca, la correspondance vue ci-dessus :

(1, 1), (1, 2), (1, 3), (2, 3), (2, 4), (3, 5), (4, 5)

a le coût de :

d(b, a) + d(b, c) + d(b, b) + d(c, b) + d(c, c) + d(a, a) + d(a, a) = 2 + 1 + 0 + 1 + 0 + 0 + 0 = 4

La dissemblance entre deux chaînes est définie comme le coût de l’association qui a le coût le
plus faible parmi toutes les associations possibles entre ces deux chaînes.
On cherche un algorithme efficace pour la calculer.
Questions.
Question 1 : (3 pts) Quelle est la dissemblance entre les chaînes aaa et a pour la matrice d
donnée ci-dessus ? Entre les mots aab et abb ? Entre les mots ab et bac ? Donner un exemple non
trivial d’un couple de mots de dissemblance nulle.
Question 2 : (4 pts) Formuler le problème comme un algorithme de programmation dynamique,
en expliquant comment s’applique le principe d’optimalité. Proposer une structure de données et
donner la relation de récurrence.
Indication : utiliser le fait que, pour tout couple de séquences, la dernière lettre de la première est
associée avec la dernière lettre de la seconde.
Question 3 : (3 pts) Ecrire le programme en pseudo-code. Quelle est son ordre de grandeur exact
de complexité en nombre de comparaisons ?
Question 4 : (3 pts) Le faire tourner sur u = bcaa et v = acbca pour la matrice d ci-dessus.
Donner les deux associations optimales.
Question 5 : (2 pts) Comment un tel programme pourrait-il servir dans un correcteur orthogra-
phique de traitement de texte ? Que pourrait-on choisir comme valeurs pour remplir la matrice d ?
Donner deux exemples de cas où il fonctionnerait mal.

La solution est page ??.

Exercice 6.5 (♥ Reconnaissance d’un mot par un automate fini non-déterministe.).


On va définir informellement une machine à reconnaître les mots sur un alphabet Σ. Une telle machine,
appelée automate fini non-déterministe (AFN) est un graphe (voir la définition d’un graphe à
l’exercice 4.4, page 40) dont les nœuds sont appelés des états. On supposera pour le moment que
chaque arc d’un AFN porte une lettre de Σ. On définit deux états particuliers appelés initial et final.
Un mot est dit reconnu ou accepté par un AFN s’il existe au moins un chemin (une suite d’arcs)
menant de l’état initial à l’état final dont la suite de lettres forme le mot. S’il n’en existe aucun, il est
refusé.
Dans l’AFN de la figure suivante, l’état initial p est indiqué par une flèche rentrante, l’état final
r par une flèche sortante. L’alphabet est Σ = {a, b, c}. Les mots aaaab, abbccc, par exemple, sont
62 CHAPITRE 6. PROGRAMMATION DYNAMIQUE.

acceptés, les mots aab ou b ne le sont pas. On remarque que le non-déterminisme vient du fait que
deux arcs portant la même lettre partent de l’état p.

a
b
p a q r
b
c

Question 1 : Donner un algorithme de programmation dynamique que décide si un mot donné


est accepté par un AFN donné.
Indication : calculer par récurrence si un préfixe du mot peut se trouver entre l’état initial et un état
quelconque de l’AFN (la définition de ce qu’est un préfixe se trouve à l’exercice ??). On peut voir le
mot vide ǫ comme le préfixe de longueur nulle.
On élargit la définition d’un AFN : les arcs peuvent maintenant porter n’importe quel mot sur Σ∗ , y
compris le mot vide ǫ. Par exemple, dans l’AFN qui suit, les mots abbc ou aabbcc sont acceptés, le
mot abba est refusé.

a
b
p ab q r
ǫ
a c

Question 2 : Est-ce que l’algorithme de la première question peut encore s’appliquer, après mo-
difications ?
On élargit encore la définition d’un AFN : il peut de plus y avoir plusieurs états initiaux et finaux.
Question 3 : Est-ce que l’algorithme de la première question peut encore s’appliquer, après mo-
difications ?

La solution est page ??

6.2.2 Optimisation combinatoire.


Exercice 6.6 (♥ Le voyageur dans le désert).
Un voyageur veut aller d’une oasis à une autre sans mourir de soif. Il connaît la position des puits sur
la route et sait qu’il consomme exactement 1 litre d’eau au kilomètre. Il est muni d’une gourde pleine
à son départ. Quand il arrive à un puits, il peut choisir de remplir sa gourde ; dans ce cas, il en vide le
contenu restant dans le sable et la remplit entièrement au puits. A l’arrivée, il vide aussi ce qui reste
dans la gourde.
Question 1 : Le voyageur veut faire le moins d’arrêts possible. Comment va-t’il choisir les puits
où il doit s’arrêter ?
Indication : montrer qu’une stratégie gloutonne est optimale.
Question 2 : Le voyageur veut verser dans le sable le moins de litres d’eau possible. Comment
doit-t’il choisir les puits où il doit s’arrêter ?
Indication : montrer que la même stratégie gloutonne est encore optimale.
6.2. EXERCICES 63

Question 3 : A chaque puits, y compris celui de l’oasis d’arrivée, un gardien lui fait payer autant
d’unités de la monnaie locale que le carré du nombre de litres d’eau qu’il a versé. Comment doit-t’il
choisir les puits où il doit s’arrêter pour payer le moins possible ?
Indication : montrer sur un exemple que la même stratégie gloutonne n’est plus optimale. Construire
une stratégie par programmation dynamique qui utilise les valeurs
– P(i) : somme minimale payée au total depuis le puits numéro 1 (l’oasis de départ). jusqu’au puits
numéro i, étant donné que le voyageur vide sa gourde au puits numéro i.
– d(i, j) : nombre de kilomètres entre le puits numéro i et le puits numéro j.
– D : volume de la gourde.
Question 4 : Application : une gourde de 10 litres et des puits situés à 8, 9, 16, 18, 24 et 27 km
de l’oasis de départ. L’arrivée est à 32 km.
La solution est page ??
Exercice 6.7 (♥ Arbres binaires de recherche.).
Soit un ensemble d’entiers {x1 , x2 , . . . , xn }, que l’on suppose strictement ordonnés : x1 < x2 . . . < xn .
On sait que l’on peut organiser ces entiers en arbre binaire de recherche (ABR) de nombreuses
manières différentes. Par exemple, pour ∀i = 1, 5, xi = i, deux ABR possibles sont :

4 2

2 5 1 3

1 3 ∗ 4

∗ 5

Fig. 6.3 – Deux ABR possibles pour l’ensemble {1, 2, 3, 4, 5}.

On suppose maintenant que chaque élément xi a la probabilité pi d’être recherché. On peut donc
Pn
appeller coût d’un ABR A la valeur C(A) = 1 pi (di + 1), où di est la profondeur de xi : ce coût
est l’espérance du nombre de tests que l’on doit faire pour trouver un élément existant dans cet ABR.
On cherche à construire l’ABR de coût minimal, connaissant l’ensemble des xi et des pi associées.
Pour les deux exemples précédents, les coûts respectifs valent 3p1 + 2p2 + 3p3 + p4 + 2p5 et
2p1 + p2 + 2p3 + 3p4 + 4p5 .
Question 1 : Montrer que tout sous-arbre de l’ABR de coût minimal est lui-même de coût minimal.
Question 2 : Soit SAG(A) le sous-arbre gauche d’un ABR A, SAD(A) son sous-arbre droit et
N (A) la somme des valeurs pi pour tous les nœuds de A (les feuilles sont des nœuds). Si A est vide,
N (A) vaut 0.
Montrer que
C(A) = C(SAG(A)) + C(SAD(A)) + N (A)
On peut s’aider pour raisonner de l’application de la question 3, pour i = 1, 2, i = 2, 3, puis i = 1, 2, 3.
Question 3 : On remplit un tableau S, tel que, pour chaque couple (i, t) :
i+t−1
X
S[i, t] = pj
j=i

Donner l’algorithme de calcul du tableau C, avec C[i, t] le coût de l’ABR de coût minimal ne contenant
que les éléments {xi , . . . , xi+t−1 } (initialiser par C[1, i] pour tout i).
64 CHAPITRE 6. PROGRAMMATION DYNAMIQUE.

Question 4 : Application sur un exemple. On prend les valeurs suivantes :


i 1 2 3 4 5
xi 1 2 3 4 5
pi 0.05 0.1 0.2 0.15 0.5
Calculer le tableau S et remplir C au moins jusqu’à C[1, 4]

La solution est page ??

Exercice 6.8 (♥ Produit chaîné de matrices.).

Il s’agit de faire le produit des matrices : M1 × M2 × ..... × Mn avec le moins de multiplications


possible.
Les dimensions de ces matrices sont décrites dans un tableau D[0 : n], avec Mi [1 : D[i − 1], 1 : D[i]]
On appelle cout(i, j) le nombre minimal de multiplications pour réaliser le produit de la chaîne
partielle de matrices : Mi × ... × Mi+j . On cherche donc cout(1, n − 1).
Question 1 : Soit les matrices M1 [10, 20], M2 [20, 50], M3 [50, 1] et M4 [1, 100].
Comparer le nombre de multiplications quand on fait les opérations dans l’ordre donné par ce paren-
thésage :
(M1 × (M2 × (M3 × M4 )))
avec le nombre donné par ce parenthésage :

((M1 × (M2 × M3 )) × M4 )

Question 2 : Appliquer le principe d’optimalité pour trouver une récurrence.


Question 3 : Donner le programme. Quelle est son ordre de grandeur de complexité ?
Question 4 : Le faire tourner sur l’exemple des quatre matrices M1 [10, 20], M2 [20, 50], M3 [50, 1]
et M4 [1, 100]
Remarque. Le nombre de façons de faire le produit de n matrices est le nombre de parenthésages
possibles d’une séquence de n éléments. On l’appelle nombre de Catalan C(n). Il est donné par la
récurrence :
n−1
X
C(n) = C(k)C(n − k)
k=1

et se calcule par récurrence comme :


 
1 2n − 2
C(n) = ∈ Ω(4n /n3/2 )
n n−1

La solution est page ??

Exercice 6.9 (♥ Formatage d’un paragraphe.).


Pour un logiciel de traitement de texte, on cherche à disposer la suite des mots qui forment un
paragraphe de manière à laisser le moins de blancs possible.
Pour simplifier, on se donne les règles suivantes :
– Chaque mot a une longueur égale à son nombre de caractères.
– Un signe de ponctuation compte pour une lettre.
– Les lignes commencent par un mot calé à gauche.
– Chaque espace a la longueur d’une lettre.
– On ne peut pas dépasser la longueur d’une ligne.
– Il y a au moins un mot par ligne.
– Les espaces sur la dernière ligne ne comptent pas.
Par exemple, pour des lignes de taille égale à 25 caractères, on a les deux possibilités suivantes, parmi
un grand nombre (les espaces sont représentées par le caractère « = ») :
6.2. EXERCICES 65

Ce=paragraphe=est=formaté Ce=paragraphe=est========
de=deux=façons=========== formaté=de=deux=façons===
différentes.============= différentes.=============

Le premier formatage compte 3 espaces sur la première ligne, qui est complètement remplie, et 13
sur la seconde ligne. Le second compte 10 espaces sur la première ligne et 6 sur la seconde ligne. Ils
comptent tous les deux 16 espaces. Pour éviter ce genre d’égalité, la mauvaise qualité d’un formatage
(son coût ) se mesure par la somme des carrés du nombre d’espaces sur toutes les lignes, sauf la
dernière. Le coût du premier formatage vaut 32 + 132 = 178 et celui du second 102 + 62 = 136. Le
second est donc meilleur que le premier.
Le but de cet exercice est de trouver, pour un texte donné et une longueur de ligne donnée, le
formatage de coût minimal.
Question 1 : Donner deux formatages analogues aux précédents pour le texte « Ce paragraphe
est formaté de deux façons très différentes. »avec une longueur de ligne égale à 25. Calculer
leur coût. Auraient-ils été égaux si on n’avait pas élevé le nombre d’espaces au carré ?
Question 2 : Un algorithme glouton consiste à remplir chaque ligne le plus possible. Qu’en pensez-
vous ?
Question 3 : Donner toutes les façons de formater la phrase « Racine est un musicien. »sur
des lignes de longueur 10.
Question 4 : On note les mots m1 , . . . , mN de longueur long(mi ), et L la longueur de la ligne.
On note cout(i, j) le coût qui résulte de l’écriture des mots mi , . . . , mj sur la même ligne, quand c’est
possible. Si c’est impossible (j < i ou j trop grand), cout(i, j) prend une valeur arbitrairement grande.
On note C(i) le coût optimal de l’écriture de mi , . . . , mN sous la contrainte que mi soit au début
d’une ligne.
Donner la formule de récurrence qui calcule C(i) en fonction de cout(i, j) et C(j) pour j = i, N − 1.
Question 5 : En déduire le pseudo-code d’un programme qui calcule cout(i, j) puis qui calcule le
formatage un texte avec un coût minimal et, finalement, l’écrit.
Question 6 : Faire tourner ce pseudo-code sur l’exemple de la Question 3.
Question 7 : Faire tourner ce pseudo-code sur l’exemple suivant, avec L = 25 :
Ce paragraphe est formaté de deux manières différentes

La solution est page ??

Exercice 6.10 (♥ Découpe d’une planche.).


On dispose d’une planche de longueur entière N que l’on veut découper en n segments de longueurs
Pn
entières l1 , l2 , . . . , ln avec i=1 li = N. Les segments doivent découper la planche de gauche à droite
selon leur indice. Par exemple, si N = 10 et l1 = 2, l2 = 5, l3 = 3, les découpes doivent se faire
aux abcisses 2 et 7 sur la planche. Découper la planche aux abcisses 5 et 8 donnerait trois segments
de même longueur, mais pas dans le bon ordre : c’est interdit. On peut illustrer cette contrainte par
exemple en imaginant que sur la planche est écrit un texte et que la découpe doit donner un segment
par mot.
On peut bien sûr procéder de gauche à droite. Mais on cherche à minimiser un certain coût pour
la découpe, qui est fondé sur le principe suivant : découper en deux un segment de taille m coûte m
unités (il faut transporter le segment de taille m vers la scie). On cherche dans quel ordre pratiquer
les découpes pour minimiser le coût total.
Dans l’exemple précédent, il n’y a que deux manières de faire : soit couper d’abord la planche à
l’abcisse 7, puis à l’abcisse 2, ce qui coûte 10 + 7 = 17 unités, soit procéder en sens inverse, ce qui
coûte 10 + 8 = 18 unités. Ces deux manières peuvent s’exprimer par les parenthésages : (l1 (l2 l3 )) et
((l1 l2 )l3 ) ou par les arbres binaires :

17 b

7 b
l3
66 CHAPITRE 6. PROGRAMMATION DYNAMIQUE.

18 b

2 1

l1 = 2 l2 = 5 l3 = 3 b8
l1

l2 l3

1 2

l1 = 2 l2 = 5 l3 = 3
Question 1 : Donner la récurrence et le pseudo-code du programme qui calculent la découpe
optimale.
Question 2 : Que devient le problème quand la planche est circulaire (un beignet plat), comme
sur l’exemple ci-dessous ?
l3 = 2

l2 = 1

l4 = 3

l1 = 2

La solution est page ??

Exercice 6.11 (♥ Chemin de coût minimal sur un échiquier avec trois déplacements
type et des pénalités.).
On considère un échiquier n × n (n > 1) et on s’intéresse au calcul du meilleur chemin allant de la
case (n, 1) à la case (1, n) sachant que :
1. chaque case a un coût (entier positif, négatif ou nul) et que le coût d’un chemin est la somme
des valeurs des cases qu’il emprunte,
2. un meilleur chemin est un chemin de valeur minimale,
3. on autorise les déplacements suivants :
(i, j) → (i, j + 1) si 1 ≤ i, j + 1 ≤ n
(i, j) → (i − 1, j − 1) si 1 ≤ i − 1, j − 1 ≤ n
(i, j) → (i − 1, j + 1) si 1 ≤ i − 1, j + 1 ≤ n
Exemple.

j= 1 2 3 4
i= 1 8
2 6 7
3 3 4 5
4 1 2 3
6.2. EXERCICES 67

Chemin : (4, 1), (4, 2), (4, 3), (3, 2), (3, 3), (2, 2), (2, 3), (1, 4)
On veut calculer par programmation dynamique la valeur MC[n, 1] qui est le coût associé au (à
un) meilleur chemin allant de la case (n, 1) à la case (1, n).
Question 1 : Connaissant les valeurs associées aux cases de l’échiquier données dans le tableau
C[i, j], établir la formule de récurrence pour calculer MC[i, j], le coût associé au (à un) meilleur chemin
allant de la case (i, j à la case (1, n) avec 1 ≤ i, j ≤ n. Par hypothèse, on supposera que pour tout
échiquier : C[1, n] = C[n, 1] = 0.
Question 2 : Expliciter l’évolution du calcul effectué par l’algorithme mettant en œuvre ces for-
mules en précisant comment on pourra mémoriser le meilleur chemin et le reconstituer.
Question 3 : Donner les valeurs de MC dans le cas du tableau C ci-dessous :
j= 1 2 3 4
i= 1 2 -4 1 0
2 -6 2 -1 3
3 5 -2 -3 3
4 0 10 2 7

La solution est page ??

6.2.3 Dénombrements.
Exercice 6.12 (♥ Nombre de partitions de l’ensemble à N éléments.).
On note S(j, N) le nombre de partitions à j blocs de l’ensemble à N éléments (ou nombre de Stirling).
Par exemple, [(1, 3), (2, 4)] et [(2), (1, 3, 4)] sont deux des sept partitions à deux blocs de l’ensemble à
4 éléments.
Question 1 : Donner toutes les partitions à un, deux, trois et quatre blocs de l’ensemble à 4
éléments.
Question 2 : Quelle est la relation de récurrence générale sur S(j, N) ? Quelles en sont les initia-
lisations ?
Question 3 : Donner le pseudo-code du programme qui calcule S(j, N) pour j = 1, N.

La solution est page ??

Exercice 6.13 (♥ Le problème du franchissement de l’escalier.).

On considère un escalier de m marches que l’on veut gravir exectement avec des sauts, au choix,
de de a1 ou a2 . . . ou ap marches. Le problème est de trouver le nombre N(s, m) de façons différentes
de gravir exactement m marches en s sauts.
Par exemple, pour m = 12 marches et avec des sauts de a1 = 2 ou a2 = 3 ou a3 = 5, on peut
gravir exactement les 12 marches par les suites de sauts suivants.
(2, 2, 2, 2, 2, 2) ou (2, 3, 2, 3, 2) ou (3, 3, 3, 3) ou (2, 2, 3, 5) ou (3, 2, 5, 2), etc.
Question 1 : Donner une façon de gravir exactement les 12 marches en trois sauts.
Question 2 : Donner la formule de récurrence du calcul de N(s, m).
N.B. : le mieux est de l’étudier sur un cas simple. Il faut particulièrement examiner les cas de succès
et d’échec.
Question 3 : Préciser l’évolution du calcul de N(s, m).
Question 4 : Calculer les valeurs N(s, m) pour m = 12, a1 = 2, a2 = 3, a3 = 5.
Question 5 : On remarque dans l’exemple précédent que certaines zones du tableau N valent 0.
Expliquer pourquoi et proposer une amélioration de l’algorithme.

La solution est page ??.


68 CHAPITRE 6. PROGRAMMATION DYNAMIQUE.

Exercice 6.14 (♥ Le jeu avec deux piles de pièces.).


On considère un jeu où on dispose de deux piles de pièces P et Q contenant respectivement p et q
pièces (p, q > 1). Le but est d’atteindre une des deux situations (p = 0, q = 1) ou (p = 1, q = 0),
sachant que l’on passe d’un état des piles au suivant de la façon suivante : on enlève deux pièces à P
(resp. Q) et on en ajoute une à Q (resp. P).
Exemple. (p = 4, q = 6) mène à : (p = 2, q = 7) ou (p = 5, q = 4).
On veut calculer par programmation dynamique le nombre de façons différentes N[p, q] permettant
d’atteindre l’un des des deux états terminaux (p = 0, q = 1) ou (p = 1, q = 0) à partir d’une situation
où le tas P a p pièces et le tas Q en a q.
Question 1 : Donner la formule de récurrence complète exprimant N[p, q].
Question 2 : Préciser l’évolution du calcul permettant d’obtenir N[p, q] (on ne demande pas l’al-
gorithme).
Question 3 : À l’aide d’un tableau calculer N[4, 2] en ne remplissant que les cases utiles.
Question 4 : Que vaut N[3, 3] ?

La solution est page ??

Exercice 6.15 (♥ Déplacements d’un cavalier aux échecs, sous certaines contraintes.).

On s’intéresse aux parcours d’un cavalier sur un échiquier de taille quelconque : combien a-t’il de
façons différentes de partir de la case (1, 1) pour se rendre à la case d’arrivée (n, m) ? On suppose que
n > 4 et m > 3
On impose que le cavalier ne puisse se déplacer qu’en augmentant l’abcisse (le numéro de colonne),
comme sur la figure.

❡ ❡ ❡ ❡ ❡ ❡ ❡ (n, m)

(1, 1) ❡

Question 1 : Soit N(i, j) le nombre de parcours différents partant de la case (1, 1) et menant à la
case (i, j). Essayer pour n = m = 5.
Question 2 : Donner la relation de récurrence générale sur N(i, j). Vérifier pour n = m = 5
Question 3 : Montrer que l’initialisation de la relation de récurrence se fait sur les colonnes 1 et
2, puis, pour chaque colonne, sur les lignes 1 et 2 et sur les lignes m − 1 et m − 2.
Question 4 : Donner le pseudo-code de l’algorithme.

La solution est page ??

6.2.4 Graphes : plus courts chemins et autres.


Le nom de ces algorithmes est le même que dans [FRO90]. Il y a parfois des légères variantes dans
d’autres ouvrages.
6.2. EXERCICES 69

Exercice 6.16 (♥ Algorithme de Floyd.).

Présentation.
L’algorithme de Floyd recherche le plus court chemin dans un graphe entre tous les couples de
sommets. Ceux-ci sont ordonnés de manière arbitraire. La récurrence porte sur le rang des sommets
qui se trouvent sur le chemin le plus court (non compris les sommets extrémités).
Définitions.
Un graphe G = (N, V) est composé d’un ensemble N fini de n nœuds ou sommets et d’un ensemble
V ⊂ N × N d’arcs. Chaque arc peut donc être vu comme un lien d’un sommet vers un autre. Si le
graphe est valué, chaque arc possède une valeur réelle strictement positive, appelée longueur.
On suppose pour ce problème qu’il y a au plus un arc entre deux sommets.
Un graphe valué peut se représenter par une matrice L[1 : n ; 1 : n], telle que L[i, j] est la longueur
de l’arc entre le sommet i et le sommet j. S’il n’y a pas d’arc entre i et j, on donne la valeur +∞
à L[i, j]. On suppose qu’aucun sommet n’a d’arc qui se termine sur lui-même, ce qui permet d’écrire
L[i, i] = 0 et que, en général, L[i, j] 6= L[j, i].
On appelle chemin dans un graphe une suite d’arcs

(n1 , n2 ), (n2 , n3 ) . . . , (ni , ni+1 ), (ni+1 , ni+2 ), . . . (np )

On l’écrit plus simplement :


n1 , n2 , . . . , ni , . . . np
Un chemin est donc composé de p − 1 arcs et passe par p nœuds. Sa longueur est la somme des
longueurs des arcs qui le composent.
Etant donné un graphe, on cherche le plus court chemin entre tout couple de sommets, c’est à
dire celui de longueur minimale.
Application.

15
1 4

30
5 5
50 15
5

2 3
15

On note LCCk [i, j] le plus court chemin entre les sommets ni et nj , sous la contrainte que les
sommets intermédiaires soient de rang inférieur ou égal à k. Par exemple, pour k = 2 les chemins
ni , nj et ni , 2, 1, nj répondent à cette contrainte.
70 CHAPITRE 6. PROGRAMMATION DYNAMIQUE.

Question 1 : Pourquoi le chemin ni , 2, 1, 1, 2, nj , qui répond à la même contrainte, n’a-t’il pas à


être pris en compte ?
Question 2 : Donner LCC1 [i, j] pour l’exemple et dans le cas général.
Question 3 : Donner la formule de récurrence définissant LCCk+1 [i, j] en fonction de la matrice
LCCk .
Indication. Il y a deux cas exclusifs : soit le plus court chemin entre les sommets i et j passe par le
sommet intermédiaire de rang k + 1, soit il n’y passe pas.
Question 4 : Quand arrêter le calcul ?
Question 5 : Donner un schéma de programme pour l’algorithme de Floyd.
Question 6 : Quel est son ordre de grandeur de complexité (l’opération élémentaire est la compa-
raison).
Question 7 : L’appliquer à l’exemple : calculer LCCk pour k croissant.
Question 8 : Que devient cet algorithme s’il peut y avoir plusieurs arcs entre deux sommets ?

La solution est page ??

Exercice 6.17 (♥ Plus court chemins : l’algorithme de Bellman-Ford.).


Cet algorithme recherche le plus court chemin dans un graphe entre un sommet donné et tous les
sommets. La récurrence porte sur le nombre d’arcs du chemin le plus court. Les définitions sont
exactement les mêmes que celles de l’exercice 6.16, à part la suivante : on note ici LCCk [i, j] la
longueur du plus court chemin entre les sommets i et j, comportant au plus k arcs.
Question 1 : Donner la formule de récurrence définissant LCCk+1 [i, j] en fonction de la matrice
LCCk .
Question 2 :
Question 3 :
Commentaire.
Il existe un algorithme plus rapide que celui-ci pour calculer le plus court chemin dans un graphe
entre un sommet donné et tous les sommets. Il s’agit de l’algorithme glouton de Dijkstra (voir l’exercice
??). Cependant, celui qui est présenté ici (algorithme de Bellman-Ford) permet de traiter des graphes
ayant des arcs de longueur négative, à condition qu’ils soient sans boucle de longueur négative (ce que
cet algorithme permet aussi de détecter).

La solution est page ??


Chapitre 7

Algorithmes gloutons

Rappels
Les algorithmes gloutons ou voraces (greedy) ont la particularité est de ne jamais remettre en cause
les choix qu’ils effectuent. La succession des choix ne mène pas forcément à une bonne solution ou à
une solution optimale. Il existe cependant des algorithmes gloutons qui trouvent toujours la solution
cherchée, qu’on appelle exacts. Il faut prouver ou infirmer qu’un algorithme glouton est exact pour
chaque problème traité.
L’avantage des algorithmes gloutons est leur faible complexité. Dans certains problèmes d’optimi-
sation, il est parfois préférable d’obtenir une solution approchée par un algorithme glouton qu’une
solution optimale par un algorithme trop coûteux.

Exercices.
Exercice 7.1 ( ✍✍ Choix d’activités.).

Le problème consiste à répartir une ressource parmi plusieurs activités concurrentes. Soit un en-
semble S = {1, ..., n} de n activités concurrentes souhaitant utiliser une même ressource laquelle ne
peut supporter qu’une activité à la fois. Chaque activité a un horaire de début di et un horaire de fin
fi avec di ≤ fi . Si elle est choisie l’activité i a lieu pendant l’intervalle de temps [di , fi [. Les activités
i et j sont compatibles lorsque di ≥ fj ou dj ≥ fi Le problème consiste à choisir le plus grand nombre
d’activités compatibles entre elles.

Un algorithme glouton résolvant ce problème est indiqué par le pseudo-code suivant. On suppose
les activités triées de manière à ce que

f1 ≤ f2 ≤ ... ≤ fn .

Si tel n’est pas le cas cette liste de n activités peut être triée en O(n.log(n)).

procedure Choix − activites − glouton


debut
A←1
j←1
pour i ← 2 jqa n faire
si di ≥ fj alors
A ← A ∪ {i}
j←i
fait

71
72 CHAPITRE 7. ALGORITHMES GLOUTONS

fin

L’algorithme opère comme suit. L’ensemble A collecte les activités sélectionnées. La variable j
indique l’ajout le plus récent à A. D’après le tri initial des activités, fj = max{fk | k ∈ A}. L’activité
1 est la première sélectionnée, puis l’algorithme ajoute systématiquement à A, la première activité i
compatible avec celles déjà présentes dans A.

Remarque : Choix − activites − glouton est de complexité linéaire.


Question.
Montrer que Choix − activites − glouton donne les solutions optimales pour le problème du choix
d’activités.
D’après [PIN]

La solution est page ??

Exercice 7.2 ( ✍✍ Plus court chemin dans un graphe.). Soit un graphe G = (V, E) et un
nœud particulier s. La longueur (strictement positive) de l’arc entre les nœuds u et v est notée C[u, v].
Elle est mise à une valeur arbitrairement grande s’il n’y a pas d’arc entre u et v. Le problème est de
trouver la longueur des plus courts chemins entre s et tous les nœuds de G de manière gloutonne.
L’algorithme1 maintient un ensemble S pour lesquels la longueur du plus court chemin D(u) entre s
et u (avec u ∈ S) est déjà connue. Il tient aussi à jour pour tous les nœuds un tableau D dont la valeur
D[u] donne la longueur du plus court chemin entre s et u dont tous les nœuds (sauf éventuellement
u si u 6∈ S) sont dans S. Si u ∈ S, la valeur D[u] est définitive.
– Initialement, S = {s} et D[s] = C[s, s].
– Chaque étape consiste à choisir le nœud w de V − S qui minimise la quantité D[w]. D est
réactualisé pour tous les nœuds v ∈ V − S par Min D[w] + C[w, v]) D[v] et w est ajouté à S.
– La procédure se termine quand S est vide.
1: Procedure Dijkstra
2: début
3: /% Initialisation. %/
4: S ← {s}
5: pour v ∈ V faire
6: D[v] ← C[s, v]
7: fin pour
8: /% Progression. %/
9: tant que S 6= ∅∅ faire
10: Choisir le w ∈ V − S qui minimise D[w]
11: pour v ∈ V − S faire
12: si D[w] + C[w, v] < D[v] alors
13: D[v] ← D[w] + C[w, v]
14: fin si
15: fin pour
16: fin tant que
17: fin
Question 1 : Montrer que cet algorithme est correct. Pour cela, montrer d’abord qu’à toute étape,
pour tout nœud v ∈ S, avec v 6= s, D[v] contient la longueur du plus court chemin entre s et v dans
G, et que cette valeur n’a plus à être remise en question.
Question 2 : Appliquer cet algorithme sur le graphe suivant (la longueur de chaque arc est indi-
quée près de son point départ, le nœud de départ est A) :
1 Proposé et démontré par E. Dijkstra en 1959
73
7

6 10
A B
50
5 30
65 4
4
6
3 44
E C
2 70 20
1 23
D
0
1 2 3 4 5 6 7
La solution est page ??

Exercice 7.3 ( ✍✍ Carrés magiques d’ordre impair.).


Remplir un carré magique d’ordre n impair. Les nombres 1 à n2 sont utilisés une fois et une seule. La
somme des lignes, des colonnes et des diagonales vaut la même valeur. Application : n = 5. Il existe
au total 275 305 224 carrés magiques d’ordre 5, on n’en demande qu’un !
La solution est page ??

Exercice 7.4 ( ✍✍ Fusion de fichiers.).


Cet enoncé semble avoir une solution triviale. Il vient de Heuristique, de J. Pearl, traduction française
chez Cépaduès. Exercice 1.3, page 36. Où est l’erreur ? Dans la traduction ? Dans l’original ? Dans ma
compréhension ?
A vérifier.
Deux fichiers triés contenant n et m enregistrements peuvent être fusionnés en un seul fichier trié
en n + m opérations.
Donner une statégie gloutonne de fusion de p fichiers.
Exemple : 5 fichiers de taille 20, 30, 10, 5, 25. Si on fusionne le premier avec le second, on a 50
opérations. Puis le résultat avec le troisième : au total 60. Puis le résultat avec le quatrième : 65. Le
résultat avec le cinquième : au total 90.
Avec cet énoncé, l’ordre n’a pas d’importance. Changer n + m en 2n + m ?

Exercice 7.5 ( ✍✍ Code de Huffman.).

Exercice 7.6 ( ✍✍ Arbres binaires de recherche.).

Reprendre l’exercice 6.7, page 63 et définir un algorithme glouton. Donner un exemple pour montrer
qu’il n’est pas exact.
La solution est page ??

Exercice 7.7 ( ✍✍ Cherchez la star.).


voir le poly de l’ENS, exo 1.7.2
74 CHAPITRE 7. ALGORITHMES GLOUTONS
Chapitre 8

Mélanges

8.1 Un problème d’optimisation particulier : le "sac à dos"


8.1.1 Trois problèmes légèrement différents.
Le sac à dos 0/1

Le problème du sac à dos (« KP » pour Knapsack problem) dans sa version entière (aussi appelée
version 0/1) s’énonce ainsi.
Il s’agit de déterminer le vecteur X = (xi )i=1...n ∈ {0, 1}n qui maximise l’expression
n
X
ui .xi
i=1

sous la contrainte
n
X
wi .xi ≤ W
i=1

pour des valeurs wi , ui et W données. wi et W sont des nombres entiers, ui est réel positif.
Un exemple de problème de sac à dos 0/1 est celui, justement, d’un sac à dos que l’on souhaite
remplir d’objets numérotés de 1 à n. L’objet numéro i a un certain prix ui et pèse le poids wi . On
cherche à charger le sac à dos par le sous-ensemble d’objets de plus grande valeur, dont le poids total
n’excède pas W.

Par exemple, prenons W = 5 et :

i 1 2 3
ui 6 10 12
wi 1 2 3

La solution optimale est Xopt = {0, 1, 1} pour une valeur totale de 22.

Le sac à dos continu

Le problème KP a aussi une version continue. Elle diffère du problème précédent par le fait que
les objets sont supposés « divisibles » et que les valeurs wi sont réelles positives.
C’est à dire que l’on veut trouver le vecteur X = (xi )i=1...n ∈ [0, 1]n qui maximise l’expression
n
X
ui .xi
i=1

75
76 CHAPITRE 8. MÉLANGES
sous la contrainte
n
X
wi .xi ≤ W
i=1
pour des valeurs réelles positives wi , ui et W données.

Un exemple de problème de sac à dos continu est celui de l’édition d’un livre : le brouillon comporte
5 chapitres, chacun d’un certain nombre de pages et d’un certain intérêt. La somme totale des pages
du brouillon est trop grande pour l’éditeur. Quelle nombre de pages doit-on prendre dans chaque
chapitre pour maximiser l’intérêt total du livre ?
Par exemple :
Chapitres Pages Intérêt
1 120 5
2 150 5
3 200 4
4 150 8
5 140 3
En enlevant tout le chapitre 5 et 20 pages du chapitre 3, on obtient un ouvrage de 600 pages (le
maximum demandé par l’éditeur) dont l’intérêt total vaut :
180 0
5+5+4 +8+3 = 21.6
200 140
On a dans cet exemple :
n = 5, W = 600
X = (120, 150, 200, 150, 140)
w1 = 1., w2 = 1., w3 = 0.9, w4 = 1., w5 = 10.
u1 = 5., u2 = 5., u3 = 4., u4 = 8., u5 = 3.
Un autre exemple est celui du chargement d’un sac à dos non pas dans un super-marché (où les
denrées sont pré-emballées par boîtes de 1kg, 2kg, etc.), mais chez un épicier où les produits sont en
vrac (mais cependant en quantité limitée au poids ui ). xi , qui vaut entre 0 et 1, indique la proportion
de la quantité totale du produit que l’on prend (dans le cas binaire, on prend tout ou rien d’un
produit).

Une version intermédiaire


Enfin, il existe une version intermédiaire dite sac à dos binaire (ou entier) à contraintes frac-
tionnaires qui s’énonce comme le sac à dos binaire, sauf que l’on accepte que les valeurs de poids wi
et W soient réelles, alors que les xi doivent toujours valoir 0 ou 1 (on prend tout l’objet ou on ne le
prend pas, mais son poids n’est pas forcément entier).

Résumé
En résumé, la signification et les domaines d’appartenance des variables sont les suivants :

n W wi ui xi
Nombre d’objets Poids maximal Poids d’un objet Valeur d’un objet Proportion de l’objet
dans le sac à dos mise dans le sac à dos

W wi ui xi
sac à dos 0/1 N N R+ {0, 1}
sac à dos intermédiaire R+ R+ R+ {0, 1}
sac à dos continu R+ R+ R+ [0, 1]
8.1. UN PROBLÈME D’OPTIMISATION PARTICULIER : LE "SAC À DOS" 77
8.1.2 Trois solutions extrêmement différentes
Les trois problèmes de sac à dos peuvent évidemment se résoudre brutalement par essais successifs,
en version récursive comme au chapitre 4, ou en version itérative par la technique PSEP (pour Pro-
grammation par Séparation et Evaluation Progressive, ou branch and bound). Mais il y a beaucoup
mieux à faire... dans deux cas sur les trois.

Le sac à dos continu

La version continue a en effet une solution gloutonne exacte. Voir par exemple [JOH04], pages 313
et suivantes pour la démonstration. Elle demande de ranger les chapitres par utilité relative ui /xi
décroissante : dans notre exemple, si ui est l’utilité du chapitre i et xi son nombre de pages, l’utilité
relative du chapitre i est ui /xi . Par exemple, celle du chapitre 4 est : 8/150 = 0.0533. Dans l’ordre
décroissant d’utilité relative, les chapitres sont dans l’ordre : 4, 1, 2, 5, 3. On prend alors le maximum
de pages dans cet ordre, soit 150 du chapitre 4, 120 du chapitre 1, 150 du chapitre 2, 180 du chapitre
3 et 0 du chapitre 5, pour obtenir la solution déjà citée ci-dessus. Elle est optimale.

Le sac à dos 0/1

Pour la version 0/1, cet algorithme glouton ne fournit pas toujours l’optimum. Pour preuve, dans
l’exemple précédent, si le nombre de pages total admis par l’éditeur est 620, elle conduit à prendre
totalement les chapitres 1, 2, 4 et 5, pour un intérêt total de 21. Or, prendre les chapitres 1, 2, 3, 4
donnerait aussi 620 pages, mais avec un intérêt total de 22. On peut démontrer de manière générale
que le problème du sac à dos en version 0/1 n’a pas de solution gloutonne toujours exacte.
C’est la même chose dans l’autre exemple donné plus haut pour le sac à dos 0/1, avec W = 5 et :

i 1 2 3
ui 6 10 12
wi 1 2 3

L’algorithme glouton va donner Xglouton = {1, 1, 0} (valeur totale : 16), alors que la solution
optimale est Xopt = {0, 1, 1} (valeur totale : 22).
En revanche, la version 0/1 peut se résoudre par programmation dynamique.
En effet1 , si on définit B(w, i) comme la valeur optimale du sac à dos de capacité w en ne prenant
en compte que les i premiers articles numérotés, on trouve que :

i
X
∀w, ∀i ≤ n B(w, i) = max xj uj
(xj )j=1...n ∈{0,1}i
j=1
P i
xj .wj ≤W
j=1
 Pi−1


 max (xi ui + max j=1 xj. uj ) si w − wi ≥ 0

 xi ∈{0,1} (xj )j=1...i−1 ∈{0,1}i−1

 i−1
P

 xj .wj ≤w−xi wi
j=1
= Pi−1

 max j=1 xj. uj si w − wi < 0

 (xj )j=1...i−1 ∈{0,1} i−1



 i−1
P
 xj .wj ≤w
j=1

max [xi ui + B(w − xi wi , i − 1)] si w − wi ≥ 0
= xi ∈{0,1}
B(w, i − 1) si w − wi < 0

La condition initiale étant ∀w ≤ W, B(w, 0) = 0 et le problème à résoudre B(W, n).


1 Cette partie est reprise de la page web de E. Rouault, http ://eleves.ensmp.fr/P00/00rouaul/
78 CHAPITRE 8. MÉLANGES
Il est à remarquer que la numérotation choisie pour les objets n’a pas d’importance, car la "tem-
poralité" induite par rapport à une programmation dynamique classique est ici purement factice :
Pi
en réordonnant les termes composant la somme j=1 xj uj , on serait arrivé à une autre expression
strictement équivalente. Cette équivalence s’entend au sens où le maximum sera le même, mais le
vecteur conduisant à ce maximum n’est pas unique.

Avec des mots, l’équation de récurrence se traduit ainsi :


– Soit le ième objet est plus lourd que la capacité du sac, auquel cas, l’optimum est atteint en ne
considérant que les (i − 1) premiers objets
– Soit on peut le prendre et il faut donc prendre une décision
– Si on le prend, le bénéfice vaut la valeur de cet objet plus le bénéfice optimum en ne considérant
que les (i − 1) premiers objets avec une capacité diminuée de la masse de l’objet
– Si on ne le prend pas, le bénéfice vaut le bénéfice optimum en ne considérant que les (i − 1)
premiers objets avec une capacité inchangée
On prend alors le maximum de ces 2 valeurs.

La version intermédiaire

Quant au problème du sac à dos binaire à contraintes fractionnaires, il n’a pas de solution rapide :
on ne peut trouver l’optimum que par essais successifs, en temps au pire d’ordre exponentiel en fonction
du nombre n (il faut essayer au pire les 2n vecteurs binaires).

8.2 Exercices.
Exercice 8.1 ( ✍ ♥ DpR contre Programmation dynamique.).
On reprend le problème du produit chaîné de matrices (page ??). On rappelle qu’il s’agit de réaliser
la multiplication de n matrices rectangulaires M1 × M2 . . . × Mi × . . . Mn en un nombre minimum de
multiplications élémentaires, sachant que le produit de matrices est associatif et que le produit M×M′
des deux matrices M (de dimensions [p, q]) et M′ (de dimensions [q, r]) prend pqr multiplications
élémentaires.
Pour n matrices, les dimensions sont rangées dans le tableau D de taille n + 1, dont les indices
vont de 0 à n. Par exemple, pour les deux matrices M et M′ , on a : D(0) = p, D(1) = q, D(2) = r,
ou plus simplement : D = (p, q, r)

Question 1

On propose le schéma d’algorithme suivant, du type « diviser pour régner », pour n ≥ 3. On


suppose (ce qui est sans importance) que les valeurs dans D sont toutes différentes.
– Choisir i tel que D(i) soit le minimum dans {D(1), . . . , D(n − 1)}.
– Diviser en deux sous-problèmes : effectuer le produit chaîné des matrices M1 × . . . × Mi et
effectuer le produit chaîné des matrices Mi+1 × . . . × Mn
– Continuer récursivement
Question 1 : Ecrire cet algorithme en pseudo-code.
Question 2 : Détailler son fonctionnement pour D = (8, 3, 2, 19, 18, 7).
Question 3 : Combien d’opérations élémentaires effectue-t’il ?

Question 2

Un autre algorithme « diviser pour régner » est le suivant : on choisit i tel que D(i) soit le maximum
dans {D(1), . . . , D(n − 1)} et on divise pareillement en M1 × . . . × Mi et Mi+1 × . . . × Mn .
Répondre aux deux dernières questions précédentes.
8.3. TRAVAUX PRATIQUES. 79

Question 3

Répondre aux deux mêmes questions en prenant l’algorithme de programmation dynamique du


cours. Quelles conclusions générales peut-on tirer de l’application des trois algorithmes à cet exemple ?

Question 4

Quels sont les ordres de complexité minimal et maximal des algorithmes « diviser pour régner »
en fonction du nombre de multiplications élémentaires ?
Quel est l’ordre de complexité exact de l’algorithme de programmation dynamique ?

Anonyme

8.3 Travaux Pratiques.


Exercice 8.2 (TP Le petit commerçant).
On s’intéresse aux différents algorithmes que peut utiliser un commerçant pour rendre la monnaie
avec des pièces. Son problème est d’arriver exactement à une somme N donnée en choisissant dans
sa caisse un multi-ensemble de pièces dont chacune possède une valeur fixée. Par exemple, dans le
système numéraire français, si le client effectue un achat de 8€10 et donne 10€, le problème consiste
pour le commerçant à composer un multi-ensemble de pièces qui totalise 1€90. Il y a un grand nombre
de solutions, parmi lesquelles :
– 1 pièce de 1€, 1 pièce de 50c, 2 pièces de 20c
– 2 pièces de 50c, 4 pièces de 20c, 2 pièces de 5c
– 19 pièces de 10c
– ...
Pour étudier le problème de manière générale, on appelle C l’ensemble des pièces de monnaie (il
y en a n différentes) et on va supposer que le commerçant dispose d’un nombre illimité de chacune
d’entre elles.
On note C = {c1 , . . . , cn }. La pièce ci a pour valeur di .
En France, en ne considérant que les pièces et pas les billets, on a un ensemble C de taille 8, avec
les valeurs :
d1 = 2€, d2 = 1€, d3 = 50c, d4 = 20c, d5 = 10c, d6 = 5c, d7 = 2c, d8 = 1c
Ou, en centimes : d1 = 200, d2 = 100, d3 = 50, d4 = 20, d5 = 10, d6 = 5, d7 = 2, d8 = 1
Pour reprendre l’exemple précédent, la première solution peut se noter par le multi-ensemble :

{c2 , c3 , c4 , c4 }

ou de manière équivalente par un vecteur de dimension n indiquant combien de pièces de chaque type
ont été prises pour la solution :
(0, 1, 1, 2, 0, 0, 0, 0)

Pour achever de définir le problème, on va supposer que le commerçant cherche à redonner le moins
de pièces possibles. On peut donc l’énoncer comme suit :
Le problème "rendre la monnaie de manière optimale" (RLMMO)
On se donne un ensemble C. A chaque élément ci de C est associée une valeur di , un
nombre entier strictement positif. Soit N un nombre entier strictement positif.
Trouver un multi-ensemble S composé d’éléments de C tel que :
– la somme des valeurs des éléments de S vaille exactement N,
– le nombre des éléments de S soit minimum.
S’il n’existe aucun multi-ensemble répondant au premier des deux critères ci-dessus, le
problème est déclaré insoluble.
80 CHAPITRE 8. MÉLANGES
1. Un algorithme très rapide, mais pas toujours exact (glouton)
La méthode employée en général par un commerçant peut se décrire ainsi : utiliser les pièces par
valeur décroissante, en prenant le plus possible de chacune d’elle. C’est cet algorithme "glouton" qui,
dans l’exemple introductif, produit la première solution {c2 , c3 , c4 , c4 }.
Question 1 : Formaliser cet algorithme. Le programmer et le tester.
Question 2 : Montrer que cet algorithme ne résoud pas le problème RLMMO quand C = {c1 , c2 , c3 },
avec d1 = 6, d2 = 4, d3 = 1 et N = 8. Trouver un autre couple (C, N) non trivialement déduit de
celui-ci pour lequel cet algorithme ne marche pas non plus.
N.B. : On peut montrer que cet algorithme résout le problème RLMMO seulement quand C possède
certaines propriétés. En particulier, c’est bien le cas pour le système de pièces français ou pour des
systèmes du type (1, 2, 4, 8, ...). On ne s’intéresse pas ici à ces propriétés.

2. Un algorithme lent, mais toujours exact


Question 3 : Utiliser la méthode des Essais Successifs pour écrire un algorithme qui résolve le
problème pour toutes les valeurs de C et de N.
On donnera comme résultat soit un vecteur de dimension n indiquant combien de pièces de chaque
type ont été prises pour la solution optimale, soit l’affirmation que le problème est insoluble. On
remarquera que les pièces n’ont pas besoin d’être rangées par ordre décroissant. Y-a t’il une façon de
les ranger qui élague efficacement ? Faire des tests.
Question 4 : En donner un ordre de complexité maximal en nombre d’appels récursifs.

3. Un algorithme rapide et toujours exact


Pour simplifier, on cherche ici simplement à trouver le nombre de pièces que comporte la solution
optimale. Les pièces n’ont pas besoin d’être rangées par ordre décroissant. On suppose de plus que la
première pièce vaut une unité : d1 = 1.
On va construire un tableau T (i, j) dont chaque valeur donne le nombre minimal de pièces néces-
saires pour payer la somme j en ne s’autorisant que le sous-ensemble des pièces {c1 , . . . , ci }. Si c’est
impossible, T (i, j) prend une valeur arbitrairement grande. On cherche donc T (n, N).
On remarque que T (i, 0) = 0 pour toute valeur de i de 0 à n.
Question 5 : Montrer que, pour i ≥ 1 et j ≥ di :

T (i − 1, j)
T (i, j) = Min
1 + T (i, j − di )

Que devient cette relation si j < di ?


Question 6 : En déduire un algorithme de programmation dynamique pour trouver le nombre
de pièces que comporte la solution optimale. Quel est son ordre de complexité exact en nombre
d’opérations Min ?
Question 7 : Comment compléter l’algorithme pour savoir quelles pièces sont rendues ?
Question 8 : Que se passe-t’il si d1 6= 1 ?

4. L’algorithme glouton est-il si inexact ?


Question 9 : Choisir un système de pièces où l’algorithme glouton ne fonctionne pas toujours.
Faire des tirages aléatoires (selon une loi à définir) de sommes à rendre, ou les examiner systémati-
quement par ordre croissant jusqu’à une certaine limite. Claculer un rapport moyen entre le nombre
de pièces rendues par cet algorithme et le nombre de pièces optimal. Conclusion ?

Anonyme

Exercice 8.3 (TP L’élément majoritaire.).


8.3. TRAVAUX PRATIQUES. 81

Enoncé du problème

On considère un tableau T [1 : n] de nombres entiers positifs avec n = 2k (k > 0). T [1 : n] est


dit majoritaire s’il existe un entier x tel que : (cardi|1 ≤ i ≤ n et T [i] = x) > n/2 ; x est appelé
élément majoritaire de T et est unique.

Un premier algorithme

On part de l’algorithme ci-dessous :


1: procedure Majoritaire1
2: Début
3: i ← 0 ;
4: tant que (nb ≤ n n
2 et i ≤ 2 ) faire
5: i←i+1
6: nb ← 1
7: j←i+1
8: tant que j ≤ n faire
9: si T [j] = T [i] alors
10: nb ← nb + 1
11: j←j+1
12: fin si
13: fin tant que
14: fin tant que
15: si nb > n 2 alors
16: Ecrire("T [i] est élément majoritaire de T ") ;
17: sinon
18: Ecrire("T n’est pas majoritaire") ;
19: fin si
20: Fin
Question 1 : Que fait cet algorithme ? Donner sa complexité temporelle exacte "au pire", en
nombre de comparaisons (ne pas oublier celles qui sont "cachées" dans les instructions tant que).

Un diviser pour régner efficace

On considère la fonction nboc(x, i, t) qui délivre qui délivre le nombre d’occurrences de x dans le
sous-tableau T [i, i + t − 1].
1: procedure nboc(x, i, t)
2: Début
3: k ← 0;
4: pour j = i, i + t − 1 faire
5: si T [j] = x alors
6: k←k+1
7: fin si
8: fin pour
9: Résultat(k)
10: Fin
On veut calculer pour T [1 : n] le doublet (X, NBX) tel que :
– X = −1, NBX = 0 si T [1 : n] n’est pas majoritaire
– X > 0, NBX > n 2 si X est majoritaire et apparaît NBX fois dans T .
On applique une technique de type "diviser pour régner" travaillant sur deux moitiés de T [1 : n].
Le couple (X, NBX) est alors calculé à partir de (xg, nbxg) (issu de la moitié gauche de T ) et de
(xd, nbxd) (issu de la moitié gauche de T ) en utilisant éventuellement la fonction nboc.
82 CHAPITRE 8. MÉLANGES
Pae exemple :

2 2 2 1 1 3 1 2 3 1 1 1 1 2 1 1
2,1 2,1 2,1 1,1 1,1 3,1 1,1 2,1 3,1 1,1 1,1 1,1 1,1 2,1 1,1 1,1
2,2 -1,0 -1,0 -1,0 -1,0 1,2 -1,0 1,2
2,3 -1,0 1,3 1,3
-1,0 1,6
1,9

Question 2 : Ecrire un programme correspondant à la procédure récursive d’en-tête :


procedure majoritaire(debut, taille, x, nbx)
telle que l’appel :
majoritaire(1, n, X, NBX)
calcule le doublet (X, NBX) recherché.
Question 3 : En donner la classe de complexité temporelle "au pire". Que dire par rapport à celle
établie en 1 ?
Question 4 : Donner le canevas d’un algorithme basé sur un tri efficace résolvant le problème du
tableau majoritaire.
Quelle en serait la complexité temporelle ?
Qu’en conclure ?

Un diviser pour régner encore plus efficace

Afin d’abaisser encore la complexité, on envisage une méthode de type "diviser pour régner" fondée
sur une procédure récursive n’utilisant plus la fonction nboc. Dans ce but, on veut calculer le doublet
(X, NBX) tel que :
– X = −1, NBX = n 2 si T [1 : n] n’est pas majoritaire
– X > 0, NBX > n 2 si nboc(X, 1, n) ≤ NBX et ∀y 6= X, nboc(y, 1, n) ≤ n − NBX ≤ 2
n

fois dans T .
Autrement dit, dans ce second cas, on sait que tout y différent de x n’est pas majoritaire et que x
est peut-être majoritaire. NBX représente le maximum possible du nombre d’occurrences de X.
Exemple.

1 2 1 3 2 2 1 3 1 2 1 1 1 1 3 2
1,1 2,1 1,1 3,1 2,1 2,1 1,1 3,1 1,1 2,1 1,1 1,1 1,1 1,1 3,1 2,1
-1,1 -1,1 2,2 -1,1 -1,1 1,2 1,2 -1,1
-1,2 2,3 1,3 1,3
2,5 1,6
1,9

Toute valeur autre que 1 n’est pas majoritaire et 1 l’est peut-être (en fait ne l’est pas).
Avec le tableau de la question 2, on obtiendrait aussi le couple (1, 9) et dans ce cas 1 est effectivement
majoritaire.
Avec le tableau T = 11212122, on obtiendrait (−1, 4) indiquant que T n’est pas majoritaire.
On considère les deux sous-tableaux contigus T [i, i + t − 1], dans lequel on calcule (xg, nbg) et
T [i + t, i + 2t − 1], dans lequel on calcule (xd, nbd).
Question 5 : Etablir en les justifiant les règles de calcul de (x, nb) pour T [i, i + 2t − 1] à partir de
(xg, nbg) et (xd, nbd), puis écrire le programme correspondant.
Question 6 : Le doublet obtenu ne fournit pas forcément la réponse désirée. Quel traitement
complémentaire proposer pour aboutir à la réponse finale ? Quelle est alors la complexité du traitement
global ?
8.3. TRAVAUX PRATIQUES. 83

Question 7 : Le passage à un tableau de taille quelconque vous semble-t-il aisé ? Que dire de
l’utilisation d’une technique de type "bourrage" avec des éléments "fantômes" ?
N.B. : une solution pour un tableau de taille quelconque est donnée dans [KLE06], exercice 5.3, page
246.
D’abord, une procédure de base appelée elimine sur un tableau de taille m quelconque.
(a) Considérer les éléments deux par deux : T [1] avec T [2], T [3] avec T [4], etc. Si m est impair, T [m]
reste seul.
(b) Pour chaque paire telle que les deux éléménts n’ont pas la même valeur, supprimer les deux
éléments.
(c) Pour chaque paire dont les deux éléments ont la même valeur, en conserver un.
(d) Conserver T [m] pour m impair.
On remarque ensuite que s’il existe un élément majoritaire dans un tableau de taille m, alors le tableau
après la procédure elimine aura aussi cet élément comme majoritaire.
On applique successivement elimine sur T de taille n jusqu’à ce que le résultat ait une taille de 1. Si
le tableau de départ a un élément majoritaire, c’est forcément cet élément-là qui est dans le tableau
de taille 1. Il suffit de le compter dans le tableau T .
Anonyme
Exercice 8.4 (TP La triangulation d’un polygone convexe.).

On se donne les n sommets d’un polygone convexe du plan2 et une mesure de la distance séparant
chaque couple de sommets. Le problème consiste à sélectionner un ensemble de cordes (segments
joignant deux sommets non adjacents) tel qu’il n’y ait aucune intersection de cordes et que le polygone
entier soit divisé en triangles. La longueur totale (la somme des distances entre les extrémités de
toutes les cordes choisies) doit être minimale. On appelle un tel ensemble de cordes une triangulation
minimale.
Exemple
La figure suivante montre un heptagone et les coordonnées (x, y) de ses sommets. Les lignes en
pointillé représentent une triangulation, qui n’est d’ailleurs pas minimale. Sa longueur totale est la
somme des longueurs des cordes (s0 , s2 ), (s0 , s3 ), (s0 , s5 ) et (s3 , s5 ) soit :
p p p p
82 + 162 + 152 + 162 + 222 + 22 + 72 + 142 = 77.56

s2 (8,26) s3 (15,26)

s4 (27,21)
s1 (0,20)

s5 (22,12)
s0 (0,10)

s6 (10,0)

2 Un polygone du plan est convexe si et seulement si les n − 2 sommets restants sont du même côté de la droite

construite sur deux sommets consécutifs, et ceci pour toute paire de sommets consécutifs.
84 CHAPITRE 8. MÉLANGES
Nous supposerons par la suite que le contour du polygone étudié a n sommets classés par ordre de
parcours rétrograde (contraire du sens trigonométrique) notés s0 , s1 , ..., sn−1 .
Questions préliminaires :
Question 1 : Combien peut-on tracer de cordes dans un polygone convexe à n sommets ?
Question 2 : Montrer par récurrence que toutes les triangulations d’un polygone convexe à n
sommets (avec n ≥ 3) ont le même nombre de cordes et le même nombre de triangles.
N.B. : On ne demande pas le nombre de triangulations différentes d’un polygone.
On cherche tout d’abord à résoudre le problème par une méthode de type essais successifs.
Questions :
Question 3 : On supposera que les cordes déjà tracées sont stockées dans un tableau. Ecrire une
fonction validecorde(i, j) qui rend VRAI si la corde joignant les sommets si et sj n’a pas déjà été
tracée et si elle ne coupe aucune corde déjà tracée.
Question 4 : On considère un algorithme par essais successifs basé sur la stratégie suivante : à
l’étape i, on trace l’une des cordes valides issues de si ou on ne trace rien. Montrer que cette stratégie
est mauvaise pour deux raisons : elle calcule plusieurs fois certaines triangulations elle ne permet pas
toujours d’obtenir toutes les triangulations.
Question 5 : On veut pouvoir construire chaque triangulation une fois et une seule.
– En supposant disponible le vecteur des cordes C tel que C[i] contient les numéros des sommets de
départ et d’arrivée de la ieme corde parmi toutes celles possibles ainsi que sa longueur, proposer
une stratégie par essais successifs. Ecrire le programme correspondant.
– Donner l’ordre de grandeur de complexité de cet algorithme en nombre d’appels récursifs.
– Proposer une (des) amélioration(s) pour déterminer qu’une branche de l’arbre des appels récursifs
ne peut plus mener à une solution.
Deuxième partie
On envisage maintenant une solution de type programmation dynamique, dont la récurrence s’ap-
puie sur les remarques suivantes :
– Dans toute triangulation d’un polygone convexe ayant n sommets numérotés comme indiqué
ci-dessus, il existe un triangle qui utilise le côté entre les sommets 1 et n.
– Si on enlève le côté entre les sommets 1 et n, on se retrouve avec deux problèmes de même
nature, mais de taille strictement inférieure.
On peut maintenant construire une récurrence.
On définit le sous-problème de taille t débutant au sommet si , noté (Ti , t) comme la triangulation
minimale du polygone si , si+1 , ..., si+t−1 , formé par la suite des sommets du contour original débutant
en si et continuant dans l’ordre rétrograde. La corde du polygone initial formant une arête de Ti , t
est (si , si+t−1 ). Pour résoudre (Ti , t), nous considérons les trois options suivantes :
– Nous pouvons choisir le sommet si+t−2 pour former un triangle avec les cordes (si , si+t−2 ) et
(si , si+t−1 ) et le troisième côté (si+t−2 , si+t−1 ) puis résoudre le problème (Ti , t − 1).
– Nous pouvons choisir le sommet si+1 pour former un triangle avec les cordes (si , si+t−1 ) et
(si+1 , si+t−1 ) et le troisième côté (si , si+1 ) puis résoudre le problème (Ti+1 , t − 1).
– Pour tout k entre 2 et t − 3, nous pouvons choisir le sommet si+k et former un triangle de côtés
(si , si+k ), (si+k , si+t−1 ) et (si , si+t−1 ) puis résoudre les sous-problèmes (Ti , k+1) et (Ti+k , t−k).
Comme la "résolution" de tout sous-problème de taille inférieure ou égale à deux ne demande
aucune action, il est possible de résumer en disant que si l’on choisit un k entre 2 et t − 2, on est
conduit à résoudre les sous-problèmes (Ti , k+1) et (Ti+k , t−k). La figure suivante illustre cette division
en sous-problèmes.
(Ti+k , t − k) • Si+t−1
8.3. TRAVAUX PRATIQUES. 85

Si+k • (Ti, t)

(Ti , k + 1) • Si

Il convient de préciser que cette stratégie est à la fois valide (on examine toute triangulation
possible) et minimale (on examine chaque triangulation une seule fois).
Questions :
Question 6 : Etablir la formule de calcul d’une triangulation minimale d’un polygone convexe de
n côtés sous forme d’une récurrence complète.
Question 7 : Proposer un algorithme de type programmation dynamique découlant de la formule
précédente. Ecrire le programme correspondant.
Question 8 : Donner la complexité spatiale de l’algorithme, ainsi que sa complexité temporelle en
terme de nombre de comparaisons (de valeurs de triangulations).
Question 9 : On constate que, dans cette stratégie, deux sous-problèmes ont toujours un sommet
commun. Expliquer en quoi cette façon de décomposer est intéressante du point de vue des trian-
gulations considérées. Quelles modifications devraient être apportées à l’algorithme précédent si la
décomposition se faisait en tirant deux cordes quelconques ?
Compléments
Question 10 : L’algorithme de programmation dynamique minimise la longueur totale tracée (la
somme des distances entre les extrémités de toutes les cordes choisies).
a) Montrer qu’il minimise en même temps la somme des périmètres des n − 2 triangles tracés dans le
polygone.
b) Soient a, b et c les longueurs des côtés d’un triangle. On considère la valeur r = |a−b|+|b−c|+|c−a|.
Que vaut r pour un triangle équilatéral ? Pour un triangle plat ? On dira que r mesure "l’irrégularité"
d’un triangle.
c) Montrer que l’algorithme peut être facilement modifié pour trouver la triangulation d’un polygone
minimisant la somme des irrégularités des triangles. Généraliser ce résultat à la minimisation de la
somme de toute fonction positive calculée sur un triangle.
d) Que devient l’algorithme si cette fonction est l’aire ?
Question 11 : On a donné la définition suivante : Un polygone du plan est convexe si et seulement
si les n − 2 sommets restants sont du même côté de la droite construite sur deux sommets consécutifs,
et ceci pour toute paire de sommets consécutifs. En déduire un algorithme qui vérifie si un polygone
défini par les coordonnées de ses sommets consécutifs est convexe.

Anonyme
86 CHAPITRE 8. MÉLANGES
Chapitre 99

Exercices en cours de rédaction.

8 mars 2007

Complexité
Exercice 99.1 (♥ Recherche dans le noir).
Vous êtes dans le noir face à un mur qui s’étend indéfiniment à gauche et à droite. Il y a une porte
dans ce mur, mais vous ignorez où. Tout ce que pouvez faire est d’explorer le mur en marchant jusqu’à
vous trouver devant la porte.
Question 1 : Donnez un algorithme qui vous permet de trouver la porte en faisant un nombre de
pas en O(n), où n est le nombre de pas qui vous sépare de la porte (évidemment, vous ne connaissez
ni la valeur de n, ni si la porte est à gauche ou à droite).
Question 2 : Quelle est la constante multiplicative entière la plus précise dans ce O(n) ?

La solution est page ??

Exercice 99.2 (♥ Cherchez la star.).


Dans un groupe de n personnes, une star est une personne que tout le monde connaît et qui ne
connaît personne. Vous êtes extérieur au groupe et vous cherchez une star dans le groupe, s’il y en a
une. La seule opération élémentaire qui vous est autorisée est de choisir une personne i du groupe et
de lui demander si elle connaît la personne j du groupe. Les réponses sont obligatoires et sincères.
Comme il y a n(n − 1)/2 paires de personnes, le problème est certainement résolu par n(n − 1)
opérations élémentaires. Evidemment, vous cherchez à poser le moins de questions possibles.
Question 1 : Montrez que s’il y a une star dans le groupe, elle est unique.
Question 2 : L’opération élémentaire se note (i → j) et la réponse est VRAI ou FAUX. Montrez,
en l’absence d’autre information, que quand vous posez la question (i → j) :
(a) si la réponse est VRAI, alors j peut être la star, mais pas i ;
(b) si la réponse est VRAI, alors i peut être la star, mais pas j.
Question 3 : Décrire un algorithme en O(n) opérations élémentaires pour trouver la star ou pour
prouver l’absence de star dans un groupe de n personnes.
Indication : Dans une première passe, posez une question à chaque personne. A la fin, vous connaîtrez
une personne qui peut être la star et qui est la seule à pouvoir être la star. Démontrez la justesse du
calcul que vous faites.
Dans une seconde passe, réinterrogez chaque personne, puis terminez par d’autres questions.
Question 4 : Donnez le pseudo-code de cet algorithme.
Question 5 : Donnez une borne supérieure exacte du nombre d’opérations élémentaires.

La solution est page ??

87
88 CHAPITRE 99. EXERCICES EN COURS DE RÉDACTION.
Diviser pour régner
Exercice 99.3 (♥ Quicksort).
Blabla.
Question 1 :
Question 2 :

La solution est page ??

Exercice 99.4 (♥ La valeur manquante).

On dispose un tableau T de taille n contenant tous les entiers de {0, ..., n}, sauf un. On veut
déterminer quel est l’entier absent de T .
Question 1 : Donner un algorithme qui résout le problème en temps linéaire (l’opération élémen-
taire est l’addition).
Question 2 : On prend une autre technique : on commence par trier le tableau T par valeur crois-
sante. Montrer que le problème peut alors se résoudre par dichotomie. Quelle est alors la complexité
(l’opération élémentaire est ici la comparaison) ?
Question 3 : Peut-on améliorer le premier algorithme quand le tableau est trié ?
Question 4 : Reprendre les questions quand le tableau contient tous les entiers de {0, ..., n + 1},
sauf deux.

Anonyme

Exercice 99.5 (♥ Les œufs par les fenêtres.).

Quand on laisse tomber un œuf par la fenêtre d’un immeuble, il peut se casser ou non : cela dépend
de la hauteur de la chute. On cherche à connaître la résistance des œufs, c’est à dire à partir de quel
nombre f d’étages un œuf se casse si on le laisse tomber par la fenêtre. Il est entendu que tous les œufs
sont identiques et qu’un œuf se casse toujours s’il tombe d’un étage de rang supérieur ou égal à f et
jamais s’il tombe d’un étage de rang inférieur à f.
Quand un œuf tombe sans se casser, on peut le ramasser et le réutiliser. S’il est cassé, on ne peut
plus s’en servir.
Etant donné un immeuble de n étages et un certain nombre k d’œufs, on cherche à trouver f. Si
le dernier étage n’est pas assez haut pour briser cette sorte d’œufs, on écrira que f = n + 1.

Une première technique


Question 1 : On ne dispose que d’un œuf (k = 1). Donner un algorithme en O(n) pour trouver f.

Question 2 : On suppose maintenant que k = 2. Donner un algorithme en O( n) pour trouver
f. Donner son déroulement sur l’exemple n = 25, h = 23.

Question 3 : On suppose maintenant que k = 3. Donner un algorithme en O( 3 n) pour trouver
f.

Question 4 : Donner un algorithme en O( k n) pour trouver f quand on possède k œufs. Prouver
son exactitude et sa complexité par récurrence.

Une seconde technique


Réponse 5 : On suppose maintenant que k ≥ ⌈log2 (n)⌉. Décrire et donner le pseudo-code d’un
algorithme qui calcule f en O(log2 (n)) lancers.
Montrer que sa complexité exacte au pire est de ⌈log2 (n)⌉ lancers.
Réponse 6 : Que peut-on faire si k = ⌈log2 (n)⌉ − 1 ? Décrire un algorithme dont la complexité
exacte au pire est de (k − 1) + 2 lancers.
n
Réponse 7 : D’une manière générale, si k < ⌈log2 (n)⌉, proposer un algorithme en O(k + 2k−1 )
lancers.
89

Moralité

Réponse 8 : Comparer les deux techniques

Anonyme

Exercice 99.6 (♥ Nombre d’inversions dans une permutation).


Le but de cet exercice est de construire un algorithme rapide pour compter le nombre des inversions
dans une permutation. Pour fixer les idées, on travaillera sur les permutations des n premiers nombres
entiers, donc les nombres de 1 à n. Une permutation est rangée dans le tableau T [1 : n] et on dit que
les nombres i et j, tels que 1 ≤ i < j ≤ n forment une inversion si T [i] > T [j].
Par exemple, pour n = 8, le nombre d’inversions de la permutation suivante vaut 13 :

i 1 2 3 4 5 6 7 8
T [i] 3 5 2 8 6 4 1 7

Ecrites comme la liste de deux termes (T [i] , T [j]), avec i < j et T [i] > T [j], les inversions sont les
suivantes :
(3,2) (3,1) (5,2) (5,4) (5,1) (2,1)
(6,4) (6,1) (4,1) (8,6) (8,4) (8,1) (8,7)

Question 1 : Ecrire un algorithme naïf qui donne le nombre d’inversions dans une permutation.
Donner l’ordre de grandeur de complexité de cet algorithme (l’opération élémentaire est la comparai-
son).
On va supposer maintenant que n = 2k , pour un certain k entier supérieur ou égal à 2. On peut
partitionner, dans l’exemple précédent, les inversions en trois groupes :
– Celles qui ont les deux termes dans la première moitié de T . Par exemple : (5, 2).
– Celles qui ont les deux termes dans la seconde moitié de T . Par exemple : (4, 1).
– Celles qui ont le premier terme dans la première moitié de T et le second dans la seconde moitié.
Par exemple : (5, 4).
Question 2 : Ecrire un algorithme Diviser pour Régner fondé sur la remarque précédente pour
calculer récursivement le nombre d’inversions. Donner l’ordre de grandeur de complexité de cet algo-
rithme.
Supposons maintenant que chaque moitié de T soit triée par ordre croissant. T pourrait être par
exemple :

i 1 2 3 4 5 6 7 8
T [i] 2 3 5 8 1 4 6 7

Montrer que dans ce cas, on arrive à compter les inversions en un temps linéaire.
Question 3 : Déduire de la remarque précédente un algorithme Diviser pour Régner qui trie T et
compte les inversions dans T avec un ordre de grandeur de complexité meilleur que le précédent.
Question 4 : Faire tourner cet algorithme sur l’exemple présenté au début. Dans quel ordre les
inversions sont-elles comptées ?
Question 5 : On dit qu’on est en présence d’une inversion forte quand i < j et T [j] > 2T [j].
Donner un algorithme de même ordre de grandeur de complexité qui compte les inversions fortes dans
une permutation.

[KLE06], pages 221-225

Exercice 99.7 (♥ Le pic).


On dispose d’un tableau de n entiers T tous différents, qui possède la propriété d’avoir un seul pic
à la position p c’est à dire que les valeurs T [1] à T [p] sont croissantes, et celles de T [p] à T [n] sont
décroissantes.
Question 1 : La définition précédente vaut pour 1 < p < n. L’étendre à 1 ≤ p ≤ n.
90 CHAPITRE 99. EXERCICES EN COURS DE RÉDACTION.
Question 2 : Donner un algorithme Diviser pour Régner qui trouve la position du pic. Quel est
son ordre de grandeur de complexité (l’opération élémentaire est la comparaison).

[KLE06], pages 242-244

Exercice 99.8 (♥ Minimum local dans un arbre).


On dispose d’un arbre binaire complet avec n nœuds (on peut donc poser n = 2d − 1, avec d entier).
A chaque chaque nœud est affectée une valeur réelle différente. Un nœud est un minimum local s’il
est plus petit que son père et que tous ses fils.
Question 1 : Montrer qu’il y a toujours un minimum local dans un tel arbre.
Question 2 : Donner un algorithme qui trouve un minimum local avec une complexité temporelle
en Θ(d). L’opération élémentaire est la comparaison des valeurs réelles affectées aux nœuds.

[KLE06], pages 248 (N° 6

Exercice 99.9 (♥ Enveloppe convexe).

Question 1 :
Question 2 :
Question 3 :

Anonyme

Exercice 99.10 (♥ Le meilleur intervalle).


On dispose d’un tableau T [1 : n] de valeurs positives réelles. Il existe deux indices i etj, définissant
l’intervalle [i, j], avec 1 ≤ i < j ≤ n, tels que la valeur T [j] − T [i] soit maximale. On cherche cette valeur
maximale (la valeur du meilleur intervalle). Si le tableau est monotone décroissant, cette valeur est
négative ou nulle : on impose alors qu’elle soit nulle.
Par exemple, le tableau T comporte les valeurs quotidiennes de l’action de la société Machin le
mois dernier. Vous vous demandez aujourd’hui quel aurait été votre gain optimal en achetant une
action puis en la revendant au cours du mois dernier. Comme vous êtes un optimiste, vous considérez
qu’une perte équivaut à un gain nul.
Question 1 : Donner un algorithme naïf pour résoudre ce problème. Quel est son ordre de grandeur
de complexité (l’opération élémentaire est la comparaison) ?
Question 2 : Donner un algorithme Diviser pour Régner pour résoudre ce problème. On pourra
dans un premier temps supposer n = 2d , avec d entier, puis on indiquera comment relâcher cette
hypothèse. Quel est l’ordre de grandeur de complexité ?
Commentaire. L’exercice 99.15, page 94, améliore encore ce résultat.

Anonyme

Exercice 99.11 (♥ Multiplication de polynômes).


On a vu à l’exercice ?? une première manière d’utiliser la technique Diviser pour Régner pour multi-
plier deux polynômes. Celle-ci est en théorie encore plus efficace. Elle s’applique aux polynômes de la
variable complexe, à coefficients complexes, donc en particulier aux polynômes classiques.
Soit PN (x) = a0 + a1 x + . . . + ak xk . . . + aN−1 xN−1 un polynôme PN de degré inférieur ou égal à
N − 1. Les coefficients ak et la variable x sont des nombres complexes.
2iπk
Soit ΩN = (1, ω1 , . . . , ωk , . . . , ωN−1 ), avec ωk = e N , la suite des racines complexes N-ièmes
de l’unité. On suppose que N est une puissance entière de 2.
On note yk = P(ωk ) la valeur de PN pour ωk et YN = (y0 , . . . , yN−1 ) la suite des valeurs que
prend PN sur ΩN .
On admet pour le moment le résultat suivant : il est possible de reconstituer sans ambiguïté PN
(c’est à dire de calculer les valeurs ak ) si l’on connaît seulement YN (on reviendra sur ce résultat à la
question ??). Autrement dit, si deux polynômes de degré inférieur ou égal à N prennent les mêmes
valeurs sur les racines N-ièmes de l’unité, ces deux polynômes sont identiques.
91

On s’intéressera d’abord à un algorithme de reconstruction de PN à partir de YN , puis à la com-


plexité du calcul des valeurs YN avant d’en déduire un algorithme rapide de multiplication de poly-
nômes. On reviendra pour finir sur le résultat admis en prémisse.

Reconstruction de PN à partir de YN .
e o e
On note YN = (y0 , y2 , . . . , yN−2 ) et YN = (y1 , y3 , . . . , yN−1 ). On définit PN (x) comme le polynôme
N e o
de degré inférieur ou égal à 2 − 1 que l’on peut reconstituer à partir de YN . De même, PN (x) est le
N o
polynôme de degré inférieur ou égal à 2 − 1 que l’on peut reconstituer à partir de YN .
N/2 N/2 2iπ
Question 1 : Montrer que PN (x) = 1+x2 PN e
(x) + 1−x2 PN o
(xe N ).
Indication : Il suffit de montrer que cette égalité est vraie pour toutes les racines N-ièmes de l’unité.
Le mieux est de le faire en deux temps : pour les racines paires, puis impaires.
Question 2 : En déduire que l’on peut reconstituer PN à partir de YN avec une complexité en
O(NLog(N)). L’opération élémentaire est la multiplication de deux nombres complexes.

Calcul de YN connaissant PN .
On s’intéresse maintenant à la complexité du calcul de YN connaissant PN .
Question 3 : Montrer qu’il existe deux polynômes R et S de degré inférieur ou égal à N/2 − 1 tels
que que PN (x) = R(x2 ) + xS(x2 ).
Question 4 : En déduire que l’on peut calculer YN à partir de PN avec une complexité en O(NLog(N)).

Multiplication de deux polynômes


Et maintenant, on multiplie deux polynômes !
Question 5 : Montrer que l’on peut multiplier deux polynômes P N (x) et Q N (x) de degré inférieur
2 2
ou égal à N
2 − 1 par la séquence des opérations suivantes :
– Evaluer P N et Q N sur ΩN .
2 2
– En déduire l’évaluation YN sur ΩN du polynôme produit P N Q N .
2 2
– Reconstituer P N Q N à partir de YN .
2 2
Question 6 : En déduire un algorithme de multiplication de P N et Q N en O(NLog(N)).
2 2
Question 7 : Généraliser au cas où P et Q sont de degrés quelconques.

Reconstitution d’un polynôme par des techniques moins efficaces.


La formule de Lagrange :
N−1
X Y x − ωm 
P(x) = yn
ωn − ωm
n=0 0≤m≤N
m6=n

permet de démontrer facilement que l’on peut reconstituer PN à partir de YN . Mais elle n’est pas
efficace du point de vue complexité.
Une version itérative de la formule de Lagrange, due à Newton, permet d’accélérer le calcul.
yN−1 − Q(ωN−1 )
P(x) = Q(x) + U(x)
U(ωN−1 )
où Q est le polynôme défini par Q{ω0 , . . . , ωN−2 } = (y0 , . . . , yN−2 ) (c’est à dire qu’il prend les valeurs
y0 à yN−2 sur les N − 1 premières racines de l’unité) et U(x) = (x − ω0 )(x − ω1 ) . . . (x − ωN−2 ).
Question 8 : Quelle est la complexité de calcul de la multiplication de deux polynômes P N (x) et
2
Q N (x) de degré inférieur ou égal à N
2 − 1 si on utilise la formule de Lagrange au lieu de la technique
2
vue ci-dessus ?
Question 9 : Même question si on utilise la formule de Newton.
La solution est page ??
Exercice 99.12 (♥ Le dessin du skyline ).
92 CHAPITRE 99. EXERCICES EN COURS DE RÉDACTION.

4 4
3 3
2 2
1 1
0 0
0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8
Figure 1 : Figure 2 :
Trois immeubles. Le skyline correspondant.

On s’intéresse au dessin du skyline d’un centre-ville construit avec des immeubles rectangulaires,
au bord de la mer. Le skyline est la ligne qui sépare le ciel des immeubles, quand on se place assez
loin en mer et qu’on regarde en direction de la ville. Les deux premières figures montrent la projec-
tion à deux dimensions d’un centre-ville composé de trois immeubles et la seconde figure donne le
skyline correspondant.
On suppose que toutes les dimensions sont entières, et que les immeubles sont construits entre les
coordonnées horizontales 0 et n.

1. Une première approche


Un skyline est représenté dans cette première partie par un tableau S[1 : n], dont la composante
i indique la hauteur du skyline entre les abcisses i − 1 et i. Pour l’exemple ci-dessus, le skyline se
représente donc par :

i 1 2 3 4 5 6 7 8
S[i] 0 1 3 1 1 0 2 2
On choisit de représenter un immeuble par le skyline qu’il aurait s’il était tout seul. La représentation
du troisième immeuble (le plus à droite) est donc :

i 1 2 3 4 5 6 7 8
I3 [i] 0 0 0 0 0 0 2 2

Un algorithme itératif de construction.


On suppose connaître le skyline S1,m−1 d’un ensemble de m − 1 immeubles. On ajoute un nouvel
immeuble Im , comme sur l’exemple ci-dessous.

4 4
3 3
2 2
1 1
0 0
0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8
Figure 3 : Figure 4 :
Ajout d’un immeuble. Le nouveau skyline.

Question 1 : Donner un algorithme pour calculer S1,m à partir de S1,m−1 et de Im .


Question 2 : Quelle est la complexité exacte en nombre de comparaisons du calcul itératif de
S1,m , à partir de I1 , . . . , Im , en fonction de n et m ?
93

Un algorithme récursif.
On suppose connaître le skyline S1,p des immeubles I1 à Ip d’une part, et le skyline Sp+1,m des
immeubles Ip+1 à Im d’autre part.
Question 3 : Donner un algorithme pour calculer le tableau S1,m à partir des tableaux S1,p et
Sp+1,m .
Question 4 : Quelle est sa complexité exacte en nombre de comparaisons ?
Question 5 : Formuler le raisonnement par récurrence qui permet de construire S1,m .
Donner un algorithme Diviser pour Régner utilisant une procédure récursive fondée sur ce raisonne-
ment, que l’on formulera comme : SKY(p, q, S), avec 1 ≤ p ≤ q ≤ n. Cette procédure doit calculer
le skyline correspondant aux seuls immeubles Ip à Iq et mettre le résultat dans S. Donner son appel
depuis le programme principal.
N.B. : Le mieux est d’utiliser trois tableaux auxiliaires de taille n : S1 , S2 et S.
Question 6 : Quelle est son ordre de grandeur de complexité exact en nombre de comparaisons ?

2. Seconde partie
Un skyline est représenté dans cette seconde partie par un tableau T , qui décrit de gauche à droite
les coordonnées et les hauteurs caractéristiques.
Pour l’exemple donné en Figure 1, le skyline se représente par :

T = (1 , 1 , 2 , 3 , 3 , 1 , 5 , 0 , 6 , 2 , 8)

Question 7 : Montrer que la taille d’un tableau T représentant le skyline de m immeubles est
inférieure ou égale à 3m.

Un algorithme itératif de construction.


On suppose connaître le skyline T1,m−1 d’un ensemble de m − 1 immeubles. On ajoute un nouvel
immeuble Im , représenté par un tableau de taille 3 noté Tm,m .
Question 8 : Montrer que l’on peut calculer T1,m à partir de T1,m−1 et de Tm,m avec une com-
plexité en O(m).
N.B. On ne demande pas l’algorithme lui-même, mais une description informelle.
On s’appuiera sur les exemples des Figures 3 et 4.
T1,m−1 = (1 , 1 , 2 , 3 , 3 , 1 , 5 , 0 , 6 , 2 , 8)
Tm,m = (4 , 4 , 7)
T1,m peut se construire en trois étapes : interclasser les abcisses de T1,m−1 et de Tm,m pour obtenir
le vecteur
(1 , . , 2 , . , 3 , . , 4 , . , 5 , . , 6 , . , 7 , . , 8)
Ensuite, remplir les valeurs des hauteurs :

(1 , 1 , 2 , 3 , 3 , 1 , 4 , 4 , 5 , 4 , 6 , 4 , 7 , 2 , 8)

Puis enlever la redondance


(1 , 1 , 2 , 3 , 3 , 1 , 4 , 4 , 7 , 2 , 8)
Question 9 : Quel est l’ordre maximal de complexité (en nombre de comparaisons) du calcul
itératif de S1,m , à partir de I1 , . . . , Im , en fonction de m ?

Un algorithme récursif.
On suppose connaître le skyline T1,p des immeubles I1 à Ip d’une part, et le skyline Tp+1,m des
immeubles Ip+1 à Im d’autre part.
Question 10 : Montrer que l’on peut calculer le tableau T1,m à partir des tableaux T1,p et Tp+1,m
avec une complexité en O(m)..
94 CHAPITRE 99. EXERCICES EN COURS DE RÉDACTION.
Question 11 : Formuler le raisonnement par récurrence qui permet de construire T1,m .
Donner un algorithme Diviser pour Régner utilisant une procédure récursive fondée sur ce raisonne-
ment, que l’on formulera comme : SKY2(p, q, T ), avec 1 ≤ p ≤ q ≤ n. Cette procédure doit calculer
le skyline correspondant aux seuls immeubles Ip à Iq et mettre le résultat dans T . Donner son appel
depuis le programme principal.
N.B. : Le mieux est d’utiliser trois tableaux auxiliaires de taille 3m : T1 , T2 et T .
Question 12 : Quelle est son ordre de grandeur de complexité maximal en nombre de comparai-
sons ?

La solution est page ??

Essais successifs.
Programmation dynamique.
Graphes
Exercice 99.13 (♥ Algorithme de Dantzig.).

Question 1 :
Question 2 :
Question 3 :

Anonyme

Exercice 99.14 (♥ Algorithme de Warshall.).

Question 1 :
Question 2 :
Question 3 :

Anonyme

Recherche d’optimum.
Exercice 99.15 (♥ Le meilleur intervalle (le retour)). Rappellons le problème, déjà présenté
dans l’exercice 99.10, page 90. On dispose d’un tableau T [1 : n] de valeurs positives réelles. Il existe
deux indices i etj, définissant l’intervalle [i, j], avec 1 ≤ i < j ≤ n, tels que la valeur T [j] − T [i] soit
positive et maximale. On cherche cette valeur maximale (la valeur du meilleur intervalle). Si le tableau
est monotone décroissant, cette valeur est négative ou nulle : on impose alors qu’elle soit nulle.
Question 1 : Donner un algorithme en Θ(n) pour calculer la valeur du meilleur intervalle.

Anonyme

Exercice 99.16 (♥ Ensemble indépendant de poids maximal dans un arbre).


Enoncé.
Soit T un arbre dont chaque sommet (feuilles comprises) possède un poids positif. On note W(v)
le poids du sommet v. Le poids W(S) d’un sous-ensemble S de sommets de T est par définition la
somme des poids des sommets qui le composent
X
W(S) = W(u)
u∈S

On dit que deux sommets u et v sonts adjacents quand u est le père de v ou quand v est le père
de u. Un ensemble de deux sommets ou plus est dit indépendant s’il ne contient pas de couple de
95

1 15

2 8 3 9

4 3 5 12

6 1 7 15 8 15 9 10

10 6 11 8 12 7 13 5

Fig. 99.1 – Un exemple d’arbre. Le numéro du sommet est encadré, le poids est écrit en gras sur le
côté. L’ensemble indépendant {1, 4, 5, 10, 11, 12, 13} a pour poids 56. L’ensemble indépendant de poids
maximal est {2, 3, 6, 7, 8, 12, 13}, dont le poids vaut 60.

sommets adjacents. Le problème est de trouver S ∗ , un sous-ensemble indépendant de poids maximal


des sommets de T ( la figure 99.1 donne un petit exemple de ce problème).
Soit u un sommet de T . On suppose qu’il a des fils v1 , . . . , vc et des petits-fils w1 , . . . , wg . On note

Su un ensemble indépendant de poids maximal pour le sous-arbre de racine u.
Question 1 : Montrer que si u 6∈ S ∗ , alors Su∗ = Sv∗1 ∪. . .∪Sv∗c , où Sv∗i est un ensemble indépendant
de poids maximal pour l’arbre de racine vi .
Question 2 : Montrer que si u ∈ S ∗ , alors Su∗ = {u} ∪ Sw ∗
1

∪ . . . ∪ Sw g

, où Sw i
est un ensemble
indépendant de poids maximal pour l’arbre de racine wi .
Question 3 : En déduire une relation de récurrence et un algorithme de programmation dyna-
mique pour calculer S ∗ et son poids. Quelle est sa complexité ?
Question 4 : L’appliquer à l’arbre exemple.

Exercice 99.17 (♥ Le sous-ensemble de somme nulle).


Soit S = {x1 , x2 , . . . , xn } un ensemble de n entiers relatifs. On cherche s’il existe un sous-ensemble non
vide de S de somme nulle.
On note P la somme des éléments strictement positifs et N la somme des éléments strictement
négatifs de S. On définit la fonction booléenne Q(i, s) comme la valeur de vérité de l’affirmation :
« il existe un sous-ensemble de {x1 , x2 . . . xi } dont la somme des éléments vaut s ». On cherche donc à
savoir si Q(n, 0) est VRAI ou FAUX.
Question 1 : Montrer que Q(i, s) est FAUX quand s < N ou quand s > P.
Question 2 : Démontrer une relation de récurrence qui relie Q(i, s), Q(i − 1, s) et Q(i − 1, s − xi )
pour i > 1. Quelle est son initialisation pour i = 1 ?
Question 3 : En déduire un algorithme de programmation dynamique pour calculer Q(n, 0). Com-
ment cet algorithme peut-il aussi calculer le (ou les) sous-ensemble(s) qui rendent cette valeur VRAI ?
L’appliquer à l’exemple suivant : S = {8, −7, −2, −3, 5}
Question 4 : Etudier sa complexité en fonction de n.
96 CHAPITRE 99. EXERCICES EN COURS DE RÉDACTION.
Question 5 : Que devient ce problème si on cherche le sous-ensemble dont la somme est négative
ou nulle, et la plus proche possible de 0 ?
Remarque : En modifiant légèrement l’énoncé de cet exercice, on retrouve les exercices 4.8 et 4.9 du
chapitre 4 : Partition de tableau (page 45) et Les élections présidentielles à l’américaine (page
46), qui sont, comme celui-ci, des variantes de problème du sac à dos 0/1 (voir le chapitre 8.1.1, page
75).
Question 6 : Modifier ce problème pour résoudre le problème de partition de tableau (exercice 4.8,
page 45). L’appliquer au tableau T = (6, 3, 5, 7).

Sur Wikipedia : Subset sum problem.

Exercice 99.18 (♥ Segmentation d’une image).


On dispose d’une image binaire sous forme de matrice de pixels T [1 : m ; 1 : n], dont les éléments
valent 0 (blanc) ou 1 (noir). On cherche à partager l’image en un ensemble de rectangles entièrement
noirs ou entièrement blancs.
La technique utilisée est celle de la guillotine, dont la règle est la suivante : étant donné un rectangle,
on a le droit de le partager en deux rectangles plus petits soit par un trait horizontal, soit par un trait
vertical.
Le problème est de trouver le nombre minimal de coups de guillotine pour séparer complètement
les pixels noirs des pixels blancs.
Dans l’exemple ci-dessous, le nombre de coups de guillotine est 12.

0 0 0 0 0 0 0 0
0 0 1 1 1 0 0 0
0 1 1 1 1 0 0 0
0 1 1 1 0 0 0 0
0 1 1 1 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 0

Question 1 : Donner un ordre possible des coups de guillotine pour le problème donné en exemple.
Question 2 : Donner une relation de récurrence pour calculer le nombre minimal de coups de
guillotine nécessaires pour partager une image quelconque.

Anonyme

Exercice 99.19 (♥ Le plus grand carré noir).


Soit une image carrée composée de pixels noirs et blancs. On cherche la plus grande sous-image carrée
complètement noire.
De manière plus formelle, étant donnée une matrice binaire T [1 : n , 1 : n] on cherche trois nombres
entiers positifs l, m et k, avec k maximum, tels que :
1. 1 ≤ l ≤ n
2. 1 ≤ m ≤ n
97

3. k ≤ Min(n − l + 1, n − m + 1)
4. T [i, j] = 1 pour l ≤ i ≤ l + k − 1 et m ≤ j ≤ m + k − 1
Par exemple, dans le tableau ci-dessous (n = 6), la solution unique est pour les valeurs : l = 3,
m = 2 et k = 3.

0 0 1 1 0 0
0 0 1 1 0 0
0 1 1 1 1 1
0 1 1 1 1 1
0 1 1 1 0 1
0 0 1 1 0 0

Question 1 : Donner un algorithme en Θ(n2 ) pour trouver l, m et k. L’opération élémentaire est


la comparaison.
Question 2 : Que dire de la complexité en espace ?

La solution est page ??

Exercice 99.20 (♥ L’étagère).


On veut ranger n livres B1 , . . . , Bn dans une étagère. Les contraintes sont les suivantes :
– L’ordre dans lequel les livres sont disposés est fixé : B1 doit se trouver à gauche de l’étage du
haut, B2 est serré à droite de B1 ou à gauche de l’étagère du dessous, etc.
– L’étagère a une largeur fixe L, mais le nombre d’étages est réglable, et la hauteur de chaque étage
est réglable. Pour simplifier, on dira que les planches qui forment les étages ont une épaisseur
nulle. L’étage du haut est surmonté d’une planche qui définit la hauteur totale de l’étagère.
– Chaque livre Bi est caractérisé par sa hauteur hi et son épaisseur ei .
La figure ci-dessous donne deux façons différentes d’arranger une étagère de largeur 10 pour ranger
cinq livres de dimensions :

i 1 2 3 4 5
ei 3 3 2 3 2
hi 5 7 9 3 10

La hauteur totale de la première étagère est 7 + 10 = 17. Celle de la seconde est 9 + 10 = 19.
Le problème est de trouver comment disposer les planches de l’étagère pour qu’elle ait une hauteur
totale minimale.
Question 1 : On note H(i) la hauteur minimale d’une étagère de largeur L qui range les livres
Bi , . . . , Bn (avec 1 ≤ i ≤ n).
98 CHAPITRE 99. EXERCICES EN COURS DE RÉDACTION.
On note t(i, j) la hauteur minimale d’une rangée de livres commençant par Bi et se terminant par Bj
(avec j ≥ i).
Pj
On a : t(i, j) = Max hk si k=i ek ≤ L et t(i, j) = +∞ sinon.
k=i,j
Trouver une relation de récurrence sur les valeurs H(i) et t(i, j).
Question 2 : En déduire un algorithme de programmation dynamique pour calculer H(1). Quelle
est sa complexité temporelle et spatiale ?

Anonyme

Exercice 99.21 (♥ La plus courte sur-séquence commune).


L(i, j est la longueur de la plus courte surséquence commune à u1 . . . ui et v1 . . . vj .
Question 1 : Récurrence ?
Question 2 : Exemple u = abcab et v = acda

Anonyme

Exercice 99.22 (♥ Approximation d’une fonction échantillonnée par une ligne brisée).
Soit un ensemble S de n points du plan, avec n ≥ 1, dont les abcisses valent 1,2, . . . n et dont les
ordonnées sont quelconques. On cherche la meilleure approximation de cet ensemble par une ligne
brisée. Pour être plus précis :
1. Une ligne brisée est une suite de segments de droites dont les extrémités sont des points de S.
Une ligne brisée peut se représenter par la suite des abcisses des points sur lesquels elle s’appuie.
On impose que le premier nombre vale 1 et le dernier n, autrement dit que le premier point de
S soit le départ du premier segment de droite et son dernier point l’arrivée du dernier segment
de droite.
Par exemple, dans les quatre figures ci-dessous, les lignes brisées sont successivement : (1, 4, 8),
(1, 5, 8), (1, 4, 7, 8) et (1, 3, 4, 8).
2. La qualité de l’approximation de S par une ligne brisée se mesure pour partie en calculant la
somme Σ des distances euclidiennes des points de S au segment de droite dont les extrémités les
encadrent. Pour la première figure, Σ est la somme :
– de la distance des points 1, 2, 3 et 4 au segment construit sur les points 1 et 4, soit environ
(0 + 0.35 + 0.35 + 0) = 0.7,
– et de la distance des points 4, 5, 6, 7 et 8 au segment construit sur les points 4 et 8, soit
environ (0 + 1.6 + 0.4 + 1.2 + 0) = 3.2.
3. On ajoute à Σ un terme positif mC, proportionnel au nombre m de segments de droite de la
ligne brisée. Dans l’exemple de la première figure, si l’on choisit la valeur de C à 2, ce terme
vaut 4, puisque m = 2.
4. Pour finir, on dit que la qualité de l’approximation réalisée est d’autant meilleure que la somme
O(n) = Σ + mC est petite.
5. Par convention, on pose O(1) = 0.
Dans le premier exemple ci-dessous, cette valeur vaut donc à peu près 0.7 + 3.2 + 4 = 7.9.
On calcule de la même manière, pour la figure 3, la valeur Σ = (0 + 0.3 + 0.3 + 0) + (0 + 0.8 + 0.2 +
0) + (0 + 0) = 1.6 et O(n) = 1.6 + 3 × 2 = 7.6. Elle est donc meilleure que la précédente.
Le second exemple propose une approximation moins bonne que la première (à cause de Σ). De
même, la quatrième approximation est moins bonne que la troisième. La meilleure des quatre est donc
finalement la troisième.
Pour C = 2, sous réserve d’un calcul exhaustif, il semble donc meilleur de prendre trois segments
que deux.
Le problème est finalement de trouver la ligne brisée optimale : étant donné S et C, celle qui
minimise O(n) = Σ + mC.
99

5 bc 5 bc

4 bc bc 4 bc bc

3 c
b bc bc bc 3 bc bc bc bc

2 2
1 c
b 1 bc

0 0
0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8
Exemple 1 : Exemple 2 :
n = 8, m = 2, ligne brisée (1, 4, 8) n = 8, m = 2, ligne brisée (1, 5, 8)
100 CHAPITRE 99. EXERCICES EN COURS DE RÉDACTION.

5 bc 5 bc

4 bc bc 4 bc bc

3 c
b bc bc bc 3 bc bc bc bc

2 2
1 c
b 1 bc

0 0
0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8
Exemple 3 : Exemple 4 :
n = 8, m = 3, ligne brisée (1, 4, 7, 8) n = 8, m = 3, ligne brisée (1, 3, 4, 8).

Question 1 : Que peut-on dire quand C est très petit ? Quand C est très grand ?
Supposons que la ligne brisée optimale de valeur O(n) corresponde à la suite croissante d’abcisses
(a1 , a2 , . . . , am−1 , am ), avec a1 = 1 et am = n. Cette suite d’abcisses est alors aussi dite optimale.
Notons plus généralement O(i) la valeur optimale que l’on peut obtenir pour approximer par une
ligne brisée l’ensemble des points d’abcisses (1, . . . i).
Maintenant, notons k = am−1 l’abcisse de départ du dernier segment de la ligne brisée optimale
pour l’ensemble S des n points, et Σk,n la somme des distances des points d’abcisses (k, . . . , n) à la
droite passant par les deux points de S ayant pour abcisses k et n.
Question 2 : Montrer que : O(n) = O(k) + Σk,n + C.
Comment calculer O(n) sans connaître k, mais en supposant connaître O(1) = 0, O(2), . . . O(n − 1) ?
Question 3 : Démontrer une formule de récurrence permettant de calculer pour tout i, avec (1 ≤
i ≤ n), la valeur O(i) en fonction
– des valeurs de O(j) pour 1 ≤ j ≤ i
– des valeurs Σj,i
– et de C.
Question 4 : On suppose qu’il existe une procedure Distance(j, i, k), avec j ≤ k ≤ i, qui calcule
la distance du point d’abcisse k à la droite construite sur les points j et i.
Quels sont les calculs à faire et dans quel ordre faut-il les faire pour calculer O(n) ?
Question 5 : En déduire le pseudo-code d’un algorithme qui calcule la valeur de l’approximation
O(n) par une ligne brisée optimale d’un ensemble S quelconque de n points.
Quel est son ordre de grandeur de complexité en nombre d’appel à Distance() ?
Question 6 : Comment pourrait-on modifier cet algorithme pour qu’il produise aussi les abcisses
des points de la ligne brisée optimale ?

La solution est page ??

Exercice 99.23 (♥ Jeu à deux joueurs).

Voir [GOO01], exercice C-9.22 et C-9.23.


Deux joueurs. Sur la table, une ligne de 2n cartes avec un nombre écrit sur chaque carte. L’ensemble
des cartes est à tout moment visible des deux joueurs. Chaque joueur, à tour de rôle, doit prendre une
des deux cartes situées aux extrémités de la ligne. La carte disparaît alors du jeu et le gain du joueur
augmente du nombre écrit sur la carte.

1. Quand l’adversaire veut perdre.

On se pose d’abord la question de savoir quel est le gain maximal que pourrait récupérer le joueur
qui joue le premier (ce qui revient à supposer que son adversaire collabore au maximum avec lui).
Question 1 : Donner une récurrence qui permet de calculer le gain final maximal du joueur qui
joue le premier,
101

Question 2 : En déduire un algorithme en O(n2 ) opérations élémentaires pour calculer cet opti-
mum.
Question 3 : Montrer sur un exemple que ce gain maximal n’est pas toujours égal à la somme des
n plus grandes valeurs des cartes.

2. Quand l’adversaire veut gagner.

On suppose maintenant que l’adversaire cherche également à gagner.


Question 4 : Donner une récurrence qui permet de calculer le gain final maximal du joueur qui
joue le premier, sans oublier que l’autre joueur cherche aussi à maximiser son propre gain.
Question 5 : En déduire un algorithme en O(n2 ) opérations élémentaires pour calculer cet opti-
mum.
Question 6 : Faire tourner l’algorithme sur la ligne de cartes suivante :

6 7 10 8

La solution est page ??

Exercice 99.24 (♥ Le tas de briques).


On dispose de briques de n types différents, et pour chaque type d’un nombre illimité d’exemplaires.
Une brique de type i est un parallélépipède rectangle dont les côtés ont pour longueur c1i , c2i et c3i ,
avec c1i ≤ c2i ≤ c3i .
On cherche à faire un tas de hauteur maximale. On commence par poser une brique sur une de ses
faces, puis une seconde sur la première, les côtés parallèles, puis une troisième, etc. La contrainte est
que l’on ne peut poser une brique sur le tas en construction que si la face que l’on pose est strictement
incluse dans la face supérieure de la brique précédente. Autrement dit, à chaque nouvelle brique, le
tas va en rétrécissant strictement.
Question 1 : Montrer qu’il y a au plus six façons de poser une nouvelle brique sur une brique déjà
posée.
Question 2 : Montrer que parmi ces six façons, seulement trois sont à prendre en compte.
Question 3 : Montrer que dans un tas il y a au plus deux briques d’un type donné.
Question 4 : Reformuler le problème comme un problème d’empilement optimal, avec un choix
non plus parmi un nombre illimité de briques, mais un choix parmi 3n objets différents.
Question 5 : Donner la formule de récurrence permettant de construire le tas le plus haut.

La solution est page ??

Exercice 99.25 (♥ Distribution de skis).


On dispose de m paires de skis qu’il faut attribuer à n skieurs, avec m ≥ n. On doit attribuer à chaque
skieur une paire de skis et une seule ; d’autre part, il s’agit de maximiser la satisfaction globale des
skieurs. On dira qu’un skieur est d’autant plus satisfait que la longueur de la paire de skis qu’on lui
attribue est proche de sa taille.
Plus formellement, notons h1 , . . . , hn les tailles des skieurs et s1 , . . . , sm les longueurs des skis. Il
s’agit de trouver une fonction injective f : {1 . . . , n} 7−→ {1, . . . , m} optimale, c’est à dire qui minimise
la valeur :
Xn
A(n, m) = |sf(k) − hk |
k=1

Pour simplifier, on supposera tous les skis de longueur différente et tous les skieurs de taille différente.
102 CHAPITRE 99. EXERCICES EN COURS DE RÉDACTION.
Analyse
Question 1 : Montrer que pour n = 2 et m = 2, la solution optimale est obtenue en attribuant
les skis les plus longs au skieur le plus grand et les skis les plus courts au skieur le plus petit.
On fait désormais l’hypothèse que les suites h1 , . . . , hn et s1 , . . . , sm sont rangées par ordre croissant.
Question 2 : Montrer que pour n = m, la solution optimale est obtenue par un algorithme glouton.
Question 3 : Proposer un algorithme glouton pour le cas m > n et donner un cas sur lequel il ne
fonctionne pas (par exemple celui de la question 6).
Notons A(i, j) la solution optimale qui utilise les skis de rang 1 à j pour équiper les skieurs de rang
1 à i, avec j ≥ i. Il y a deux cas exclusifs :
– soit la paire de skis de rang j a été attribuée à un skieur,
– soit elle n’a pas été attribuée.
Question 4 : En utilisant la question 2, montrer que dans le premier cas cité ci-dessus, c’est
obligatoirement au skieur de rang i que la paire de skis de rang j a été attribuée.
Question 5 : En déduire la formule de récurrence liant A(i, j), A(i, j − 1), A(i1 , j − 1) et |sj − hi |.
Ne pas oublier son initialisation. Comment en déduire la fonction f ?

Implémentation
On s’intéresse maintenant à l’implémentation de cette récurrence sous la forme d’un algorithme.
Question 6 : Prendre comme exemple m = 5 et n = 3, les valeurs de longueur des skis : s1 =
158, s2 = 179, s3 = 200, s4 = 203, s5 = 213 et les tailles des skieurs : h1 = 170, h2 = 190, h3 = 210.
Résoudre le problème.
Question 7 : Expliquer de manière générale dans quel ordre un tableau A[1 : n , 1 : m] doit être
rempli pour que l’on puisse calculer A(n, m).
Question 8 : Montrer que remplir complètement le tableau A n’est pas nécessaire. Quelle est la
complexité minimale spatiale et temporelle de l’algorithme ? Vérifier que, pour n = m, on retrouve
l’algorithme glouton de la question 2.
La solution est page ??

Algorithmes gloutons.
Exercice 99.26 (♥ Les relais pour téléphones portables).
Une longue route droite de campagne, le long de laquelle sont dispersées des habitations. Chaque
maison veut être reliée au réseau de téléphone portable, sur place et sur la route. Un émetteur du réseau
permet l’usage du téléphone dans une zone à distance fixe autour de l’émetteur ; tous les émetteurs
ont la même puissance. L’opérateur veut poser le moins d’émetteurs possibles pour « couvrir »toutes
les maisons.
Formellement : T [1 : n] est un tableau de nombres réels positifs croissants. On donne un nombre
réel positif t. On cherche l’ensemble de valeurs réelles de cardinal minimum p, S = {s1 , . . . , sp } tel
que pour toute valeur T [i], il existe une valeur sj telle que : | T [i] − sj | ≤ t.
Question 1 : Donner informellement un algorithme glouton pour résoudre ce problème.
Question 2 : Le donner en pseudo-code.
Question 3 : Montrer qu’il est optimal.
Anonyme
Exercice 99.27 (♥ Titre).
Blabla.
Question 1 :
Question 2 :
La solution est page ??
Chapitre 99

Exercice en test.

Exercice 99.1 (♥ Les guetteurs).

On veut placer un nombre minimal de reines sur un échiquier n × n de sorte que :


1. elles ne sont pas en prise,
2. l’ensemble des cases de l’échiquier est sous leur contrôle.
On rappelle qu’une reine contrôle les cases :
1. de la colonne sur laquelle elle se trouve,
2. de la ligne sur laquelle elle se trouve, et
3. des deux diagonales passant par la case où elle se trouve.
Exemple.
Voici une solution pour un échiquier 8 × 8.

(5, 3)

(4, 5)

(3, 1)

(2, 4)

(1, 1)

Remarque : on ne sait pas si elle est optimale (en fait elle l’est).
Eléments d’analyse
Il s’agit clairement d’un problème de recherche de solution optimale : ici le critère d’optimalité
porte sur le nombre de reines posées. Dans la continuité de ce qui a été fait pour le problème des n
reines, l’idée consiste à examiner successivement les lignes (ou les colonnes) puisque deux reines ne
peuvent partager la même ligne (ou colonne) vu qu’elles ne doivent pas être en prise. On va utiliser les
tableaux A[1 .. n], B[2 .. 2*n] et C[1 - n .. n - 1] vus précédemment pour gérer l’occupation des colonnes

103
104 CHAPITRE 99. EXERCICE EN TEST.

et des diagonales. Cependant, vu qu’on peut ne pas poser de reine dans une ligne (ou colonne), il faut,
outre les choix de colonne 1 à n associés à la ligne courante, ajouter le choix correspondant à ne pas
poser de reine.
Un point nouveau concerne le calcul des cases "en vue" de la solution courante avec le fait qu’une
case peut être en vue de plusieurs reines et donc que la suppression d’un choix (opération défaire)
doit être soigneusement étudié. Le principe va consister à calculer les cases en vue spécifiquement
de la reine placée et ce sont celles-là qui seront enlevées lors de la remise en cause du choix courant
(opération défaire).
Pour ce qui est des aspects liés à l’optimalité de la solution cherchée, il faut gérer le nombre de
reines utilisées. Il apparaît difficile de prédire un nombre de reines minimal à poser pour contrôler les
cases non encore sous contrôle. On va donc se contenter de dire que tant qu’il reste au moins une case
à contrôler, on devra encore poser au moins un reine (on inclura donc dans l’opération opt-encore-
possible un test sur le nombre de reines de la solution courante + 1 d’une part et la valeur optimale
actuelle du nombre de reines - celui associé à la solution optimale courante, d’autre part).
Spécification.
Vecteur Le vecteur X représentant la solution courante (en cours de construction) est de taille n. Le
domaine Si de chaque coordonnée est constitué des valeurs 0 à n : la valeur 0 pour la coordonnée
i indique que l’on ne pose pas de reine dans la ligne i. Une valeur j non nulle indique que l’on
pose une reine en ligne i et colonne j.
Satisfaisant Si xi = 0, Satisfaisant est VRAI.
Si xi ∈ [1, n] Satisfaisant est VRAI quand la colonne xi est libre et les deux diagonales passant
par (i, xi ) sont libres.
Enregistrer Si xi = 0, Enregistrer se limite à : X[i] ← xi .
Si xi ∈ [1, n], c’est un peu plus compliqué :
X[i] ← xi ; nb-reines-cour ← nb-reines-cour +1 ; colonne xi occupée ; deux diagonales passant
par xi occupées ; calcul des cases nouvellement prises (cases-nouv-prises) ; cases-à-contrôler ←
cases-à-contrôler − cases-nouv-prises
soltrouvé Si xi = 0 : faux
Si xi ∈ [1, n] : vrai si l’ensemble des cases restant à contrôler est vide
meilleur/optimal nb-reines-cour < nb-reines-opt (pas indispensable si on met le test d’espoir d’op-
timalité dans opt-encore-possible)
opt-encore-possible (nb-reines-cour +1) < nb-reines-opt
défaire Si xi = 0 : rien
Si xi ∈ [1, n] : nb-reines-cour nb-reines-cour −1 ; colonne xi libre ; deux diagonales passant par
xi libres cases-à-contrôler ← cases-à-contrôler cases-nouv-prises
Déclarations globales
type case c’est struct ent lig, col fstruct ; ens case nonatt init (1 :n)x(1 :n) ; var X[1 :n], SOPT[1 :n]
ent ; tab A[1 :n] bool init vrai, B[2 :2*n] bool init vrai ; var C[-n+1 :n-1] bool init vrai ; ent nb-reines-
cour, nb-reines-opt, n, j ;
Appel
lire(n) ; nb-reines-opt n + 1 ; nb-reines-cour 0 ; guetteurs(1) ; pour j de 1 à n faire ecrire(SOPT[j])
fait ;
Procédures
proc guetteurs c’est fixe (ent i) ent j ; ens case nouvcasatt ; début X[i] 0 ; si i < n alors guetteurs(i+1)
fsi ; co traitement du cas 0 à part fco ; pour j de 1 à n faire si A[j] et B[i+j] et C[i-j] alors X[i] j ; A[j] faux ;
B[i+j] faux ; C[i-j] faux ; nouvcases(i,j)(nouvcasatt) ; nonatt nonatt - nouvcasatt ; nbreines nbreines +
1 ; si vide(nonatt) alors nb-reines-opt nb-reines-cour ; gardersol(i) fsi sinon si i < n et (nb-reines-cour
+ 1) < nb-reines-opt alors guetteurs(i+1) fsi fsi ; nb-reines-cour nb-reines-cour - 1 ; nonatt nonatt U
nouvcasatt ; A[j] vrai ; B[i+j] vrai ; C[i-j] vrai fsi fait fin
105

proc gardersol c’est fixe (ent i) ent j ; début pour j de 1 à i faire SOPT[j] X[j] fait ; pour j de (i+1)
à n faire SOPT[j] 0 fait fin
proc nouvcases c’est fixe (ent i,j) mod (ens case E) case c ; début E ; pour tout c de (1 :n) (1 :n)
faire si c.lig = i ou c.col = j ou c. lig+c.col = i+j ou c.lig-c.col = i-j alors E E c fsi fait E E nonatt fin
Quelques résultats intéressants
taille de l’échiquier "n reines" nbsol "guetteurs" nb-reines-opt - nbsol
1 1 1 1 2 0 1 4 3 0 1 1 4 2 3 16 5 10 3 16 6 4 4 120 7 40 4 8 8 92 5 728
Question 1 :
Question 2 :

La solution est page ??


106 CHAPITRE 99. EXERCICE EN TEST.
Chapitre 99

Exercice en test.

Exercice 99.1 (♥ Distance entre séquences : algorithme de Wagner et Fisher.).


Définitions
On se donne un alphabet X, c’est-à-dire un ensemble fini de lettres dont la taille est notée |X| ;
soit par exemple l’alphabet X = {a, b, c}. Une séquence, ou chaîne, ou mot ) sur X est une suite finie
de lettres de X. La longueur |u| d’une séquence u est son nombre de lettres. Soit par exemple les
séquences u = acbb et v = cab. On a |u| = 4. On appelle préfixe d’une séquence une séquence par
laquelle elle commence. Par exemple, le préfixe de longueur 2 de u est ac.
On note u[i] la lettre numéro i de u. Par exemple : u[3] = b
On note ǫ la séquence de longueur nulle. Cette notion sera utile ici à deux choses : d’abord, le
préfixe de longueur 0 de toute séquence est donc ǫ. Ensuite, étant donnée une séquence u = u1 u2 ...un ,
on peut y insérer autant de ǫ que l’on veut sans en changer la signification. On appellera superséquence
de u la séquence u avec de telles insertions. Par exemple, si u = bcaa, une superséquence de u est :
ǫbcǫǫaa. Par abus de langage, on dira que la longueur de cette surséquence est 7.
Soit deux séquences u = u1 u2 ...un et v = v1 v2 ...vm et deux superséquences de u et de v de
même longueur. On appelle « trace »entre u et v la mise en correspondance lettre à lettre des deux
superséquences. On exige de plus qu’une trace n’associe pas deux ǫ.
Par exemple, entre les mots u = bcaa et v = acbca, on peut créer la trace :

ǫ ǫ b c a a
| | | | | |
a c b ǫ c a

Une trace peut s’interpréter comme une suite d’insertions, de suppressions et de transformations de
lettres de la première séquence pour former la seconde. Dans l’exemple précédent, la trace correspond
à la suite suivante :

1. insertion de a 4. suppression de c
2. insertion de c 5. transformation de a en c
3. transformation de b en b 6. transformation de a en a

Pour donner une valeurs aux coûts des insertions, des suppressions et des transformations, on
définit une matrice δ de nombres réels positifs ou nuls de taille |X| + 1 × |X| + 1. C’est une matrice qui
définit une distance : elle symétrique, de diagonale nulle et vérifiant l’inégalité triangulaire. La valeur
d’un élément de cette matrice peut s’interpréter comme le coût de transformer une lettre en l’autre
ou comme le coût de suppression et d’insertion pour chaque lettre.
Par exemple, sur un alphabet de trois lettres, une telle matrice pourrait être :

107
108 CHAPITRE 99. EXERCICE EN TEST.

δ ǫ a b c
ǫ 0 1 1 1
a 1 0 1.5 1.2
b 1 1.5 0 1.7
c 1 1.2 1.7 0
Dans cet exemple, le coût de suppression de a est 1 (δ(a, ǫ) = 1), le coût d’insertion de b est 1
(δ(ǫ, b) = 1), le coût de transformation de a en c est 1.2 (δ(a, c) = 1.2).
On appelle le coût d’une trace la somme des coûts élémentaires des opérations qui la constituent.
Dans l’exemple précédent, le coût de la trace est donc : 1 + 1 + 0 + 1 + 1.2 + 0 = 5.2
Une autre trace entre les mots u = bcaa et v = acbca est par exemple, pour un coût de 1.5 + 0 +
1.5 + 1 + 0 = 4 :
b c a ǫ a
| | | | |
a c b c a
Le problème.
Le problème est de trouver le coût de la trace optimale, c’est à dire la moins coûteuse, entre deux
séquences u et v.
On définit pour cela une matrice ∆(0 : n , 0 : m), de taille (n + 1) × (m + 1), telle que ∆(i, j) est
le coût de la trace la moins coûteuse entre le préfixe de longueur i de u et le préfixe de longueur j de
v. On cherche donc la valeur ∆(|u| + 1, |v| + 1) = ∆(n + 1, m + 1).
Questions.
Question 1 : Comment calculer ∆(0, j) pour j = 1, |v| et ∆(i, 0) pour i = 1, |u| ? Quelle valeur
donner à ∆(0, 0) ?
Question 2 : Cette question vise à établir une relation de récurrence qui calcule ∆(i, j) à partir
de ∆(i, j − 1), ∆(i − 1, j), ∆(i − 1, j − 1), δ(u[i], ǫ), δ(ǫ, v[j]) et δ(u[i], v[j]).
Pour cela, on remarque qu’une trace peut s’interpréter comme un chemin dans un graphe. Cette
interprétation est illustrée ci-dessous sur l’exemple de trace :
ǫ ǫ b c a a
| | | | | |
a c b ǫ c a
Un chemin dans le graphe entre le nœud étiqueté (0/0) et le nœud étiqueté (4/5) représente une trace
entre les séquences u = bcaa et v = acbca. La trace précédente est marquée en gras.

a 4/0 4/1 4/2 4/3 4/4 4/5

a 3/0 3/1 3/2 3/3 3/4 3/5

c 2/0 2/1 2/2 2/3 2/4 2/5

b 1/0 1/1 1/2 1/3 1/4 1/5

0/0 0/1 0/2 0/3 0/4 0/5

a c b c a

(a) Définir dans le cas général un tel graphe pour deux séquences quelconques u et v.
(b) Montrer, en donnant des longueurs calculées à partir de δ aux arcs du graphe, que la trace optimale
entre deux mots correspond au calcul d’un plus court chemin dans ce graphe.
(c) Compte tenu de la forme particulière du graphe, donner une relation de récurrence pour calculer
la longueur du plus court chemin entre le nœud (0/0) et tout autre nœud de ce graphe.
109

(d) En déduire la relation de récurrence du calcul du coût de la trace optimale en remplissant la


matrice ∆(0 : n , 0 : m). Le coût de la trace optimale doit être la valeur de ∆(n, m).
Question 3 : Combien y a-t’il de traces différentes entre deux mots u et v ?
Question 4 : Donner la procédure de Wagner et Fisher WF qui calcule le coût de la trace la moins
coûteuse (et permet de reconstituer cette trace) entre deux séquences quelconques sur un alphabet Σ,
connaissant la matrice δ, avec une complexité en taille O(nm) (rappelons que n = |u| et m = |v|).
Question 5 : Montrer que si l’on cherche simplement le coût de la trace la moins coûteuse, une
complexité en taille O(Max(n, m)) est suffisante. Ecrire ce programme (appelé Wagner et Fisher
« linéaire », ou WFL).
Question 6 : On prend l’alphabet du français, avec :
– pour toute lettre α : δ[α, ǫ] = δ[ǫ, α] = 2 ;
– si α et β sont deux consonnes ou sont deux voyelles : δ[α, β] = δ[β, α2] = 1 ;
– si α est une consonne et β une voyelle : δ[α, β] = δ[β, α] = 3 ;
Donner la matrice ∆ pour les séquences malin et coquine.
Question 7 : Comment reconstituer une trace optimale à partir de ∆ ? Dans l’exemple précédent,
cette trace est unique. Est-ce cas en général (indication :essayer u = rien et v = est).
Question 8 : Montrer que la trace optimale entre les phrases miroir u et v se déduit directement
de celle entre u et v, si elle est unique. Et si elle ne l’est pas ?
Question 9 : Montrer que, puisque δ définit une distance, alors ∆ définit aussi une distance (d’où
le titre de cet exercice).

La solution est page ??

Exercice 99.2 (♥ titre).

Réponse 1 :

La solution est page ??


110 CHAPITRE 99. EXERCICE EN TEST.
Bibliographie

[Bou93] L. Bougé, C. Kenyon, J.-M. Muller et Y. Robert. Algorithmique, exercices corrigés. Ellipses,
1993.
[BRA96] G. Brassard and P. Bratley. Fundamentals of algorithmics, Prentice-Hall, 1996.
[COR90] T. Cormen, C. Leiserson and R. Rivest. Introduction to algorithms., MIT Press, 1990.
[CRO01] M. Crochemore, C. Hancart and Thierry Lecroq. Algorithmique du texte., Vuibert, 2001.
[FRO90] C. Froidevaux, M.-C. Gaudel, M. Soria. Types de données et algorithmes., McGraw-Hill,
1990.
[GOO01] M. Goodrich and R. Tamassia. Algorithm design, Wiley, 2001.
[GRI93] D. Gries and F. Schneider. A logical approach to discrete mathematics, Springer, 1993.
[HOR78] E. Horowitz, S. Sahni and S. Rajasekaran Computer algorithms, Computer Science Press,
1998.
[KLE06] J. Kleinberg and E. Tardos Algorithm Design, Addison Wesley, 2006.
[Knu] D. Knuth, The Art of Computer Programming, Addison-Wesley.
[JOH04] R. Johnsonbaugh and M. Schaeffer. Algorithms, Pearson, Prentice-Hall 2004.
[MAN89] U. Manber Introduction to algorithms : a creative approach., Addison Wesley, 1989.
[PAR95] I. Parberry. Problems on algorithms, Prentice-Hall, 1995.
[PIN] S. Pinchinat et V. Schmitt. Algorithmique et complexité, Polycopié IFSIC, Université de
Rennes 1.
[Que00] M. Quercia, Nouveaux exercices d’algorithmique, Vuibert 2000.
[RUS95] S. Russel and P. Norvig, Artificial intelligence, a modern approach, Prentice-Hall 1995,
Second edition 2002.
[Web1] http://www2.toki.or.id/book/AlgDesignManual/
[Web2] http://valis.cs.uiuc.edu/~sariel/teach/courses/473/

111
Index

suite, 58, 107

112