INF431
Algorithmes gloutons
CORRIGÉ
Version: 1643:2185M
Les exercices de cette feuille sont l’occasion de revenir sur différents points traités en cours.
1 Minimum spanning tree dynamique
On considère un graphe pondéré G= (X, E, w)ànsommets et marêtes, dont on a calculé un
arbre couvrant Ade poids minimum (à l’aide par exemple de l’algorithme de Kruskal).
Question 1 Supposons dans un premier temps que les poids des arêtes sont tous distincts. Montrer qu’il
existe alors un unique arbre couvrant de poids minimum. (indication : considérer deux arbres minimaux
distincts et les utiliser pour en construire un troisième de poids strictement inférieur)
Solution. Supposons qu’on ait deux arbres couvrants minimaux distincts Aet A0. Soit eune arête de
A0\A. Par définition des arbres A∪ {e}contient un unique cycle simple C= (e, e1, . . . , ek), qui est
de longueur k+ 1 3. Par ailleur, pour tout i,w(e)> w(ei)(sinon A\ {ei} ∪ {e}serait un arbre
couvrant de poids inférieur à A).
Le cycle Ccontient eet donc intersecte les deux composantes connexes de A0\{e}. En particulier il
existe au moins une autre arête eide Cqui lie ces deux composantes connexes. Mais alors A0\{e}{ei}
est un arbre (puisque c’est, comme A0, un graphe connexe avec un sommet de plus que d’arêtes). Or cet
arbre est de poids inférieur à A0, ce qui contredit l’hypothèse de départ.
Question 2 Considérons une arête ede Gqui n’est pas dans Aet diminuons son poids d’une quantité
α > 0. Donner une condition nécessaire et suffisante sur αpour que l’arbre couvrant minimum ne
change pas.
Solution. Si l’arbre Areste couvrant minimal dans Gavec les nouveaux poids, alors en particulier
Asatisfait la condition (*) suivante : pour tout e0dans l’unique cycle simple Cde A∪ {e}, on doit
avoir w(e0)< w(e)α(sinon A\ {e0} ∪ {e}serait un arbre de poids inférieur). Montrons que cette
condition nécessaire est en fait suffisante. L’unique arbre couvrant minimum Ade Gavec les anciens
poids peut être construit par l’algorithme de Kruskal. Mais si αsatisfait la condition (*), l’exécution de
cet algorithme sur Gmuni des nouveaux poids sera essentiellement inchangée : l’arête e0sera peut-être
considérée un peu plus tôt mais toujours après les autres arêtes de Cet elle sera donc toujours rejetée.
On obtiendra donc le même arbre.
Question 3 Donner le pseudo-code d’une procédure qui, étant donnés G,A,eet α, teste si l’arbre
change. On supposera que les sommets sont indicés de 1 à net que l’arbre est représenté par un tableau
qui à chaque sommet associe le numéro de son père et le poids de l’arête correspondante. Quelle est la
complexité de votre solution en nombre d’opérations.
Solution. Pour tester la condition (*) il faut parcourir le cycle C. La complexité de ce parcours dépend
de la représentation de l’arbre A.
Avec la représentation proposée, on trouve le cycle en temps O(n): par exemple si e={x, y}
on remonte à la racine depuis xen marquant les sommets rencontrés, puis on remonte de yà la
racine jusqu’à rencontrer un sommet marqué z, enfin on recommence entre xet zet yet zpour
vérifier la condition (*). Le pseudo code pourrait être :
1
Procedure Test(t,e,alpha)
soit e=(x,y) ;
z:=x ; marquer z
tant que z<>racine faire
z:=t[z].pere
marquer z
z:=y ;
tant que z n’est pas marqué faire
si t[z].poids > w(e)-alpha alors renvoyer faux
z:=t[z].pere
ancetre:=z
z:=x
tant que z<>ancetre faire
si t[z].poids > w(e)-alpha alors renvoyer faux
z:=t[z].pere
renvoyer vrai
Remarquons que si An’était représenté qu’implicitement par le marquage de certaines arêtes de
G, alors le coût serait en O(m): on doit effectuer les même opérations que précédemment mais
en chaque sommet on doit parcourir les arêtes incidentes pour trouver celle qui est marquée.
Question 4 Donner le pseudo-code d’un algorithme qui calcule le nouvel arbre couvrant minimum
obtenu lorsque αne respecte pas la condition de la question précédente. Justifier la validité de votre
algorithme (On pourra comparer l’exécution de Kruskal avec les anciens et les nouveaux poids).
Solution. Si αest trop grand, l’arbre devient A\{ej}∪{e}avec jtel que w(ej) = max(w(e1), . . . , w(ek)).
En pseudo-code
Procedure Update(t,e,alpha)
soit e=(x,y) ;
z:=x ; marquer z
tant que z<>racine faire
z:=t[z].pere
marquer z
z:=y ; my:=w(e)-alpha;
tant que z n’est pas marqué faire
si t[z].poids > my faire my=t[z].poids
z:=t[z].pere
ancetre:=z
z:=x ; mx:=w(e)-alpha
tant que z<>ancetre faire
si t[z].poids > mx faire mx=t[z].poids
z:=t[z].pere
m=max(mx,my)
si m<w(e)-alpha alors terminer
si m=mx alors p=y ; z=x ; pd=w(e)
sinon p=x ; z=y ; pd=w(e)
tant que t[z].poids <> m faire
f=t[z].pere ; npd = t[z].poids ;
t[z].pere = p ; t[z].poids = pd ;
p=z; z=f ; pd = npd
t[z].pere = p ; t[z].poids = pd ;
L’exécution de l’algorithme de Kruskal sur Gavec le nouveau w(e)est la même jusqu’à ce qu’on
atteigne e, qui est acceptée dans le nouvel arbre. L’exécution se poursuit alors de manière identique
2
tant qu’on a pas atteint ej: en effet toutes les arêtes qu’on acceptait dans Aavant ejcontinuent d’être
acceptée car le seul nouveau cycle possible est Cqui nécessite ej, et toutes les arêtes qu’on refusait
dans Aavant ejcontinuent a fortiori d’être refusée maintenant qu’on a une arête de plus. Lorsqu’on
atteint ejon doit le rejeter à cause du cycle C: l’ensemble des composantes connexes est à ce point
exactement le même dans les constructions de Aet A0donc l’exécution se termine identiquement dans
les deux cas.
Question 5 Mêmes questions dans le cas où on augmente de α > 0le poids d’une arête equi est dans
l’arbre couvrant minimum.
Solution. La forêt A\ {e}est constituée de deux composantes connexes C1et C2. Soit E0l’ensemble
des arêtes de Gjoignant ces deux composantes. En particulier E0contient e. Soit e0l’arête de poids
minimum dans E: si αest trop petit (plus précisément si w(e) + αw(e0)), e0=eet A0=Asinon
A0=A\ {e}∪{e0}. Pour tester cette condition il faut maintenant nécessairement un temps O(m).
La preuve est exactement la même que pour la question précédente : on analyse l’exécution de
Kruskal dans les deux cas et on trouve comme seule différence l’inclusion de e0au lieu de e.
Question 6 Peut on supprimer l’hypothèse selon laquelle les arêtes sont de poids distincts ?
Solution. Si l’arbre courant a été obtenu par Kruskal, les arêtes de même poids ont été utilisées dans
un certain ordre par l’algorithme. On peut modifier de manière infinitésimale les poids pour les rendre
croissants en respectant cet ordre, de façon à se ramener au pb précédent.
En fait on peut montrer que tout arbre couvrant minimum peut être obtenu par Kruskal : il suffit là
encore de modifier de manière infinitésimale les poids des arêtes pour les rendre distincts prenant soin
lorsque plusieurs arêtes ont le même poids de donner des poids inférieurs aux arêtes de l’arbre désiré.
On a alors un arbre couvrant minimum unique qui peut être obtenu par Kruskal, puis en annulant les
modifications mais en gardant l’ordre induit sur les arêtes de poids identiques on obtient une exécution
de Kruskal qui donne le bon arbre.
2 Set cover, échec du glouton mais facteur log nde l’optimum
Le gérant d’un nouveau magasin de meubles veut créer son réseau de livraison à domicile : chaque
livreur candidat couvre un certain ensemble de nvilles, le gérant doit en choisir un nombre minimum
tout en s’assurant que toutes les destinations soient couvertes.
Formellement, le problème de recouvrement d’ensemble (ou set cover) est le suivant :
Donnée : un ensemble de msous-ensembles S1, . . . , Smde {1, . . . , n}
Problème : trouver un sous-ensemble Ide cardinalité minimale de {1, . . . , m}tel que les Si,
iIcouvrent {1, . . . , n}:SiISi={1, . . . , n}.
On considère l’algorithme glouton qui sélectionne des sous-ensembles itérativement en ajoutant à
chaque étape un Siparmi ceux qui couvrent le plus d’éléments encore non couverts.
Question 7 Construire un exemple pour lequel cet algorithme ne donne pas l’optimum.
Solution. S1={1,2,3};S2={1,4};S3={2,5};S4={3,6}
Question 8 Montrer que si la couverture optimale est formée de ksous-ensembles, alors il existe un
sous-ensemble contenant au moins n/k éléments.
Solution. Max >Moyenne.
Question 9 Soit njle nombre d’éléments non couverts après que l’algorithme glouton ait sélectionné
jsous-ensembles. Montrez que njnj1(1 1/k), puis que nj< nej/k et en déduire une borne
sur le nombre d’étapes nécessaires à l’algorithme glouton pour couvrir tout l’ensemble.
3
Solution. La première inégalité demandée vient en adaptant la question précédente à la jème étape.
En effet, il reste alors nj1éléments non couverts et la solution optimale utilise kjsous-ensembles
pour les couvrir, donc au moins un sous-ensemble contenant nj1/(kj)nj1/k éléments encore
non couverts. Comme l’algorithme glouton est glouton il couvrira au moins ce nombre d’éléments et
nj< nj1nj1/k.
Pour continuer on utilise l’inégalité bien connue 1x<expour x > 0, appliquée à nj
n(1 1/k)j(déduite par récurrence de la précédente).
Enfin on déduit de nj< nej/k que nj= 0 si j > k log n: l’algorithme glouton s’arête après au
plus klog nétapes et utilise donc au plus log nfois plus d’ensembles que nécessaire.
Question 10 Montrer que pour des valeurs de nbien choisies (mais arbitrairement grande), il existe une
instance du problème précédent tel que : il y a néléments à couvrir ; le recouvrement optimal utilise
deux ensembles ; l’algorithme glouton utilise de l’ordre de log2nensembles.
Solution. Cherchons à construire le contre-exemple sur {1, . . . , n}: l’optimal doit être formé de deux
sous-ensembles, prenons le sous-ensembles Pdes entrées paires et celui Ides entrées impaires. Pour
tromper le glouton on va ajouter dans la collection des sous-ensembles un ensemble de taille en gros
n/2+2et recommencer avec les éléments restants. Pour simplifier les notations, on suppose que n=
2p+2pavec p > 1, et on considère l’ensemble des 2p1+p+2 derniers entiers : comme 2p1+p+2 >
bn/2c+ 1, glouton choisit ce sous-ensemble au lieu de Pou I. À l’étape suivante on est ramené à un
problème identique avec n= 2p1+ 2(p1) et le glouton continue de se tromper. Au final il utilise p
sous-ensembles au lieu de 2, soit de l’ordre de log2nsous-ensembles.
3 Satisfiabilité des formules de Horn
Une formule de la logique propositionnelle est une formule écrite à l’aide d’un ensemble de variables
et de connecteurs logiques ¬(la négation), (le et logique) et (le ou logique). Étant donnée une
formule on se demande s’il existe une façon de choisir une valeur vrai ou faux pour chaque variable de
sorte que la formule complète s’évalue à vrai. Ainsi la formule
(xy)(xz)(¬y∨ ¬z)
est satisfaite par l’affectation {x=V, y =F, z =V}, alors que la formule
(xy)(xz)(¬y∨ ¬z)∧ ¬x
n’est satisfaite par aucune affectation.
Question 11 Donner un algorithme de complexité m2npour déterminer si une formule de longueur m
sur nvariables est satisfiable.
Solution. Construire la table de vérité de la formule en l’évaluant pour toutes les 2naffectations pos-
sibles. Lorsqu’on aura vu le backtracking on pourra faire (un peu) mieux.
Appelons littéral une variable (littéral positif) ou une négation de variable (littéral négatif), et clause
une disjonction de littéraux. On écrit pour simplifier ¯xau lieu de ¬xpour les littéraux négatifs. Une
formule formée d’une conjonction de clauses (comme les exemples ci-dessus), est dite en forme normale
conjonctive. On peut montrer que toute formule de logique propositionnelle peut s’écrire en forme
normale conjonctive. Remarquons que satisfaire une formule en forme normale conjonctive revient à
trouver une affectation satisfaisant simultanément toutes les clauses de la formule, il est du coup naturel
de considérer une formule comme un ensemble de clauses.
On s’intéresse à une classe particulière de formules (en forme normale conjonctive), les formules de
Horn, dans lesquelles on se limite à deux types de clauses :
les implications positives, de la forme : y¯x1 · · · ∨ ¯xk(ou de manière équivalente (x1 · · · ∧
xk)y) où les x1, . . . , xket ysont des littéraux positifs
les négations pures, de la forme : ¯x1· · · ¯xk, (ou de manière équivalente (x1· · ·xk)faux).
4
Nous allons montrer qu’il existe un algorithme efficace pour décider de la satisfiabilité des formules
de Horn. Outre le fait qu’il s’agisse d’un joli exemple d’algorithme glouton, cet algorithme, dû à Dow-
ling et Gallier, peut être vu comme un cas particulier des algorithmes utilisés pour l’évaluation des
requêtes dans les bases de données déductives. En particulier on peut voir les formules de Horn comme
une forme simplifiée du langage Datalog.
Question 12 Montrer que l’algorithme glouton suivant décide correctement la satisfiabilité d’une for-
mule de Horn :
Affecter la valeur faux à toutes les variables.
Tant qu’il existe une implication non satisfaite (x1. . . xk)y, affecter la valeur vrai ày.
Si toutes les clauses négatives sont satisfaites renvoyer l’affectation obtenue sinon énoncer l’ab-
sence de solution.
Solution. L’algorithme termine car chaque tour de boucle augmente strictement le nombre de variables
prenant la valeur vrai. (Si l’implication (x1. . .xk)yn’est pas satisfaite, c’est que ya actuellement
la valeur faux. Donc, en attribuant la valeur vrai ày, on augmente strictement le nombre de variables
prenant la valeur vrai.)
Si l’algorithme renvoie une affectation alors par construction toutes les clauses sont satisfaites.
L’invariant suivant permet de faire la preuve que si l’algorithme énonce l’absence de solution, il n’y
a effectivement pas de solution :
Si une variable a actuellement la valeur vrai, alors elle a la valeur vrai dans toute affectation
satisfaisant la formule.
Cette propriété est vraie au départ et reste vraie à chaque nouvelle affectation : en effet le fait d’affecter
à vrai la valeur d’une nouvelle variable ne fait que faire grandir l’ensemble des clauses d’implication
dont le membre gauche est satisfait.
L’invariant affirme que l’affectation proposée est “inférieure” (au sens faux<vrai composante par
composante sur chaque variable) à n’importe quelle affectation valide. Or si la vérification finale échoue
pour une affectation, elle échouera a fortiori pour toute affectation supérieure. Il n’y a donc pas d’affec-
tation valide dans ce cas.
Question 13 Expliquer comment implanter l’algorithme de résolution ci-dessous pour qu’il fonctionne
en temps linéaire en le nombre de littéraux et de clauses. (Afin de justifier la complexité de l’algorithme
on pourra par exemple associer un graphe à la formule et indiquer comment on représente ce graphe
en mémoire.)
Solution. À chaque clause on associe un compteur indiquant le nombre des littéraux à gauche de l’im-
plication qui prennent la valeur faux dans l’affectation courante.
On démarre en mettant toutes les clauses sans argument dans une liste des implications à satisfaire.
Lorsqu’on traite une implication, deux cas se présentent : si la variable à droite de l’implication prend
déjà la valeur vrai, il n’y a rien de plus à faire ; sinon on lui affecte la valeur vrai, et on décrémente le
compteur associé à toutes les implications dans lesquelles cette variable apparaît à gauche de l’implica-
tion. Si un compteur atteint zéro, la clause associée est basculée dans la liste des clauses à satisfaire. On
s’arrête dès que la liste des clauses à traiter devient vide. Il reste à évaluer les clauses négatives, ce qui
se fait en temps linéaire.
Chaque décrémentation de compteur est associée à un littéral d’une clause, le nombre de décrémen-
tations est donc linéaire en la taille de la formule. Chaque clause est traitée au plus une fois. Il reste
donc uniquement à préciser qu’on peut faire les décrémentations en temps constant par décrémentation
à condition de prévoir un prétraitement : en temps linéaire on associe à chaque variable la liste des
clauses la contenant. Autrement dit on travaille avec le graphe ayant un sommet par clause de type im-
plication et un sommet par variable, et des arêtes de xàCsi xest à gauche de l’implication et de Cày
si yest à droite de l’implication. La représentation de ce graphe par liste d’adjacence est bien de taille
linéaire en la taille de la formule et peut être construite en temps linéaire.
5
1 / 5 100%