TDetTP 2
TDetTP 2
TDetTP 2
Université de Rennes I
ENSSAT Lannion
EII 2 et LSI 2
ALGORITHMIQUE AVANCÉE
8 mars 2007
ii
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
2 Invariants, itération. 19
2.1 Rappels sur la logique de l’itération. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Exercices. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
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
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
Notations, conventions.
Notations.
Nombres.
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.
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.
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 .
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
n
X n(n + 1)
i=
2
i=1
implique
n+1
X (n + 1)(n + 2)
i=
2
i=1
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.
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).
...
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 .
Exemple
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.
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
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.
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.
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.
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
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
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 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 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) . . .
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 ?
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)
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 )
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
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).
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
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...
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.
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.
Invariants, itération.
19
20 CHAPITRE 2. INVARIANTS, ITÉRATION.
Chapitre 3
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]
j=logb (n)−1
X n
T (n) = Θ[nlogb (a) ] + aj .f( )
bj
j=0
a = b =⇒ T (n) ∈ Θ[n.Log(n)]
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
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.
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 ?
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.
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
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.
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 ?
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.
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.
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 » ?
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.
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 ?
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.
Anonyme
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
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
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
√ √
iπ
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
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
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) ?
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.
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
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
Fig. 4.1 – Arbre de récursion à moitié complet, avec l’ordre de construction des nœuds en profondeur
d’abord.
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
❡ ❡ ❡ ❡ ❡ ❡ ❡ (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
Anonyme
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
ABBCACBA
ABCACBBA
Anonyme
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 .
a
e
c d
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
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
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
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
N E U F
+
U N
+
U N
O N Z E
4.2. EXERCICES 47
(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
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
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
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.
✄ ✂ ✄ ✄ ✂ ✄
✕ ✕ ✖ ✕ ✕ ✕ ✰ ✕
Chapitre 5
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).
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
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.2 Exercices
Exercice 5.1 (♥ Titre.).
Question 1 :
Question 2 :
Question 3 :
Anonyme
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
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é ?
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.
ǫ ǫ 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 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).
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 :
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.
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
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 ?
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
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.
((M1 × (M2 × M3 )) × M4 )
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
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
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
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.
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.
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.
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
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.
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)).
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.
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 ??
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 ??
Mélanges
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.
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 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).
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.
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.
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 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
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
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
{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.
Anonyme
Enoncé du problème
Un premier algorithme
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
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
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) ?
87
88 CHAPITRE 99. EXERCICES EN COURS DE RÉDACTION.
Diviser pour régner
Exercice 99.3 (♥ Quicksort).
Blabla.
Question 1 :
Question 2 :
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
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.
Moralité
Anonyme
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.
Question 1 :
Question 2 :
Question 3 :
Anonyme
Anonyme
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)).
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.
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
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.
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.
(1 , 1 , 2 , 3 , 3 , 1 , 4 , 4 , 5 , 4 , 6 , 4 , 7 , 2 , 8)
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 ?
Essais successifs.
Programmation dynamique.
Graphes
Exercice 99.13 (♥ Algorithme de Dantzig.).
Question 1 :
Question 2 :
Question 3 :
Anonyme
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
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.
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
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
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
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 ?
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.
6 7 10 8
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.
(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 :
Exercice en test.
ǫ ǫ 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 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
Réponse 1 :
[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
112