Examen AAC 1ère session
Documents autorisés
8 janvier 2009
M1 Informatique
UFR IEEA
Remarque: Le correcteur se réserve le droit d’enlever des points si le nombre de réponses
incongrues lui paraît déraisonnable.
Exercice 1 : Quelques questions de compréhension du cours
Q 1. La professeur Terrorica a demandé à ses étudiants d’étudier la complexité de six prob-
lèmes de décision P1, P2, P3, P4, P5, P6. Chacun énonce ce qu’il a réussi à prouver:
Etudiant 1: Le problème P1 est NP.
Etudiant 2: Le problème P2 se réduit polynômialement dans le problème P1.
Etudiant 3: Le problème P3 se réduit polynômialement dans le problème P2.
Etudiant 4: Le problème P4 se réduit polynômialement dans le problème P2.
Etudiant 5: Le problème P5 se réduit polynômialement dans le problème P4.
Etudiant 6: Le problème P3 est NP-dur.
Etudiant 7: Le problème P4 est P.
Bien sûr leurs preuves ne sont pas à mettre en doute. Parmi P1, P2, P3, P4, P5, quels sont
les problèmes dont on peut affirmer qu’ils sont N P ? N P −durs? P ? Justifier brièvement.
P3 ≤P P2 ≤P P1, P5 ≤P P4 ≤P P2 ≤P P1
Donc on peut affirmer qu’ils sont tous N P , que P5 et P4 sont P et que P3, P2etP1 sont N P −durs.
Q 2. Soit un problème d’optimisation où il s’agit de minimiser une fonction objectif, et deux
heuristiques H1 et H2 (qu’on suppose correctes, c.à.d. donnant toutes les deux une solution
non nécessairement optimale mais correcte!). Le ratio de garantie de H1 est 3, celui de H2
est 6.
Soit f opt(I) le coût minimal pour l’instance I, f h1(I) le coût de la solution donnée par H1 pour
I et f h2(I) celui donné par H2; on a donc les contraintes:
f opt(I) ≤ f h1(I) ≤ 3 ∗ f opt(I)
f opt(I) ≤ f h2(I) ≤ 6 ∗ f opt(I)
Q 2.1. Peut-on avoir, pour une certaine donnée, H1 qui donne une solution de coût 7 alors
qu’H2 donne une solution de coût 2? Justifier.
Non, car pour toute instance, le coût de la solution proposée par H1 est au plus 3 fois celui de
l’optimal donc au plus trois fois celui de la solution proposée par H2: f h1(I) ≤ 3∗f opt(I) ≤ 3∗f h2(I)
Q 2.2. Peut-on avoir, pour une certaine donnée, H1 qui donne une solution de coût 2 alors
qu’H2 donne une solution de coût 7? Justifier.
Oui, par exemple si H1 donne une solution optimale, i.e. f opt(I) = f h1(I) = 2. En fait les
contraintes nous donnent 7/6 ≤ f opt(I) ≤ 2.
Q 2.3. Peut-on avoir, pour une certaine donnée, H1 qui donne une solution de coût 7 alors
qu’H2 donne une solution de coût 3? Justifier.
Oui, par exemple si H2 donne une solution optimale i.e. f opt(I) = f h2(I) = 3. En fait les
contraintes nous donnent 7/3 ≤ f opt(I) ≤ 3.
Q 2.4. Peut-on avoir, pour une certaine donnée, H2 qui donne une solution de coût 7 alors
qu’H1 donne une solution de coût 3? Justifier.
Oui, par exemple si H1 donne une solution optimale, i.e. f opt(I) = f h1(I) = 3. En fait les
contraintes nous donnent 7/6 ≤ f opt(I) ≤ 3.
1
Q 3. Pour chaque affirmation suivante, dire si elle est vraie ou fausse. Justifiez briévement.
L’alphabet est {a, b}.
1. L’ensemble des mots ayant un nombre pair de b est récursif.
Oui, il est même reconnaissable.
2. L’ensemble des mots ayant autant de a que de b est récursif.
Oui, il est même algébrique.
3. Si L1 et L2 ne sont pas récursifs, L1 ∪ L2 ne l’est pas non plus.
Non, on peut prendre pour L1 un langage non récursif -il en existe- et pour L2 son complémen-
taire -qui n’est donc pas récursif, puisque les récursifs sont clos par complémentaire et que le
complémentaire du complémentaire d’un langage est ce langage -: l’union est {a, b}∗ et est un
langage récursif.
4. Si L1 ∪ L2 n’est pas récursif, L1 ne l’est pas non plus.
Non, on peut choisir L1 = ∅ -qui est récursif- , L2 un langage non récursif, et donc L1 ∪ L2 = L2
n’est pas récursif.
5. Si L1 ∩ L2 n’est pas récursif, L1 ne l’est pas non plus.
Non, on peut choisir L1 = {a, b}∗ -qui est récursif- , L2 un langage non récursif, et donc
L1 ∩ L2 = L2 n’est pas récursif.
6. Si L1 ∪ L2 n’est pas récursif, L1 ou L2 ne l’est pas non plus.
Oui, car si les deux sont récursifs, l’union l’est aussi.
Remarque: l’affirmation est vraie Ssi sa contraposée est vraie; or sa contraposée est:
Si L1 et L2 sont récursifs, L1 ∪ L2 l’est.
7. Si L1 ∩ L2 n’est pas récursif, L1 ou L2 ne l’est pas non plus.
Oui, car si les deux sont récursifs, l’intersection l’est aussi.
Remarque: l’affirmation est vraie Ssi sa contraposée est vraie; or sa contraposée est:
Si L1 et L2 sont récursifs, L1 ∩ L2 l’est.
Advertisement
Rappel: un langage est récursif si il existe un algorithme pour décider l’appartenance à ce
langage.
Exercice 2 : Maximum
Soit un tableau T de n entiers; on suppose qu’il existe un indice p, 0 ≤ p < n tel que la
suite T [0], T [1], ..., T [p] soit strictement croissante et la suite T [p], T [p + 1], ..., T [n − 1] soit
strictement décroissante. Par exemple, si n = 6 et T [0] = 1, T [1] = 6, T [2] = 12, T [3] =
16, T [4] = 9, T [5] = 4, alors p = 3.
Proposer un algorithme en O(log n) pour déterminer p.
// il existe p tel que T[0]< T[1]<..<T[p] > T[p+1]>.....T[n-1];
a=0;
b=n-1;
while (a<b){ // invariant : a <=p <=b
m=a+b/2; // a<=m<b car a<b
2
if (T[m] <T[m+1]) //ok car m+1<=b
a=m+1;
else b=m;
}
//a=p=b
return a;
Remarque on a bien (b − a) qui décroît strictement à chaque itération, d’où l’arrêt. La correction
est prouvée en utilisant les assertions qui décorent l’algorithme;
DEUX erreurs faites souvent dans les copies (et peu pénalisées):
. algo qui boucle comme par exemple si on remplace a=m+1 ci-dessus par a=m;
. accès à T [i] avec i hors des bornes du tableau par exemple ici avec un test T [m − 1] < T [m]
Exercice 3 : Au suivant
On veut pouvoir générer tous les mots sur {a, b} de longueur inférieure ou égale à une valeur
donnée dans l’ordre alphabétique. Par exemple l’énumération pour 3 doit produire:
a
aa
aaa
aab
ab
aba
abb
b
ba
baa
bab
bb
bba
bbb
Q 1. Pour cela, complétez le code des méthodes Suivant() et EstDernier() de la classe
suivante:
class Mot {
//le mot
private char[] cont;
//la longueur du mot courant
private int longueur;
//la longueur maxi
private int longueurmaxi;
//constructeur initialise le mot au mot vide, la longueur au maxi
Mot(int longueurmaxi){
3
this.cont=new char[longueurmaxi];
this.longueur=0;
this.longueurmaxi=longueurmaxi;
}
//tranforme le mot en son suivant pour l’ordre alphabétique
//on peut supposer que this.EstDernier() est faux;
void Suivant(){ A faire}
//retourne vrai Ssi le mot est le dernier dans l’ordre alphabétique: bbbb...bbbbb
boolean EstDernier(){A Faire}
......
}
//tranforme le mot en son suivant pour l’ordre alphabétique
void Suivant(){
if (longueur <longueurmaxi) {longueur++;cont[longueur-1]=’a’;}
else {
while (cont[longueur-1]==’b’) longueur- -; // evt cont[longueur]=’ ’;
//remarque déclenche une exception si Estdernier() est vrai
Advertisement
cont[longueur-1]=’b’;
}}
//retourne vrai Ssi le mot est le dernier dans l’ordre alphabétique bbbb...bbbbb
boolean EstDernier(){
if (longueur<longueurmaxi) return false;
for (int i=0;i <longueur; i++) {
if (this.cont[i]==’a’) return false;
}
return true;
}
Exercice 4 : Plaques
Soit n plaques rectangulaires de dimension (xi, yi) avec xi ≥ yi, 0 ≤ i ≤ n − 1; ces plaques
ont toute la même épaisseur;
Q 1. On cherche à empiler le maximum de ces plaques en respectant la contrainte suivante:
la plaque j ne peut être empilée au-dessus de la plaque i que si xj ≤ xj et yj ≤ yi. Par
exemple, si on a 6 plaques de dimensions respectives (12, 7), (11, 8), (7, 7), (9, 6), (5, 5), (9, 4)
on ne pourra pas empiler toutes les plaques, mais on pourra empiler les plaques 0, 2, 4 dans
cet ordre (il y a d’autres solutions optimales comme 1, 2, 4 ). Le problème est donc:
Donnée: un entier n, le nombre de plaques
un tableau x de taille n contenant les longueurs des plaques
un tableau y de taille n contenant les largeurs des plaques
Sortie: J ⊂ [0, n − 1] de cardinal maximal tel que pour tout i de J et tout j de J, soit
x[i] ≤ x[j] et y[i] ≤ y[j], soit x[i] ≥ x[j] et y[i] ≥ y[j]
Q 1.1. A quelle condition peut-on empiler toutes les plaques en respectant la contrainte?
4
Il faut juste que pour tout i et tout j, si x[i] ≤ x[j], alors y[i] ≤ y[j]
Q 1.2.Glouton?... Pensez-vous que l’algorithme de type glouton suivant donne toujours la
solution optimale? Justifier.
x[i] décroissant.
Trier les plaques par
Initialiser J à l’ensemble vide;
Ajouter 0 à J;
largeur=y[0];
pour i de 1 à n-1
si y[i]< =largeur {
ajouter i à J;
largeur=y[i];}
Non, contre-exemple: (10, 1), (8, 3), (7, 2)
Q 1.3.ou dynamique? En utilisant la programmation dynamique, proposer un algorithme en
O(n2) pour résoudre le problème. Vous pourrez supposer les plaques triées par x[i] décroissant.
Remarque: vous pouvez répondre partiellement en calculant uniquement le nombre maxi-
mal de plaques qu’on peut empiler (i.e. card J).
// les plaques sont triées par $x[i]$ décroissant
int[] nb=new int [n];
//nb[i] sera le nombre
int[] suiv=new int[n];
//suiv[i] sera le no de la première plaque qu’on empile au-dessus de i dans une solution optimale.
int max=0; la hauteur maxi d’une pile
int indmax;
for (int i=n-1;i>=0;i- -)
de plaques maximum qu’on peut empiler au-dessus de la plaque i
nb[i]=0;
suiv[i]=i;
for (int j=i+1; j<n;j++} {
if (y[i]>=y[j]) && (nb[j]>=nb[i]) {
nb[i]=1+nb[j];suiv[i]=j}
}
if (nb[i]>=max) max=1+nb[i]; indmax=i;}
}
ensemble J;
ind=indmax;
J.ajouterr(ind); // première plaque est plaque ind
while nb[ind]>0 {ind=suiv[ind];J.ajouter(ind);} //empiler plaque ind
return(J);
}
Remarque: cela correspond exactement à l’algorithme de recherche d’une plus longue sous-suite
croissante, qui était l’objet d’un exercice d’une feuille de TD!
Q 2. On cherche maintenant à choisir un nombre minimum de plaques telle que pour toute
plaque, il existe une plaque parmi les plaques choisies qui puisse la recouvrir. Dans l’exemple
ci-dessus, on choisirait donc les plaques {0, 1}.
Donnée: un entier n, le nombre de plaques
Advertisement
x, un tableau de taille n contenant les longueurs des plaques
y , un tableau de taille n contenant les largeurs des plaques
5
Sortie: J ⊂ [0, n − 1] de cardinal minimal tel que pour tout i de [0, n − 1], il existe j dans
J tel que x[i] ≤ x[j] et y[i] ≤ y[j].
Remarque: On suppose que deux plaques différentes ont au moins une dimension différente,
c.à.d. si i (cid:54)= j, x[i] (cid:54)= x[j] ou y[i] (cid:54)= y[j].
Q 2.1. Quelle est la solution optimale pour (17, 5), (12, 7), (9, 5), (9, 7), (8, 6)?
(17, 5), (12, 7)
Q 2.2. Peut-il y avoir deux solutions optimales différentes pour la même donnée? Justifier.
Non! Supposons qu’on ait deux solutions différentes S1, S2. Donc, il existe une plaque i qui
est dans l’une et qui n’est pas dans l’autre: on peut supposer sans perte de généralité qu’elle est
dans S1 et pas dans S2. Cette plaque i est couverte par une plaque j de S2 puisque S2 est solution:
x[i] ≤ x[j], y[i] ≤ y[j] avec x[i] < x[j] ou y[i] < y[j] d’après l’hypothèse: deux plaques différentes
ont au moins une dimension différente. Mais comme S1 est solution la plaque j est couverte par une
plaque k de S1: x[j] ≤ x[k], y[j] ≤ y[k]. Donc x[i] ≤ x[k], y[i] ≤ y[k] avec x[i] < x[k] ou y[i] < y[k] : la
plaque i est couverte par la plaque k de S1: mais alors S1 privée de la plaque i est toujours solution
-puisque toute plaque recouverte par i l’est par k- et S1 ne serait pas optimale.
Q 2.3. Proposez un algorithme de type glouton pour le problème. Justifiez sa correction et
analysez sa complexité.
x[i] décroissant, PUIS par y[i] décroissant en cas d’égalité des x
Trier les plaques par
Initialiser J à l’ensemble vide;
Ajouter 0 à J;
largeurmaxi=y[0];
pour i de 1 à n-1
si y[i] >largeurmaxi {
ajouter i à J;
largeurmaxi=y[i];}
Preuve: l’ensemble J ainsi construit est bien une couverture puisque si une plaque n’est pas ajoutée,
c’est qu’elle est couverte par une plaque examinée auparavant. C’est bien une couverture optimale:
quand on ajoute i à J, on est sûr qu’elle ne peut être couverte par aucune autre plaque: une plaque
qui la couvrirait aurait un x >x[i] et un y >= y[i] ou un x >x[i] et un y >=y[i] ( car elles sont
toutes différentes): mais alors on aurait déjà examinée cette plaque, elle serait donc couverte et on
aurait largeurmaxi>=y[i].
Exercice 5 : Processus
Soit un système temps réel avec n processus asynchrones et m ressources. Quand un processus
est actif, il bloque un certain nombre de ressources et une ressource ne peut être utilisée que
par un seul processus. On cherche à activer simultanément k processus.
Le problème de décision DecProc est donc:
Donnée:
n, le nombre de processus
m le nombre de ressources
pour chaque processus i, la liste Pi des ressources qu’il bloque.
k le nombre de processus que l’on souhaite activer
Sortie:
Oui, si on peut activer k processus simultanément, non sinon.
6
Par exemple si n = 4, m = 5, et P1 = {1, 2}, P2 = {1, 3}, P3 = {2, 4, 5}, P4 = {1, 2, 4} on
peut activer simultanément les processus 2 et 3 et donc la réponse est Oui pour k = 2 mais
la réponse est N on pour k = 3.
Q 1. Montrer que le problème DecProc est N P .
Exemple de solution:
Remarque préliminaire: la taille du problème est au moins n + m.
Un certificat est simplement un ensemble de processus, donc un sous-ensemble de {1, ..., n}. Un
certificat peut donc être codé par un vecteur de n booléens et la taille d’un certificat est donc n, donc
inférieure à la taille du problème.
La vérification consiste juste à vérifier que deux processus actifs ne partagent pas une ressource,
et que le cardinal est bien k, soit:
//probleme donne par n, P1, ...Pi
//certificat : ensemble de processus
boolean correct(certificat certif){
ensemble R=ensemble vide; //les ressources utilisees
int card=0; // pour calculer card (certif), le nb de processus actives
pour i de 1 à n
si certif.appartient(i) alors{
card++;
si intersection( Pi, R) est non vide
alors
sinon R=union(R, Pi);}
return False; //conflit sur au moins une ressource
Advertisement
fsi
fpour
return (card == k);}
L’algorithme de vérification est bien polynômial; sa complexité exacte dépend de la complexité de
appartient(), intersection(,) et union(,) mais sera de toute façon polynômiale, le coût de ces
opérations étant polynômial
Remarque 1: si on veut préciser cette complexité -ce qui était non demandé-, soit Pa(x) (resp.
Pin(x), Pu(x)) bornant la complexité de appartient() pour un ensemble de cardinal au plus x (resp.
le test du vide de l’intersection(resp. le calcul de l’union) de deux ensembles de cardinal au plus x), le
coût de l’algo est en O(n ∗ (Pa(n) + Pin(m) + Pu(m))2) soit O(n ∗ (Pa(t) + Pin(t) + Pu(t))2), si t est
la taille du problème.
Remarque 2: pour beaucoup, la notion de certificat semble encore confuse; un certificat est juste
"un essai de solution", pas forcément une solution correcte;
Q 2. En utilisant le fait que le problème Independent Set étudié en TP est N P -dur,
montrer que le problème DecProc est N P -dur.
Remarque: Attention à ne pas se tromper de sens dans la réduction!!!!!
Il fallait réduire Independent Set , connu N P -dur, dans DecProc, et non le contraire.
Il faut donc associer à toute instance I de Independent Set , une instance red(I) de DecProc
telle que red(I) soit positive Ssi I l’est.
Une instance de Independent Set est la donnée d’un graphe (Sommets, Arcs) et d’un entier k0.
On définit red(I) par:
n, le nombre de processus par n = card(Sommets)
m le nombre de ressources p = card(Arcs)
pour chaque processus i, la liste Pi des ressources qu’il bloque par Pi = l’ensemble des arcs
adjacents au sommet i.
k le nombre de processus que l’on souhaite activer par k = ko
La réduction est bien polynomiale (il faut juste calculer card(Sommets), card(Arcs) et pour chaque
sommet, les arcs adjacents).
7
Elle est bien exacte: si I est une instance positive de Independent Set, il existe donc un
sous-ensemble de cardinal k de sommets indépendants:
les k processus associés ne partagent donc
aucune ressource et forment donc un sous-ensemble de cardinal k de processus qu’on peut activer
simultanément: red(I) est positive.
Réciproquemment, si red(I) est positive, il existe un sous-ensemble de k processus qu’on peut
activer simultanément: les k sommets associés ne partagent donc pas d’arcs et sont indépendants: I
est bien positive.
Q 3. Que pensez-vous de la complexité du problème DecProc si chaque processus utilise
une seule ressource?
Le problème devient bien sûr polynômial et peut par exemple être résolu par un glouton:
//probleme donne par n, P1, ...Pi
//certificat : ensemble de processus
boolean solve(){
ensemble R=ensemble vide; //les ressources utilisees
int card=0; // pour calculer e nb de processus actives
pour i de 1 à n
si intersection( Pi, R) est
alors { card++; R=union(R, Pi);}
vide
fsi
fpour
return (card >= k);}
Q 4. Que pensez-vous de la complexité du problème DecProc si chaque ressource est utilisée
par au plus deux processus?
Il est toujours N P −dur puisque, dans les instances obtenues par réduction dans la question 2,
chaque ressource (un arc) est utilisée par au plus deux processus (ses extrémités).
Q 5. Que pensez-vous de la complexité du problème d’optimisation associé OptProc:
Donnée:
n, le nombre de processus
m le nombre de ressources
pour chaque processus i, la liste Pi des ressources qu’il bloque.
Sortie:
k maximum tel qu’on puisse activer k processus simultanément.
On peut dire qu’il est N P − dur: si on avait un algorithme polynômial pour le problème d’optimisation,
on en aurait un pour celui de décision! (puisque (I(n, m, (P i), k).DecP roc() = (I(n, m, (P i)).OptP roc() >=
k)).
8