qui - Dipartimento di Informatica

Transcript

qui - Dipartimento di Informatica
Università degli Studi di Salerno
Note per il Corso di
Algoritmi e Strutture Dati II
Parte B
Docente: Ugo Vaccaro
Corso di Laurea in Informatica – Anno Accademico 2004-2005
Indice
Lezione 1:
Definizione di algoritmo di approssimazione; Vertex Cover; Massimo Sottografo Aciclico; Maximum Cut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . pag. 1
Lezione 2:
Set Cover; Copertura Massima . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . pag. 9
Lezione 3:
Steiner Tree; TSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .pag. 17
Lezione 4:
K-Centro; PTAS; FPTAS; Zaino 0-1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . pag. 28
Lezione 5:
Algoritmi di approssimazione via arrotondamento della soluzione di sistemi di PL; Vertex Cover;
Set Cover; Set Multicover; Hitting Set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . pag. 36
Lezione 6:
Arrotondamento probabilistico; Set Cover; Max-Sat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . pag. 45
Lezione 7:
Principio della Dualità in Programmazione Lineare; Set Cover via Dual Fitting; Hitting Set via
slackness primale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . pag. 54
Lezione 8:
Algoritmi di approssimazione via Primale-Duale; Vertex Cover; Set Cover . . . . . . . . . . . . . pag. 65
Lezione 9:
Algoritmi on-line; Analisi competitiva di algoritmi; Gestione della Cache; 2-competitività di LRU;
Ottimalità di LRU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . pag. 72
Lezione 10:
Accesso e Gestione di Liste dinamiche; 2-competitività di MTF; ottimalità di MTF . . . . pag. 81
Lezione 11:
Scheduling on-line; Bin Packing on-line; Analisi competitiva del Problema dell’affitto di sci e del
Problema della Compravendita di azioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . pag. 89
1
ASDII:Parte B — Lezione 1
Algoritmi e Strutture Dati II: Parte B
Anno Accademico 2004-2005
Lezione 1
Docente: Ugo Vaccaro
Dalla Teoria della NP-Completezza abbiamo appreso che esiste una classe di problemi
(NP-hard) per cui non è noto alcun algoritmo di soluzione di complessità polinomiale ed
è inoltre molto improbabile che per tali problemi possa essere mai trovato un algoritmo
di soluzione di complessità polinomiale. Tuttavia, la classse dei problemi NP completi
contiene molti problemi di grande importanza pratica. Per essi è molto importante dover
produrre una “qualche soluzione”, independentemente dal fatto che la soluzione “migliore”
sia difficile da trovare o meno.1 Tipicamente, ci sono tre differenti approcci che si possono
seguire quando ci si trova di fronte alla necessità di trovare soluzioni accettabili in pratica
per problemi NP completi .
1. Se l’input al problema dettato dalla specifica situazione pratica in cui il problema
sorge è piccolo, allora si può provare a trovare la “migliore” soluzione mediante
una ricerca esaustiva, che essenzialmente genera tutte le possibili soluzioni associate
all’istanza di input e poi se ne sceglie la migliore. Tale approccio genera algoritmi
con complessità esponenziale. Tuttavia, se l’input è piccolo, in pratica può produrre
soluzioni in un tempo ragionevole.
2. Il fatto che un problema sia NP completo essenzialmente implica che è molto improbabile che esista un algoritmo di complessità polinomiale che produce la soluzione
desiderata in corrispondenza ad ogni possibile istanza di input. Può capitare, però
, che per “specifiche” particolari istanze di input, un tale algoritmo possa essere
trovato. Pertanto, una analisi accurata delle possibili istanze di input che possono
“ragionevolmente” accadere in pratica, può portare alla elaborazione di algoritmi
efficienti.
3. Il terzo approccio consiste nel progettare algoritmi che producano una soluzione
in tempo polinomiale, per ogni possibile istanza di input, non necessariamente di
valore ottimo, ma che si discosti dal valore ottimo per “poco”, e che tale “poco” sia
stimabile in funzione della taglia dell’input.
In questo corso seguiremo il terzo approccio.
Precisiamo i concetti sopra esposti. Innanzitutto diciamo che ci occuperemo di problemi di ottimizzazione. Informalmente un problema di ottimizzazione Π è definito da:
• un insieme IΠ di possibili istanze di input;
1
Per il momento la discussione è da intendersi ad un livello molto informale, successivamente daremo
un senso preciso ai termini “migliore soluzione” e “qualche soluzione” per i problemi NP completi che
considereremo.
2
ASDII:Parte B — Lezione 1
• ad ogni possibile istanza di input i ∈ I Π è associato un insieme di possibili soluzioni
SΠ (i). É definita inoltre una funzione costo che in corrispondenza ad ogni possibile
istanza di input i ∈ IΠ associa ad ogni soluzione s ∈ SΠ (i) un costo c(s) ∈ R+ .
In linea di principio, l’obiettivo sarebbe di trovare in maniera efficiente, per ogni istanza di
input i ∈ IΠ , una soluzione di minimo costo (o massimo costo, a seconda che il problema
sia di minimo o di massimo, rispettivamente), associata alla istanza i. Sia OPT Π (i) il
costo di una tale soluzione. Per i problemi di ottimizzazione che studieremo in questo
corso, calcolare OPTΠ (i) è difficile, ovvero il corrispondente problema di decisione è NP
completo (vedi ASD2 parte A).
Esempio 1 Un classico esempio di problema di ottimizzazione è il problema del Minimum
Spanning Tree . L’insieme delle possibili istanze di input a Minimum Spanning Tree
consiste di tutte le possibili coppie (G, w), dove G = (V, E) è un grafo con insieme di
vertici V ed insieme di archi E, e w : E → R + è una funzione peso che associa numeri non
negativi agli archi nell’insieme E. Per ogni possibile istanza di input i = (G, w), l’insieme
delle possibili soluzioni S(i) consiste di tutti i sottoalberi di G che contengono tutti i
vertici in V . Ad ogni possibile soluzione s ∈ S(i) (ovvero ad ogni possibile sottoalbero T
di G che contiene tutti i vertici di G) è associato come costo il valore c(s) pari alla somma
dei pesi secondo la funzione w degli archi di T . Il problema di ottimizzazione consiste nel
trovare per ogni i = (G, w) un albero di minimo costo in S(i).
Esempio 2. Un altro classico problema di ottimizzazione è il problema del Vertex
Cover . L’insieme delle possibili istanze di input a Vertex Cover consiste di tutti i
possibili grafi G = (V, E). Per ogni grafo G, l’insieme delle possibili soluzioni ad esso
associato consiste di tutti i sottoinsiemi S ⊆ V tali che per ogni arco e ∈ E, almeno uno
dei due vertici incidenti su e appartiene a S. Ogni tale S viene chiamato vertex cover di
G. Il costo di una soluzione S ⊆ V associata al grafo G corrisponde alla sua cardinalità
|S|. Il problema di ottimizzazione consiste nel trovare per ogni grafo G un vertex cover di
minima cardinalità .
Informalmente, un algoritmo di approssimazione A per un problema di ottimizzazione
Π produce, in tempo polinomiale ed in corrispondenza ad ogni istanza di input di Π, una
soluzione di valore (costo) “prossimo” all’ottimo, dove per prossimo intendiamo che differisce dal valore ottimo per un certo fattore, sperabilmente piccolo. Precisiamo maggiormente questi concetti, differenziando tra problemi di ottimizzazione di minimo e problemi
di ottimizzazione di massimo.
Definizione. Sia Π un problema di ottimizzazione di minimo, e sia ρ : N → R + , con
ρ(n) ≥ 1, per ogni n ∈ N . Un algoritmo A è detto essere un algoritmo di approssimazione
con fattore di approssimazione ρ per il problema di ottimizzazione Π se, per ogni istanza
di input i di Π l’algoritmo A produce in tempo polinomiale nella taglia dell’input |i| una
soluzione s ∈ SΠ (i) tale che il valore della funzione costo sulla soluzione s soddisfi
c(s) ≤ ρ(|i|)OP TΠ (i)
3
ASDII:Parte B — Lezione 1
Chiaramente, più prossimo a 1 è ρ(|i|) e migliore è l’approssimazione prodotta
dall’algoritmo. Analoga è la definizione di algoritmo di approssimazione per problemi
di ottimizzazione di massimo.
Definizione. Sia Π un problema di ottimizzazione di massimo, e sia ρ : N → R + , con
ρ(n) ≥ 1, per ogni n ∈ N . Un algoritmo A è detto essere un algoritmo di approssimazione
con fattore di approssimazione ρ per il problema di ottimizzazione Π se, per ogni istanza
di input i di Π l’algoritmo A produce in tempo polinomiale in |i| una soluzione s ∈ S Π (i)
tale che il valore della funzione costo sulla soluzione s soddisfi
c(s) ≥
OP TΠ (i)
ρ(|i|)
Per semplicità , denoteremo con SOL il valore della soluzione prodotta dagli algoritmi
di approssimazione che esamineremo, e con OP T il valore della soluzione ottima.
Dalla definizione di algoritmi di approssimazione discende che per provare che un
algoritmo A abbia un fattore di approssimazione ρ occorre provare che il costo della
soluzione trovata da A si discosta dal valore ottimo OP T della soluzione al più per un
fattore ρ. Si pone il problema di come dimostrare questo fatto, visto che per i problemi
che studieremo non conosciamo ovviamente a priori OP T né riusciamo a calcolarlo. Il
“trucco” consiste nello stimare OP T con quantità di più facile calcolo. Vediamo come
farlo nel seguente problema.
VERTEX COVER
• Input:
– grafo G = (V, E)
• Output: sottoinsieme S ⊆ V di minima cardinalità |S| tale che ∀(u, v) ∈ E valga
{u, v} ∩ S 6= ∅.
L’intuizione alla base di un algoritmo di approssimazione per il problema del Vertex
Cover è la seguente. Se nel grafo G abbiamo un certo numero k di archi disgiunti, ovvero
di archi in cui nessuno di essi è adiacente all’altro, allora occorreranno almeno k vertici
per coprirli tutti. Ora, un insieme M ⊆ E di archi disgiunti viene chiamato matching.
L’osservazione appena fatta ci permette di trarre la seguente conclusione: ogni Vertex
Cover per G deve essere grande almeno quanto ogni matching di G. Ciò vale ovviamente
anche per il Vertex Cover ottimo (ovvero quello di cardinalità minima). Sia OP T la
cardinalità del Vertex Cover di taglia minima. Abbiamo quindi appena dimostrato che
OP T ≥ |M |,
per ogni M matching in G.
(1)
Quindi la (1) ci ha permesso di ottenere una prima stima di OP T che useremo per valutare
il fattore di approssimazione del nostro algoritmo. Diremo ora che un matching M è
4
ASDII:Parte B — Lezione 1
massimale se per ogni arco e ∈ E vale che M ∪ {e} non è più un matching. L’osservazione
chiave è che l’insieme A dei vertici che incidono sugli archi di un matching massimale
formano un Vertex Cover per G. Infatti, se ciò non fosse, ovvero se esistesse un arco
e ∈ E non coperto dai vertici di A, ciò vorrebbe dire che l’arco e non è adiacente ad alcun
arco in M , ovvero M ∪ {e} è ancora composto da archi non adiacenti, quindi M ∪ {e} è
ancora un matching, contraddicendo il fatto che M è un matching massimale. Quindi, il
seguente algoritmo produce un Vertex Cover .
• Costruisci un matching massimale M in G
• Output tutti i vertici che toccano gli archi di M
Valutiamo ora il fattore di approssimazione dell’algoritmo. Sia SOL il valore
della soluzione ritornata dall’algoritmo, ovvero il numero di vertici nell’insieme output
dell’algoritmo. Abbiamo che
SOL = 2|M |
≤ 2OP T per la (1)
e quindi l’algoritmo ha un fattore di approssimazione costante pari a 2. Resta da vedere
come costruire in tempo polinomiale un matching massimale in G. Ciò è semplicemente
fatto mediante il seguente algoritmo.
Algoritmo per la costruzione di un matching massimale
• M ← e, (e arco qualunque di E)
• Cancella nel grafo G tutti gli archi adiacenti a e
• Itera nel grafo rimanente fin a quando non diventa vuoto
• Output M
É istruttivo chiedersi se è possibile migliorare il fattore di approssimazione 2, mediante
un’analisi più accurata dell’algoritmo. La risposta è no. Si consideri il seguente esempio di
grafo in cui l’algoritmo da noi proposto produrrebbe una soluzione dal valore esattamente
pari a 2OP T .
Il grafo è un grafo bipartito con 2n nodi, in cui ogni nodo del lato sinistro è connesso ad
ogni noto del lato destro. Un Vertex Cover di cardinalità minima è dato da uno dei due
lati dei vertici, quindi OP T = n. D’altra parte, l’algoritmo considererebbe un matching
massimale di cardinalità n (rappresentato ad esempio dagli archi di maggior spessore nella
ASDII:Parte B — Lezione 1
5
figura di sopra) e di conseguenza la soluzione prodotta dall’algoritmo avrebbe cardinalità
SOL = 2n. Inoltre, esiste una classe di grafi in cui ogni matching massimale ha cardinalità
esattamente pari alla metà di un Vertex Cover ottimale. Tale classe di grafi è composta
da tutti i grafi completi2 su n nodi, con n dispari. Da ciò si evince che se si insiste ad
usare la tecnica del matching, non si potrà mai migliorare il fattore di approssimazione
2 appena provato. A tutt’oggi, algoritmi di approssimazione per Vertex Cover con
fattore di approssimazione costante inferiore a 2 non sono noti.
Esercizio 1
Presentiamo un altro algoritmo di approssimazione per il problema del Vertex Cover
con fattore di approssimazione pari a 2. L’algoritmo consiste dei seguenti passi:
Algoritmo alternativo per il calcolo del Vertex Cover
• Esegui una DFS sul grafo G
• Output: l’insieme S costituito da tutti i vertici dell’albero DFS tranne le foglie.
Mostriamo innanzitutto che l’insieme S output dall’algoritmo è un Vertex Cover del
grafo. Supponiamo per assurdo che non lo sia. Ciò implica che esiste un arco e = (u, v) ∈
E, con u e v entrambi foglie dell’albero DFS. Senza perdita di generalità , supponiamo che
u sia il primo tra i due vertici u, v ad essere stato visitato durante la visita DFS. Allora,
per come la DFS opera, v diventerà figlio di u nell’albero. Ciò contraddice il fatto che u
è una foglia dell’albero DFS.
Sia S l’insieme output dell’algoritmo sopra esposto. Ci resta da provare che |S| ≤
2OP T , dove OP T rappresenta la cardinalità di un Vertex Cover di taglia minima di
G. Proviamo innanzitutto che nell’albero DFS (e quindi anche in G) vi è un matching
M di cardinalità almeno |S|/2. Costruiamo il matching M nel seguente modo, partendo
dal nodo di livello 0 dell’albero, ovvero dalla radice r. Scegliamo un figlio arbitrario u di
2
Si ricordi che un un grafo è completo se esiste un arco tra ogni coppia di vertici.
6
ASDII:Parte B — Lezione 1
r e poniamo (r, u) in M . Iterando, al livello 1 prendiamo tutti i nodi non incidenti ad
archi già posti in M , se esistono, e per ciascuno di essi scegliamo un figlio arbitrario e
poniamo gli archi dei nodi al livello 1 ai loro figli cosi scelti nel matching M . Iteriamo fin
quando tutti i nodi interni dell’albero DFS non sono stati considerati. Abbiamo quindi
costruito un matching M i cui archi incidono su ognuno degli |S| nodi in S. Ogni arco di
M può incidere su al più due nodi di S, quindi |M | ≥ |S|/2. D’altra parte, sappiamo che
la cardinalità OP T di un minimo Vertex Cover per G soddisfa OP T ≥ |M |, quindi
otteniamo che |S| ≤ 2OP T .
Fino ad adesso abbiamo considerato problemi di minimo. Consideriamo ora un esempio
di problema di ottimizzazione di massimo
Esercizio 2
Consideriamo il seguente problema
Massimo Sottografo Aciclico
• Input:
– grafo diretto G = (V, E)
• Output: sottoinsieme A ⊆ E di massima cardinalità |A| tale che il grafo H = (V, A)
non contiene cicli.
Il problema è NP completo . Un algoritmo di approssimazione può essere il seguente.
Scriviamo i vertici V = {1, 2, . . . , n} da sinistra a destra su di una linea. Poniamo in A
tutti gli archi di G della forma (i, j) con i < j, ed in B tutti gli archi della forma (i, j)
con i > j. Ovviamente, A ∪ B = E, e A ∩ B = ∅. Di conseguenza, |A| + |B| = |E|, e
quindi o A oppure B ha cardinalità almeno |E|/2. Tale insieme di cardinalità almeno |E|/2
sarà l’output del nostro algoritmo. Esso è sicuramente una soluzione valida (ovvero è un
insieme di archi senza cicli in G, in quanto gli archi vanno tutti in una stessa direzione).
Detta SOL la sua cardinalità , avremo che
SOL ≥ |E|/2 ≥ OPT /2,
dato che ovviamente OP T ≤ |E|. Pertanto, il nostro algoritmo ha un fattore di approssimazione pari a 2.
Esercizio 3
Consideriamo il seguente problema
7
ASDII:Parte B — Lezione 1
Maximum Cut
• Input:
– grafo non diretto G = (V, E)
• Output: partizione dell’insieme dei vertici V in due sottoinsiemi S 1 , S2 , con S1 ∪
S2 = V , S1 ∩ S2 = ∅, in modo tale che il numero di archi c(S 1 , S2 ) tra S1 e S2 sia
massimizzato.
Il problema è NP completo . Un algoritmo di approssimazione per tale problema può
essere ottenuto mediante la tecnica delle ricerca locale, che consiste, data una possibile
soluzione, nel tentare di migliorarla mediante “piccole” modifiche di tipo locale. Nel nostro
caso, si parta con una arbitraria partizione di V nei due insiemi S 1 e S2 . Fin quando esiste
un vertice che portato da S1 a S2 (o da S2 a S1 ) aumenta il corrente valore c(S1 , S2 ), lo
si faccia. Si termina quando non esiste alcun vertice con questa proprietà.
Algoritmo per Maximum Cut
Sia (S1 , S2 ) una partizione arbitraria di V
While esiste un vertice x ∈ V tale che
c(S1 ∪ {x}, S2 − {x}) > c(S1 , S2 ) (oppure c(S1 − {x}, S2 ∪ {x}) > c(S1 , S2 ))
do S1 ← S1 ∪ {x}, S2 ← S2 − {x} (oppure S1 ← S1 − {x}, S2 ← S2 ∪ {x})
• Output la partizione (S1 , S2 )
Il numero di iterazioni dell’algoritmo è chiaramente al più pari a |E|, visto che ogni
iterazione del ciclo while aumenta il valore di c(·, ·), e questo non può superare |E|. Inoltre,
alla terminazione dell’algoritmo, ogni vertice x ∈ V avrà almeno deg(x)/2 vertici nell’altro
lato della partizione, dove deg(x) è il grado di x nel grafo G. Infatti, se ciò se non
fosse, allora sarebbe possibile portare x dal suo lato della partizione all’altro lato della
partizione ed aumentare il valore di c(S 1 , S2 ), contro l’ipotesi. Quindi, usando il fatto che
P
x∈V deg(x) = 2|E|, otteniamo che
2c(S1 , S2 ) =
X
(numero di archi che vanno da x all’altro lato della partizione) (2)
x∈V
≥
X deg(x)
x∈V
2
(3)
8
ASDII:Parte B — Lezione 1
=
1
2|E| = |E|.
2
(4)
Pertanto, il valore della soluzione S restituita dall’algoritmo soddisfa
SOL = c(S1 , S2 ) ≥
|E|
OP T
≥
.
2
2
Può essere istruttivo generalizzare l’esercizio al caso in cui si desideri trovare la partizione
di V in k classi con il massimo numero di archi tra vertici appartenenti a classi differenti.
Concludiamo la lezione con la prova del fatto che
X
deg(x) = 2|E|.
(5)
u∈V
La (5) è ovvia, una volta che si osservi che ogni arco (u, v) viene conteggiato due volte
nella somma che appare al membro sinistro della (5), la prima volta quando si somma
deg(u), la seconda quando si somma deg(v).
9
ASDII:Parte B — Lezione 2
Algoritmi e Strutture Dati II: Parte B
Anno Accademico 2004-2005
Lezione 2
Docente: Ugo Vaccaro
Tra le tecniche per il progetto di algoritmi di approssimazione un ruolo importante è
giocato dalla tecnica greedy. Tale tecnica produce una soluzione al problema in esame
in maniera iterativa, effettuando ad ogni passo la scelta che in quel momento sembra la
migliore. Detto in altri termini, se ci trovassimo di fronte ad un problema di ottimizzazione
in cui occorre massimizzare una data funzione f su di un dominio D, la tecnica greedy
costruirebbe in maniera iterativa una soluzione S = {s 1 , . . . , sn } ⊆ D, in maniera tale ad
ogni istante i viene scelto quell’elemento s i che, a “paritá di condizioni”3 , tra tutte le legittime scelte massimizzerebbe l’incremento f (s 1 , . . . , si ) − f (s1 , . . . , si−1 ). Analogamente
per problemi di minimo.
Illustriamo l’uso della tecnica greedy per algoritmi di approssimazione sull’importante
esempio del Set Cover .
SET COVER
• Input:
– insieme U = {e1 , . . . , en }, famiglia S = {S1 , . . . , Sk }, con Si ⊆ U , per i = 1, . . . , k;
– funzione costo c : S ∈ S → c(S) ∈ R+ ;
• Output: sottofamiglia S 0 ⊆ S di costo c(S 0 ) =
S
S∈S 0 S ⊇ U .
P
S∈S 0
c(S) minimo tale che
Il problema del Set Cover include, come casi particolari, molti problemi di ottimizzazione che occorrono in pratica. Anche il problema del Vertex Cover visto nella lezione
precedente è un caso particolare di Set Cover . Infatti, dato un grafo G = (V, E), se
poniamo U = E, e per ogni vertice v ∈ V poniamo S v = {e ∈ E : e incide su v}, otteniamo una famiglia S = {Sv : v ∈ V }, con funzione costo c(Sv ) = 1, per ogni v ∈ V . È
facile vedere che ogni Vertex Cover A ⊆ V per il grafo G corrisponde ad una qualche
sottofamiglia S 0 = {Sv : v ∈ A} ⊆ S che copre tutto U . Di conseguenza, un Vertex
Cover di cardinalità minima è equivalente ad un Set Cover di costo minimo per la
particolare istanza sopra considerata.
Un naturale algoritmo per Set Cover potrebbe essere il seguente: iterativamente, aggiungi a S 0 l’insieme S ∈ S che ha il miglior (nel senso di più basso) rapporto costo/(numero
3
tali condizioni dipenderanno ovviamente dal problema in questione
10
ASDII:Parte B — Lezione 2
di elementi di U coperti da S e non ancora coperti), rimuovi da U tutti gli elementi ricoperti da S e continua fin quando non abbiamo coperto tutto U . Più formalmente, possiamo
elaborare il seguente algoritmo
Algoritmo Greedy per Set Cover
1. C ← ∅, S 0 ← ∅
% (S 0 è il cover che vogliamo costruire, C è l’insieme degli elementi di U finora coperti)
2. While C 6= U do
• Trova l’insieme S ∈ S con il valore c(S)/(|S − C|) minimo
• Sia α ← c(S)/(|S − C|) tale valore minimo
• Per ogni e ∈ S − C poni prezzo(e) ← α
• Poni S 0 ← S 0 ∪ {S}, C ← C ∪ S
3. Output S 0
Analizziamo ora le performances dell’algoritmo. Numeriamo gli elementi di U
nell’ordine in cui l’algoritmo li copre per la prima volta e sia e 1 , e2 , . . . , en tale ordinamento. Proviamo innanzitutto il seguente risultato.
Lemma 1 Per ogni k ∈ {1, 2, . . . , n} vale che
prezzo(ek ) ≤
OPT
n−k+1
dove OPT è il costo della soluzione ottima a Set Cover , ovvero il costo della famiglia
di minimo costo che copre tutto U .
Dim. Consideriamo il momento in cui viene assegnato il prezzo all’elemento e k , ovvero
il momento in cui viene coperto ek per la prima volta. Ciò vuol dire che all’istante
precedente l’elemento ek non era ancora coperto, ovvero si era in una situazione del tipo
e , . . . , es , es+1 , . . . , ek , ek+1 , . . . en .
| 1 {z
C
}|
{z
U −C
}
(6)
Tutti gli insiemi della soluzione ottima non presi e messi in S 0 dall’algoritmo greedy,
copriranno tutto U − C (visto che tutti gli insiemi della soluzione ottima coprono tutto
U ), ad un costo al più OPT . Siano S1 , . . . , St tali insiemi. Vogliamo innanzitutto mostrare
che esiste un i ∈ {1, . . . , t} tale che
OPT
c(Si )
≤
.
|Si − C|
|U − C|
11
ASDII:Parte B — Lezione 2
Infatti, se ciò non fosse, avremmo che per ogni i vale la diseguaglianza
c(Si )
OPT
>
,
|Si − C|
|U − C|
da cui
OPT ≥
t
X
c(Si ) >
i=1
t
OPT X
|Si − C|.
|U − C| i=1
(7)
Ricordiamo ora che per gli insiemi S 1 , . . . , St vale che
t
[
i=1
ovvero
t
[
i=1
da cui
t
X
i=1
Si ⊇ U − C
(Si − C) ⊇ U − C
|Si − C| ≥ |
t
[
i=1
(Si − C)| ≥ |U − C|.
Ritornando alla (7) otteniamo che
OPT >
t
OPT X
|Si − C| > OPT
|U − C| i=1
il che è ovviamente assurdo.
Riassumendo, abbiamo quindi dimostrato che esiste un insieme S i non ancora scelto
dall’algoritmo greedy per cui vale che
OPT
c(Si )
≤
.
|Si − C|
|U − C|
L’algoritmo greedy sceglie ad ogni passo un insieme S che ha il miglior rapporto c(S)/(|S−
C|). Pertanto, l’algoritmo sceglierà sicuramente un insieme S che copre e k per cui
c(S)
OPT
≤
.
|S − C|
|U − C|
D’altra parte, quando ek verrà coperto per la prima volta, C contiene meno di k elementi
(vedi la (6)), ovvero |U − C| ≥ n − k + 1. Pertanto
prezzo(ek ) =
OPT
OPT
c(S)
≤
≤
.
|S − C|
|U − C|
n−k+1
12
ASDII:Parte B — Lezione 2
Usando questo risultato, possiamo provare che l’algoritmo greedy per Set Cover ha
un fattore di approssimazione pari a H n = 1 + 1/2 + 1/3 + · · · + 1/n, ovvero produce una
soluzione del valore SOL per cui
SOL ≤ Hn OPT .
(8)
Ricordiamo che ogni qualvolta mettiamo un insieme S nella soluzione, il suo costo viene
ripartito uniformemente tra i nuovi elementi che S copre, ponendo prezzo(e) = c(S)/(|S −
C|), per ogni e ∈ S − C, e quindi vale anche
X
prezzo(e) = c(S)
e∈S−C
da cui
n
X
prezzo(ei ) =
X
c(S) = SOL .
S∈S 0
i=1
Ma prima avevamo mostrato che prezzo(e k ) ≤ OPT /(n − k + 1), da cui otteniamo
SOL =
n
X
i=1
prezzo(ei ) ≤ OPT
n
X
i=1
1
= OPT
n−i+1
1 1
1
1 + + + ··· +
2 3
n
= OPT Hn .
Abbiamo quindi provato la (8).
Valutiamo ora la somma Hn =
Hn =
Pn
i=1
1/i in termini di quantità più familiari. Si ha
1 1
1 1 1 1 1 1
1
1
1
1
+
+ + + + + + +
+··· + .
+
+ ··· +
1
2
3
4
5
6
7
8
9
10
15
n
|{z}
| {z }
|
{z
} |
{z
}
gruppo 1 gruppo 2
gruppo 3
gruppo 4
In ciascun gruppo la somma degli elementi è maggiore di 1/2 e sicuramente al più 1. Nel
gruppo i-esimo ci sono 2i elementi. Quanti gruppi possiamo avere? Ne posso avere t + 1,
dove t è il più piccolo intero per cui 1 + 2 + 2 2 + · · · + 2t ≥ n, ovvero per cui 2t+1 − 1 ≥ n.
Da cui segue che t + 1 = dlog(n + 1)e < log n + 1. Abbiamo quindi t + 1 gruppi, ciascuno
di essi con somma al più 1, quindi in totale al somma degli elementi in tutti i gruppi è al
più log n + 1, ovvero
Hn < log n + 1.
Possiamo quindi concludere che la soluzione proposta dall’algoritmo greedy per Set
Cover ha un valore SOL per cui
SOL < (1 + log n)OPT .
Presentiamo ora un esempio di una istanza di Set Cover su cui l’algoritmo greedy
produce una soluzione che si discosta dall’ottimo per un fattore pari a log n. Da ciò
13
ASDII:Parte B — Lezione 2
discenderà che l’analisi prima fatta dell’algoritmo greedy non è migliorabile in generale.
Sia
U = {e1 , . . . , en }, S = {{e1 }, . . . , {en }, {e1 , . . . , en }}.
Inoltre, valga
c({e1 }) =
1
1
, c({e2 }) =
, . . . , c({en }) = 1, c({e1 , . . . , en }) = 1 + .
n
n−1
Eseguendo l’algoritmo greedy su questa istanza, l’output sarà composto dagli n insiemi
di cardinalità 1 ciascheduno, di costo totale pari a 1 + 1/2 + . . . + 1/n = H n , mentre la
soluzione ottima ha ovviamente costo pari a 1 + .
Concludiamo (per il momento) la nostra analisi del problema del Set Cover menzionando due fatti:
1. un’analisi che tenga in conto di altri parametri permetterebbe di dimostrare che il
valore SOL della soluzione prodotta dall’algoritmo greedy soddisfa la diseguaglianza
SOL < Hd OPT
dove d = max{|S| : S ∈ S}, il che, in certi casi, è migliore della (8);
2. esistono fondate ragioni che inducono a sospettare che algoritmi di approssimazione
per Set Cover con fattore di approssimazione del tipo (1 − ) log n, non esistono,
qualunque sia il valore del parametro .
Algoritmo Greedy per il problema della Copertura Massima
Vediamo ora un’ applicazione dell’algoritmo greedy a problemi di ottimizzazione massimo.
Consideriamo il cosiddetto problema della Copertura Massima .
Copertura Massima
• Input:
– insieme U = {u1 , . . . , un }, famiglia S = {S1 , . . . , Sm }, con Si ⊆ U per i = 1, . . . , m;
– funzione profitto w : u ∈ U → w(u) ∈ R+ ;
– intero k > 0;
k insiemi Si1 , . . . , Sik in S di profitto totale w(Si1 ∪ . . . ∪ Sik ) =
∪...∪Si w(u) massimo.
• Output:
P
u∈Si1
k
14
ASDII:Parte B — Lezione 2
Il problema può essere visto come una generalizzazione del problema dello zaino visto in
ASD1. La differenza sta nel fatto che mentre nel problema dello zaino possiamo prendere
un qualunque elemento di U , nel problema della Copertura Massima possiamo prendere
solo gruppi di oggetti, specificati dagli insiemi in S. Un algoritmo greedy per il problema
della Copertura Massima può essere quello che ad ogni passo sceglie un insieme in S
che dà il maggior incremento al profitto attuale.
Algoritmo per Copertura Massima
1. C ← ∅
2. for i = 1 to k do
seleziona S ∈ S che massimizza il valore w(C ∪ S) − w(C)
3. C ← C ∪ S
4. Output C
Osserviamo che l’incremento del profitto avviene solo in relazione ai nuovi elementi di
U effettivamente inseriti. Vogliamo provare che il valore w(C) della soluzione C restituita
dall’algoritmo greedy soddisfa la relazione
"
#
1 k
w(C) ≥ 1 − 1 −
OP T
k
1
>
1−
OP T,
e
dove e è il numero di Nepero, ovvero che l’algoritmo greedy ha fattore di approssimazione
(1 − 1/e) ≈ 0.632....
Proviamo innanzitutto il seguente risultato
Lemma 2 Supponiamo che l’algoritmo greedy prenda gli insiemi S 1 , . . . , Sk nell’ordine.
Allora, per ogni ` = 1, . . . , k abbiamo
w
`
[
i=1
!
Si − w
`−1
[
i=1
Si
!
≥
OPT − w
S
k
`−1
i=1 Si
.
Dim. Alla fine del passo ` − 1-esimo dell’algoritmo greedy abbiamo scelto ` − 1 insiemi
`−1
S1 , . . . , S`−1 di profitto totale w(∪i=1
Si ). Se a S1 , . . . , S`−1 aggiungiamo passo passo tutti i
k insiemi della soluzione ottima, arriveremo ad un profitto almeno OPT . Di conseguenza,
vi deve essere almeno un passo in cui l’incremento al profitto è di almeno
`−1
OPT − w(∪i=1
Si )
.
k
(9)
15
ASDII:Parte B — Lezione 2
Ovvero, deve esistere almeno un insieme che aggiunto ai precedenti aumenta il profitto almeno della quantità (9). D’altra parte, nell’algoritmo greedy viene scelto ad ogni
passo sempre l’insieme che massimizza l’incremento del profitto. Ovvero, l’insieme scelto
dall’algoritmo greedy ottiene sicuramente un incremento di profitto dato da
`
[
w
i=1
!
Si − w
`−1
[
Si
i=1
!
almeno pari a (9), da cui il lemma.
Dal Lemma appena dimostrato otteniamo che
`
[
w
Si
i=1
!
OPT − w
≥
S
`−1
i=1 Si
k
+w
`−1
[
OPT
1
w
+ 1−
k
k
=
Si
i=1
`−1
[
i=1
!
Si
!
(10)
Lemma 3 Per ogni ` = 1, . . . , k vale che
`
[
w
Si
i=1
!
"
1
≥ 1− 1−
k
` #
OPT
(11)
Dim. Per induzione su `. Proviamolo innanzitutto per ` = 1. In tal caso la (11) è
equivalente a provare che
OPT
w(S1 ) ≥
,
k
che risulta essere vera a causa della (10).
Supponiamo ora la (11) vera per `, proviamola per ` + 1. Si ha
w
`+1
[
i=1
Si
!
= w
≥ w
=
`
[
i=1
`
[
i=1
!
"
Si + w
!
Si +
1
w
1−
k
"
`+1
[
i=1
!
Si − w
OPT − w
`
[
i=1
!
S
`
i=1
k
Si +
`
[
i=1
Si
Si
!#
(dal Lemma 2)
OPT
k
#
1 `
OPT
≥
1− 1−
OPT +
k
k
"
`+1 #
1
= 1− 1−
OPT
k
1
1−
k
(dall’ipotesi induttiva)
16
ASDII:Parte B — Lezione 2
A questo punto, ponendo ` = k nel Lemma 3, otteniamo che
w
k
[
i=1
Si
!
"
1
= w(C) = SOL ≥ 1 − 1 −
k
Ora, ricordando che
lim
1
1−
k
k
k→∞
e che
otteniamo finalmente
1
1−
k
k
=
k #
OPT .
1
≈ 0.632...
e
1
≤ 1−
k+1
k+1
1
≤ ,
e
1
OPT .
SOL = w(C) > 1 −
e
Vale la pena di notare che in alcune applicazioni pratiche la implementazione del passo
2 dell’algoritmo (ovvero la scelta di un S che massimizza la differenza w(C ∪ S) − w(C))
può essere onerosa dal punto di vista computazionale. Pertanto, potrebbe essere fattibile
solo la selezione di un insieme S che incrementa la differenza w(C ∪ S) − w(C) di un
fattore β < 1 dell’incremento massimo. In tale caso non è difficile vedere, seguendo i passi
dell’analisi appena effettuata, che l’algoritmo greedy assicura una soluzione dal valore
SOL ≥ 1 −
β
k
k
OPT > 1 −
1
eβ
OPT .
17
ASDII:Parte B — Lezione 3
Algoritmi e Strutture Dati II: Parte B
Anno Accademico 2004-2005
Lezione 3
Docente: Ugo Vaccaro
In questa lezione deriveremo algoritmi di approssimazione per alcuni classici problemi
su grafi pesati. Iniziamo con il considerare il seguente problema
Steiner Tree
• Input:
– grafo G = (V, E)
– funzione peso w : E → R+ sugli archi
– sottoinsieme R ⊆ V
• Output: sottoalbero di G di minimo costo totale che copra tutti i vertici di R, dove
il costo dell’albero è definito come la somma dei costi dei suoi archi.
Ci sono delle similitudini tra questo problema e quello di determinare lo spanning tree
di peso minimo in un grafo. Infatti, se R = V allora i due problemi coincidono esattamente.
Come conseguenza, il problema dello Steiner Tree è risolubile in tempo polinomiale nel
caso particolare che R = V . In generale, il problema dello Steiner Tree è N P -hard. Il
seguente esempio mostra in cosa il problema dello Steiner Tree differisce dal problema
del Minimum Spanning Tree . Consideriamo il grafo a sinistra nella figura di sotto,
in cui solo i vertici anneriti sono richiesti. La figura immediatamente a destra mostra
un Minimum Spanning Tree sui vertici richiesti, di costo totale pari a 4. All’estremità
destra è invece mostrato un albero di Steiner che fa uso anche di vertici non richiesti (nel
caso in questione, quello centrale), di costo totale pari a 3.
2
2
1
1
2
2
1
1
1
1
2
Quindi, includere dei nodi non necessariamente richiesti nell’albero può far abbassare
il costo della soluzione. Questo è in effetti il tratto che distingue il problema del Minimum Spanning Tree dal problema dello Steiner Tree . Il fatto che nel problema
dello Steiner Tree si possano scegliere o meno nodi in V − R fa sı́ che l’insieme delle
18
ASDII:Parte B — Lezione 3
possibili soluzioni al problema aumenti enormemente, rendendo difficile l’individuazione
della soluzione ottima.
Passiamo ora a considerare il progetto di algoritmi di approssimazione per il problema
dello Steiner Tree . Per prima cosa, mostriamo che è sufficiente considerare istanze del
problema in cui il grafo G = (V, E) è un grafo completo, ed in esso vale la diseguaglianza
triangolare, ovvero
∀u, v, z ∈ E w(u, v) ≤ w(u, z) + w(z, v).
(12)
Piò precisamente, mostreremo che se siamo in grado di produrre un algoritmo di approssimazione con fattore di approssimazione ρ per il problemo dello Steiner Tree in
grafi completi in cui vale la diseguaglianza triangolare, allora siamo anche in grado di
produrre un algoritmo di approssimazione con lo stesso fattore di approssimazione ρ in
istanze arbitrarie del problema dello Steiner Tree .
Procederemo come segue. Data una istanza I = (G, R, w) arbitraria del problema
dello Steiner Tree , dove G è un grafo, R è il sottoinsieme dei vertici richiesti, e w è la
funzione peso sugli archi di G, trasformiamo innanzitutto I in tempo polinomiale in una
istanza I 0 = (G0 , R, w0 ), dove G0 è un grafo completo sugli stessi vertici di G, l’insieme dei
vertici R richiesto è immutato, e la funzione peso w 0 sugli archi (u, v) di G0 è definita come
w0 (u, v) =
X
w(e),
e∈p(u,v)
dove p(u, v) è un cammino di peso totale minimo tra i vertici u e v nel grafo G.
Osserviamo ora che per ogni arco (u, v) ∈ G, vale che
w(u, v) ≥ w 0 (u, v).
(13)
Ciò è ovvio in quanto il costo dell’arco (u, v) in G 0 è pari al costo del cammino di minimo
costo tra u e v in G. Sia OP T (I) il costo della soluzione ottima in G, e sia OP T (I 0 ) il
costo della soluzione ottima in G0 . Come conseguenza della (13) otteniamo che
OP T (I) ≥ OP T (I 0 ).
(14)
Sia ora T 0 un albero di Steiner in G0 , di costo minimo OP T (I 0 ). Consideriamo il sottografo
H di G composto da tutti i cammini di G che corrispondono agli archi di T 0 . Ovviamente
vale che il costo totale di H (ovvero la somma dei costi degli archi di H) è esattamente
pari al costo di T 0 . Il sottografo H non è necessariamente un albero. Eliminando da H
tutti gli archi che creano cicli, otteniamo un sottoalbero A di G che copre tutti i vertici
richiesti, di costo al più pari al costo di T 0 (visto che abbiamo eliminato degli archi da T 0
per ottenere A). Daltra parte, per definizione, il costo OP T (I) di un albero di Steiner di
G è non superiore a A, pertanto otteniamo
OP T (I) ≤ costo(A) ≤ costo(T 0 ) = OP T (I 0 ).
(15)
Mettendo insieme la (14) e la (15) otteniamo che
OP T (I) = OP T (I 0 ).
(16)
ASDII:Parte B — Lezione 3
19
Riassumendo, abbiamo fatto vedere che il costo di una soluzione ottima in un grafo arbitrario G è uguale al costo di una soluzione ottima in un grafo completo G 0 facilmente
ottenibile da G. Non solo, abbiamo anche fatto vedere che data una soluzione ottima in G 0 ,
è semplice ottenere da essa una soluzione ottima nell’istanza originale G. Di conseguenza,
possiamo limitarci a progettare algoritmi per il problema dello Steiner Tree in grafi
completi, in cui valga la diseguaglianza triangolare. L’algoritmo di approssimazione in tali
grafi è molto semplice.
Algoritmo di approssimazione per Steiner Tree
1. Costruisci un Minimum Spanning Tree T sui vertici in R del grafo input G
2. Return T
Dimostriamo che l’algoritmo sopra esposto ha fattore di approssimazione pari a 2. Sia
S uno Steiner tree di costo minimo. Sostituiamo ogni arco (u, v) di S con la coppia di archi
direzionati (u, v) e (v, u) e si proceda ad una visita di tutti i nodi del grafo cosı́ ottenuto
(si veda la figura di sotto).
Il percorso relativo alla visita potrá passare per piú di una volta per uno stesso vertice,
e passerá sia attraverso vertici in R che attraverso vertici non in R (denotati con un circolo
vuoto nella figura di sopra). Il costo di questo percorso é pari a 2OP T , visto che usiamo
ogni arco di S esattamente due volte (una volta in un verso, e la seconda nell’altro verso).
A questo punto, usiamo la diseguaglianza triangolare per evitare di passare due volte
attraverso uno stesso vertice e per evitare di passare attraverso vertici non in R (si veda
la figura di sotto).
Essenzialmente, stiamo qui usando il fatto che andare direttamente da un vertice u ad
un vertice v costa di meno che andarci passando attraverso un qualche vertice intermedio.
20
ASDII:Parte B — Lezione 3
In questo modo otterremo un ciclo che attraversa tutti i vertici di R, di costo al piú 2OP T
(a causa della diseguaglianza triangolare). Eliminando un qualsiasi arco da questo ciclo
ci dará un albero Z che attraversa tutti e solo i vertici di R, di costo ancora inferiore,
ovvero il costo di Z sará al piú 2OP T . D’altra parte, il costo del Minimim Spanning Tree
T (output del nostro algoritmo di approssimazione) sará per definizione inferiore al costo
dell’albero Z, che abbiamo prima mostrato essere inferiore a 2OP T . Otteniamo quindi
che la nostra soluzione al problema dello Steiner Tree , ovvero un Minimum Spanning
Tree su R, ha costo al piú 2OP T .
Possiamo mostrare che esistono classi di grafi in cui il costo di un Minimum Spanning
Tree é circa 2 volte il costo di uno Steiner Tree , quindi l’analisi della tecnica prima
esposta non puó essere essenzialmente migliorata. Consideriamo ad esempio il grafo completo G = (V, E), dove V = {1, . . . , n, n + 1}, insieme richiesto R = {1, . . . , n}, e funzione
peso sugli archi w : E → R+ cosı́ definita
∀(i, j) ∈ E
w(i, j) =
1
2
se i = n + 1,
altrimenti.
Il costo della soluzione prodotta dal nostro algoritmo di approssimazione è pari al costo
di un MST su R, e quindi pari a 2(n − 1) D’altra parte, lo Steiner Tree di costo minimo è
composto da tutti gli archi dal nodo n + 1 (non richiesto) a tutti gli altri nodi del grafo.
Tale Steiner Tree ha costo totale pari a n, cioè circa la metá del costo di un MST su R.
Le tecniche appena sviluppate sono utili anche per la progettazione di algoritmi di
approssimazione per il seguente importante problema: Problema del Commesso Viaggiatore (TSP).
21
ASDII:Parte B — Lezione 3
TSP
• Input:
– grafo G = (V, E)
– funzione costo c : E → R+ sugli archi
• Output: un ciclo di minimo costo totale che attraversa tutti i vertici di G una ed
una sola volta.
Il problema del TSP è NP-completo ed, in generale, è difficile da approssimare.
Teorema 1 Per ogni funzione ρ(n) calcolabile in tempo polinomiale, la esistenza di un
algoritmo di approssimazione con fattore di approssimazione ρ(n) per TSP implica che
P = NP .
Dim. Proveremo che se esistesse un algoritmo di approssimazione con fattore di approssimazione ρ(n) per TSP, allora sarebbe possibile decidere in tempo polinomiale se
un grafo arbitrario G possiede un ciclo Hamiltoniano, 4 problema questo notoriamente
N P -completo.
Sia a tale scopo G = (V, E) un grafo arbitrario, n = |V |. Trasformiamo G in una
istanza di TSP nel modo seguente. Sia G 0 = (V, E 0 ) un grafo completo sullo stesso insieme
dei vertici di G. Inoltre, G0 ha una funzione costo c : E 0 → R+ sugli archi definita nel
modo seguente:
∀(u, v) ∈ E
0
c(u, v) =
1,
C > nρ(n),
se (u, v) ∈ E,
se (u, v) ∈
/ E.
Supponiamo di avere un algoritmo di approssimazione A per il problema del TSP, con
fattore di approssimazione ρ(n). Sull’input G 0 l’algoritmo A produrrá una soluzione dal
valore SOL. Possono accadere due casi:
1. SOL < nρ(n). Sotto questa ipotesi, e per come è definito il grafo G 0 , possiamo
dire che l’algoritmo A ha trovato un ciclo che visita tutti i vertici di G 0 ed il ciclo è
composto solo da archi di costo 1. Ció implica, per come sono definiti i costi degli
archi di G0 , che il ciclo trovato da A è composto solo da archi di G. Ovvero, possiamo
concludere che G possiede un ciclo Hamiltoniano.
2. SOL ≥ nρ(n). Sotto questa ipotesi, e per come è definito il grafo G 0 , possiamo
dire che il grafo G non possiede un ciclo Hamiltoniano. Infatti, se lo possedesse,
vorrebbe dire che la soluzione ottima del problema del TSP in G 0 ha valore pari a n,
contraddicendo il fatto che l’algoritmo A ha fattore di approssimazione pari a ρ(n).
4
Si ricordi che un ciclo Hamiltoniano è un ciclo che attraversa tutti i vertici del grafo, una ed una sola
volta.
ASDII:Parte B — Lezione 3
22
In conclusione, la esistenza di un algoritmo di approssimazione con fattore di approssimazione ρ(n) per TSP implica la esistenza di una procedura polinomiale per decidere se
un grafo arbitrario ha un ciclo Hamiltoniano o meno, il che implicherebbe che P = N P a
causa della N P -completezza del problema del ciclo Hamiltoniano.
Nell’ ipotesi che la funzione costo sugli archi del grafo G soddisfi la diseguaglianza
triangolare, è abbastanza semplice produrre un algoritmo di approssimazione per il problema del TSP con fattore di approssimazione 2. L’algoritmo è simile all’algoritmo di
approssimazione per Steiner Tree .
Algoritmo di approssimazione per TSP
1. Costruisci un Minimum Spanning Tree T del grafo input G
2. Sostituisci ogni arco (u, v) di T con la coppia di archi (u, v) e (v, u)
3. Costruisci un tour T del grafo cosı́ ottenuto, passando attraverso tutti i suoi archi
4. Output il ciclo C che visita i vertici di G nell’ordine della loro prima apparizione nel
tour T .
Il seguente esempio illustra il funzionamento dell’algoritmo. L’albero di sotto è il
Minimum Spanning Tree costruito dall’algoritmo. Su di esso costruiamo il tour T
rappresentato dagli archi trattegiati, più o meno come si procedeva nel caso dello Steiner
Tree .
Dopodichè, in accordo all’algoritmo, otteniamo un ciclo che enumera i vertici
dell’albero in accordo all’ordine in cui essi compaiono per la prima volta nel tour T .
ASDII:Parte B — Lezione 3
23
Per la diseguaglianza triangolare, i percorsi che vanno da un vertice direttamente ad un
altro senza passare per vertici intermedi, sono di costo minore e quindi non aumentano il
costo del ciclo C rispetto al costo del tour T .
Proviamo ora che l’algoritmo ha fattore di approssimazione pari a 2. Osserviamo
innazitutto che
costo(M ST ) ≤ OP T.
(17)
Infatti, se dal ciclo di costo OP T togliamo un arco, otteniamo un alberto T 0 , di costo
inferiore al ciclo, ed in piú T 0 avrá sicuramente costo maggiore od al piú uguale a T , che
è un MST. Pertanto il costo SOL della soluzione prodotta dall’algoritmo di sopra sará:
SOL = costo(C)
≤ costo(T ) (a causa della diseguaglianza triangolare)
= 2costo(M ST ) (per costruzione)
≤ 2OP T (per la (17)),
il che prova che l’algoritmo ha un fattore di approssimazione pari a 2.
Ci poniamo ora il problema di elaborare un algoritmo per il problema del TSP con
un migliore fattore di approssimazione. A tale scopo, è utile comprendere meglio come
abbiamo ottenuto il semplice algoritmo con fattore di approssimazione pari a 2. Siamo
partiti con uno MST T del grafo G ed abbiamo ottenuto un tour che visita tutti gli archi
di T , essenzialmente raddoppiando gli archi di T . Ció ci costa 2 volte il costo di T . Da
questo tour, abbiamo ottenuto un ciclo in G prendendo delle ”scorciatoie” nel tour. A
causa delle diseguaglianza triangolare, queste scorciatoie non hanno aumentato il costo
della soluzione. La prova si concludeva con l’osservazione che 2·(costo di T ) è inferiore a
2 · OP T . La chiave del miglioramento dell’algoritmo prima esposto risiedeá nel fatto che
ASDII:Parte B — Lezione 3
24
sará possibile ottenere un tour che attraversa tutti i vertici di G ad un costo inferiore a
2·(costo di T ). A tale scopo abbiamo bisogno di introdurre il concetto di Tour Euleriano
di un grafo.
Un Tour Euleriano di un grafo G consiste in un percorso nel grafo G con vertice di
partenza e vertice di arrivo coincidenti, e che attraversa tutti gli archi di G una ed una sola
volta. La similaritá di questo problema con quello del ciclo Hamiltoniano è solo apparente.
Mentre infatti il problema di stabilire se un grafo possiede o meno un ciclo Hamiltoniano
è N P -completo, stabilire se un grafo possiede un tour Euleriano è un problema risolubile
in tempo polinomiale. Possiamo infatti provare il seguente risultato.
Teorema 2 Il grafo G possiede un tour Euleriano se e solo se G non ha vertici di grado
dispari.
Dim. Proviamo innazitutto che se G = (V, E) possiede vertici di grado dispari allora G
non ammette un tour Euleriano. Supponiamo quindi che G possieda un vertice x di grado
dispari e scegliamo x come ipotetico punto di partenza e di arrivo del tour Euleriano.
Osserviamo che ogni volta che con questo percorso usciamo da x e vi ritorniamo, usiamo
due archi incidenti su x. Non saremmo quindi in grado di visitare tutti gli archi incidenti
su x una ed una sola volta con un percorso che parte e ritorna in x, in quanto tali archi
sono in numero dispari.
La prova che è possibile costruire un tour Euleriano di G se G ha solo vertici di grado
pari la si effettua per induzione sul numero di archi |E|. Se |E| = 1 non vi è nulla da
provare. Assumiamo il teorema vero per tutti i grafi con |E| ≤ k, per qualche k ≥ 1, e
sia G un grafo con |E| = k + 1. Prendiamo un arbitrario vertice x in G e scegliamolo
come punto di partenza ed iniziamo a visitare il grafo, con l’accortezza di non passare mai
due volte su di uno stesso arco. Poichè il grado di ogni vertice v è pari, ogni volta che
entriamo in un vertice v ne possiamo anche uscire. Poichè il grafo G finito, prima o poi
ritorneremo nel vertice x. Se abbiamo visitato tutti gli archi di G, allora abbiamo trovato
il tour Euleriano che cercavamo. Altrimenti abbiamo trovato solo un percorso che parte
ed arriva in x e che ogni qualvolta è entrato in qualche vertice v ne è anche uscito. In altre
parole, questo percorso ha visitato, per ogni vertice v, un numero pari di archi incidenti
su v. Togliamo gli archi di tale percorso da G, rimane un grafo G 0 con un numero di archi
≤ k, ed ogni vertice di G0 ha ovviamente grado pari. Il grafo G 0 ammette quindi un tour
Euleriano per ipotesi induttiva. Il tour prima trovato congiunto al tour di G 0 formano
ovviamente un tour che parte e arriva in x e che visita tutti gli archi di G una ed una sola
volta.
Diciamo che un grafo G è Euleriano se in G vi è un tour Euleriano. A questo punto
dovrebbe essere anche chiaro perchè raddoppiavamo tutti gli archi del Minimum Spanning
Tree T nel primo algoritmo di approssimazione per TSP prima di trovare un tour nel
grafo: raddoppiando tutti gli archi ottenevamo infatti un grafo con tutti i nodi di grado
pari e quindi il tour Euleriano esisteva sicuramente. Non è detto peró che questo sia il
modo piú economico per ottenere da T un grafo Euleriano. Anzi, in generale non lo è .
25
ASDII:Parte B — Lezione 3
Banalmente, se un nodo ha giá grado pari in T non vi è alcuna necessitá di raddoppiare
il suo grado. In virtú del teorema precedente, sono solo i vertici di grado dispari che
sono fonte di problemi. Questi problemi li possiamo far scomparire aggiungendo un arco
a ciaschedun nodo di grado dispari, ottenendo quindi un grafo con tutti i vertici di grado
pari in cui costruire un tour Euleriano e poi, usando le scorciatoie e la diseguaglianza
triangolare, ottenere un ciclo che attraversa tutti i vertici una ed una sola volta.
Ricordiamo la seguente relazione, che abbiamo provato nella prima lezione:
X
deg(x) = 2|E|.
(18)
u∈V
Immediata conseguenza della (18) è che il numero di vertici in G che hanno grado dispari
è pari, visto che la quantitá al membro destro della (18) è pari. Pertanto, dato un generico
grafo G, per trasformare G in un grafo Euleriano basta individuare il sottoinsieme dei
vertici V 0 di grado dispari, dividerlo in due sottoinsiemi disgiunti di cardinalitá |V 0 |/2
ciascheduno, ed aggiungere un matching tra tali due sottoinsiemi. Abbiamo quindi il
seguente nuovo algoritmo di approssimazione per TSP.
Algoritmo di approssimazione per TSP – fattore di approssimazione 3/2
1. Costruisci un Minimum Spanning Tree T del grafo input G
2. Calcola un matching perfetto M di minimo costo sui vertici di grado dispari di T .
Aggiungi M a T per ottenere un grafo Euleriano
3. Costruisci un tour euleriano T del grafo ottenuto al passo precedente
4. Output il ciclo C che visita i vertici di G nell’ordine della loro prima apparizione nel
tour T .
Analizziamo il costo della soluzione prodotta dall’algoritmo. A causa della diseguaglianza triangolare, il costo SOL della soluzione prodotta non sará superiore al costo
del grafo Euleriano ottenuto al passo 2 dell’algoritmo, ovvero
SOL ≤ costo(T ) + costo(M ).
Giá sappiamo che costo(T ) ≤ OP T , valutiamo quindi il costo di M . A tale scopo, effettuiamo le seguenti osservazioni. Consideriamo la soluzione ottima al problema dello TSP,
ovvero il ciclo C che attraversa tutti i vertici del grafo con un costo totale OP T . Tale ciclo
C attraverserá anche tutti i vertici dell’insieme V 0 (composto dai vertici di grado dispari
nel MST T usato nel passo 1 dell’algoritmo), in un qualche ordine. Numeriamo i vertici
di V 0 con i numeri 1, 2, . . . , |V 0 | secondo quest’ordine di attraversamento (si veda la figura
di sotto).
Ora, il ciclo C per andare dal vertice 1 al vertice 2 seguirá un certo percorso (rappresentato dalla linea tratteggiata tra 1 e 2 nella figura di sopra) che, a causa della diseguaglianza
26
ASDII:Parte B — Lezione 3
1
2
3
4
|V’|
triangolare, avrá un costo ≥ del costo dell’arco tra 1 e 2. Stesso discorso per il percorso
che C effettuerá per andare da 2 a 3, e cosı́ via. In conclusione, la somma dei costi dei
percorsi del ciclo C tra i vertici di {1, 2, . . .} = V 0 , comprensivo del ritorno al vertice 1
sará ≥ della somma dei costi degli archi diretti tra i vertici {1, 2, . . .} = V 0 . Facendo
attenzione, si puó comprendere che gli archi tra i vertici di V 0 si possono decomporre in
due matching disgiunti (nella figura di sopra sono rappresentati con archi di differente
spessore). Possiamo quindi dire che il costo OP T del ciclo ottimo C sará ≥ della somma
dei costi dei percorsi del ciclo C tra i vertici di {1, 2, . . .} = V 0 che, a sua volta, è ≥ del
costo dei due matching tra i vertici di V 0 . Almeno uno dei due matching avrá quindi costo
≤ OP T /2. A maggior ragione, il matching di costo minimo M aggiunto tra i vertici di V 0
nel passo 2 dell’algoritmo avrá costo ≤ OP T /2. Riassumendo otteniamo
3
1
SOL ≤ costo(T ) + costo(M ) ≤ OP T + OP T ≤ OP T,
2
2
il che conclude (finalmente) l’analisi dell’algoritmo.
La seguente figura mostra che l’analisi dell’algoritmo non puó essere migliorata, ovvero
esistono istanze di input su cui l’algoritmo produce effettivamente una soluzione di costo
pari a 3/2OP T.
Il Minimum Spanning Tree trovato al Passo 1 dell’algoritmo è rappresentato da
archi di maggior spessore. Notiamo che ha solo due vertici di grado dispari, pertanto il
matching di minimo peso che viene aggiunto tra di essi è dato dall’arco di peso bn/2c.
Abbiamo quindi che l’algoritmo produce un ciclo di peso totale
SOL = (n − 1) + bn/2c.
27
ASDII:Parte B — Lezione 3
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
.......
n/2
D’altra parte, è facile vedere che il ciclo ottimo nel grafo di sopra ha peso esattamente n.
Da cui segue che nel grafo di sopra il nostro algoritmo è forzato a produrre una soluzione
di costo 3/2OP T .
28
ASDII:Parte B — Lezione 4
Algoritmi e Strutture Dati II: Parte B
Anno Accademico 2004-2005
Lezione 4
Docente: Ugo Vaccaro
In questa lezione deriveremo un algoritmo di approssimazione per il seguente problema.
Approfitteremo dell’occasione per introdurre una nuova tecnica per il progetto di algoritmi
di approssimazione, che vá sotto il nome di Pruning Parametrico.
K-Centro
• Input:
– grafo completo G = (V, E)
– funzione costo c : E → R+ sugli archi, soddisfacente la diseguaglianza triangolare
– intero k > 0
• Output: insieme S ⊆ V , |S| = k, che minimizza max v∈V {connect(v, S)}, dove
connect(v, S) = minu∈S c(v, u).
L’assunzione che la funzione costo sugli archi di G soddisfi la diseguaglianza triangolare è necessaria, altrimenti si può dimostrare che il problema del K-Centro non può
essere approssimato a meno di un qualsiasi fattore α(n) polinomialmente computabile,
assumendo che P 6= N P . Elaboreremo un algoritmo di approssimazione per il problema
dei K-Centro usando la tecnica del Pruning Parametrico. Tale tecnica è basata sulle
seguenti considerazioni. Se conoscessimo il costo di una soluzione ottima ad un problema,
potremmo eliminare le parti non rilevanti dell’input e pertanto semplificare la ricerca di
una buona soluzione. Ad esempio, se sapessimo che il costo di un ciclo di costo minimo in
una istanza di TSP fosse 100, potremmo eliminare dal grafo tutti gli archi di costo superiore a 100, in quanto essi sicuramente non interverranno nella soluzione ottima. Purtroppo,
calcolare il costo di una soluzione ottima è esattamente la parte difficile dei problemi di
ottimizzazione NP-hard. La tecnica del Pruning Parametrico cerca di evitare questa difficoltá nel modo seguente. Supponiamo, per fare un esempio, di considerare un problema
di ottimizzazione di minimo. Viene scelto un parametro t (che puó essere interpretato
come un tentativo iniziale di stimare il valore della soluzione ottima). Per ogni valore di t,
eliminiamo dalla istanza di input I sotto esame tutte le parti che sicuramente non intervengono in una qualche soluzione di costo ≤ t. Denotiamo con I(t) tale istanza ridotta.
L’algoritmo consiste in due passi: nel primo passo la famiglia di istanze I(t) viene usata
per calcolare una limitazione inferiore per OP T , si esso t ∗ (questo calcolo viene tipica-
ASDII:Parte B — Lezione 4
29
mente fatto nel modo seguente: se si riesce in qualche modo a far vedere che in I(t ∗ ) non vi
è soluzione al problema, allora è chiaro che occorre aggiungere altre parti a I(t ∗ ) e quindi
OP T sarà sicuramente ≥ t∗ ). Nel secondo passo, si trova una soluzione in I(αt ∗ ), per un
opportuno valore di α. Ciò porterà ad una soluzione di valore SOL ≤ αt ∗ ≤ αOP T .
Per vedere come applicare la tecnica del Pruning Parametrico al problema dei KCentro , procediamo nel modo seguente. Ordiniamo gli archi del grafo G = (V, E) in
ordine crescente di costo, ovvero poniamo E = {e 1 , . . . , em }, con c(e1 ) ≤ . . . ≤ c(em ).
Definiamo Gi = (V, Ei ), dove Ei = {e1 , . . . , ei }. Un insieme dominante in un grafo
H = (U, F ) è un sottoinsieme S ⊆ F tale che ogni vertice in U − S è adiacente a qualche
vertice in S. Sia dom(H) la cardinalità di un insieme dominante in H di cardinalità
minima. È abbastanza ovvio verificare che il problema del K-Centro è equivalente a
trovare il più piccolo indice i tale che il grafo G i contiene un insieme dominante di
cardinalità ≤ k, ovvero che Gi contenga k “stelle” (cioè alberi di altezza 1) che ricoprono
tutto l’insieme V . Se i∗ è tale più piccolo indice, allora è ovvio che c(e i∗ ) è il costo di un
K-Centro ottimo. Denoteremo con OP T tale valore c(e i∗ ).
Il problema con la procedura di sopra consiste nel fatto che determinare se un grafo
contiene un insieme dominante di una data cardinalità è esso stesso un problema NPhard. Cercheremo di aggirare questa difficoltà lavorando su grafi in cui si possa stimare
tali cardinalità con parametri di più facile calcolo.
Dato un grafo H = (U, F ), definiamo il quadrato di H, e lo denoteremo con H 2 , come
il grafo che contiene gli stessi vertici di H e per ogni coppia di vertici u, v, esiste un arco
in H 2 tra u e v se e solo se nel grafo H c’è un cammino di lunghezza al più 2 tra gli stessi
vertici u e v. Ricordiamo infine che un insieme I ⊆ U è detto indipendente se ∀u, v ∈ I
vale che (u, v) ∈
/ F . Il seguente risultato ci darà un modo per limitare inferiormente il
valore di OP T .
Lemma 4 Dato un grafo H = (U, F ), sia I un generico insieme indipendente in H 2 .
Vale che
|I| ≤ dom(H).
Dim. Sia D un insieme dominante di cardinalità minima in H. Da quanto detto in
precedenza, ciò implica che esistono |D| stelle che ricoprono tutti i vertici di H. I vertici
in ciascuna di queste stelle sono ovviamente connessi da cammini di lunghezza al più due.
Per definizione, nel grafo H 2 tali cammini si trasformano in archi, pertanto ogni stella in
H si trasforma in un piccolo sottografo grafo completo in H 2 (ovvero in un sottografo in
cui esistono archi tra ogni coppia di vertici). Un qualsivoglia insieme indipendente I di H 2
non può contenere più di un vertice di ciascuno di questi sottografi completi (altrimenti
non sarebbe composto da vertici non collegati da archi). Pertanto, la cardinalit d̀i di un
qualsiasi insieme indipendente I di H 2 non può essere più grande del numero |D| di stelle
che ricoprono H. Ciò implica il risultato del lemma.
Prima di introdurre l’algoritmo per il problema del K-Centro , ricordiamo che un
sottoinsieme I di vertici è detto massimale indipendente se I è indipendente ed inoltre per
30
ASDII:Parte B — Lezione 4
ogni vertice u vale che I ∪ {u} non è più indipendente. Mentre il calcolo di un insieme
indipendente di cardinalità massima è un altro problema NP-Hard, il calcolo di un insieme
massimale indipendente è agevole e lo si può effettuare mediante una semplice procedura
greedy:
Algoritmo per il calcolo di un insieme massimale indipendente
• Si scelga nel grafo un vertice arbitrario u e lo si inserisca in I;
• si eliminino dal grafo il vertice u e tutti i suoi vicini;
• si iteri il procedimento sul grafo rimanente, fin quando esso non diventi vuoto.
• Return I.
Presentiamo ora l’algoritmo di approssimazione per il problema del K-Centro .
Algoritmo di approssimazione per K-Centro
1. Costruisci i grafi G21 , G22 , . . . , G2m .
2. Calcola un insieme massimale indipendente M i in ciascun grafo G2i .
3. Trova il più piccolo intero i tale che |M i | ≤ k, sia esso j.
4. Return Mj .
Vogliamo provare che questo algoritmo ha un fattore di approssimazione 2. Proviamo
innanzitutto il seguente risultato che ci dà una limitazione inferiore al valore OP T .
Lemma 5 Per l’indice j definito dall’algoritmo, vale che
c(ej ) ≤ OP T.
Dim. Dalla definizione dell’indice j abbiamo che per ogni indice ` < j vale che |M ` | > k.
Pertanto, dal Lemma 4 abbiamo che dom(G ` ) ≥ |M` | > k. Ricordando che i∗ è il più
piccolo indice i per cui dom(Gi ) ≤ k, abbiamo che i∗ > `, e quindi i∗ ≥ j. Ma OP T =
c(ei∗ ), e ricordando che i pesi c(es ) degli archi erano crescenti nell’indice s, abbiamo che
c(ej ) ≤ c(ei∗ ) = OP T .
Possiamo ora provare il seguente
Teorema 3 L’algoritmo ha un fattore di approssimazione 2 per il problem del K-Centro
.
31
ASDII:Parte B — Lezione 4
Dim. Partiamo con l’effettuare l’osservazione chiave che un qualunque insieme indipendente massimale I in un grafo è anche un insieme dominante. Infatti, se non lo fosse,
vorrebbe dire che esisterebbe un vertice u che non è adiacente ad alcun vertice in I,
pertanto potrebbe essere inserito in I e l’insieme I continuerebbe a rimanere un insieme
indipendente, contraddicendo la massimalità di I. Sulla base di questa osservazione, possiamo concludere che l’insieme Mj restituito dall’algoritmo è un insieme dominante di
cardinalità al più k in G2j . In altri termini, esistono |Mj | “stelle” in G2j , centrate sui vertici
di Mj , che ricoprono tutti i vertici del grafo. Ricordando che ogni arco in G 2j corrisponde
ad un cammino di lunghezza al più 2 in G j , e che vale la diseguaglianza triangolare, abbiamo che ogni arco di queste stelle ha peso al più pari a 2c(e j ). Ricordando che il costo
SOL della soluzione è pari al costo dell’arco più costoso che si è usato, abbiamo che, in
virtù del Lemma 5, vale
SOL ≤ 2c(ej ) ≤ 2OP T,
il che prova il Teorema.
Si può provare che esistono esempi su cui l’algoritmo da noi proposto produce effettivamente una soluzione di costo pari a 2 volte l’ottimo. Consideriamo infatti il grafo
completo G = (V, E), con U = {1, 2, . . . , n, n + 1}, con funzione costo c(i, j) sugli archi
cosı̀ definita
1 se i = n + 1
c(i, j) =
2 altrimenti.
Per k = 1, la soluzione ottima consiste nello scegliere il singolo vertice n+1, che è connesso
ad ogni altro vertice da un arco di peso 1, pertanto avremmo OP T = 1 in questo caso.
D’altra parte, l’algoritmo produrrebbe un indice j = n ed in questo caso G 2n sarebbe
di nuovo un grafo completo. Se, per sfortuna, scegliessimo come insieme indipendente
massimale un vertice s 6= n + 1, avremmo che il costo della soluzione ritornata sarebbe
pari a 2, ovvero 2 volte il costo di OP T .
Proviamo ora che il fattore di approssimazione 2 appena ottenuto non è essenzialmente
migliorabile.
Teorema 4 Se esistesse un algoritmo di approssimazione con fattore di approssimazione
pari a 2 − per il problema del K-Centro , per un qualche > 0, allora P = N P .
Dim. Proveremo che se fosse possibile in tempo polinomiale produrre una approssimazione
del problema del K-Centro a meno di un fattore 2 − , allora sarebbe possibile in tempo
polinomiale decidere se un arbitrario grafo possiede un insieme dominante di cardinalitá
al piú k. Dalla NP-completezza del problema degli insiemi dominanti, si ha la tesi.
Sia G = (V, E) un grafo arbitrario, definiamo il grafo completo G 0 (V, E 0 ) e la funzione
costo c : E 0 → R+ come
∀(u, v) ∈ E
0
c(u, v) =
1
2
se (u, v) ∈ E
se (u, v) ∈
/E.
Il grafo G0 soddisfa la diseguaglianza triangolare. Notiamo inoltre che
ASDII:Parte B — Lezione 4
32
1. se dom(G) ≤ k allora la soluzione ottima per il problema del K-Centro in G 0 ha
valore 1 (infatti se esiste un insieme dominante D di cardinalitá al piú k in G, allora
in G0 ogni vertice puó essere connesso a vertici in D mediante archi di peso pari a
1, e meglio non si puó fare).
2. se dom(G) > k, allora la soluzione ottima per il problema del K-Centro in G 0 ha
valore pari a 2 (infatti, l’ipotesi che dom(G) > k ci forza necessariamente ad usare
archi in G0 non presenti in G, e quindi di peso 2 in G 0 ).
Supponiamo ora di avere un algoritmo polinomiale di approssimazione per il problema del
K-Centro che in tempo polinomiale produce una soluzione di valore SOL ≤ (2 − )OP T .
Eseguiamo tale algoritmo sull’istanza G 0 = (V, E 0 ) appena definita. L’algoritmo restituirá
un valore SOL. Possono verificarsi due casi:
1. SOL < 2. Allora necessariamente OP T = 1 e quindi, per quanto prima detto,
dom(G) ≤ k.
2. SOL > 2. Allora necessariamente OP T = 2 e quindi, per quanto prima detto,
dom(G) > k.
Avremmo quindi una procedura polinomiale per determinare se dom(G) ≤ k o meno, da
cui la tesi del Teorema.
Tra le varie questioni studiate fin’ora, abbiamo anche scoperto che esistono problemi
di ottimizzazione che non possono essere approssimati al di sotto di una certa soglia, a
meno di improbabili conseguenze, quali P = N P . Vogliamo vedere nel seguito di questa
lezione che esistono anche problemi di ottimizzazione N P -hard la cui soluzione ottima può
essere invece approssimata con un fattore di approssimazione arbitrariamente prossimo ad
1. In un certo senso, tra tutti i problemi N P -hard, questo sono per cosı̀ dire i più facili.
Precisiamo innanzitutto i concetti in questione.
Sia Π un problema di ottimizzazione N P -hard. Diremo che un algoritmo di approssimazione A è uno schema di approssimazione per Π se avendo in input (I, ), dove I è una
istanza di input di Π e > 0 è un parametro d’errore, esso produce una soluzione s di
valore SOL(I, ) tale che
• SOL(I, ) ≤ (1 + )OP T (I), nel caso in cui Π sia un problema di minimo;
• SOL(I, ) ≥ (1 − )OP T (I), nel caso in cui Π sia un problema di massimo.
L’algoritmo A è detto schema di approssimazione polinomiale, (abbreviato in PTAS) se
per ogni fissato , il suo tempo di esecuzione è polinomiale nella taglia dell’istanza I.
La definizione appena data permette che il tempo di esecuzione di A dipenda arbitrariamente da . La seguente definzione è più stringente. Diremo che l’algoritmo A è
uno schema di approssimazione pienamente polinomiale (abbreviato in FPTAS) se A è un
PTAS ed inoltre il suo tempo di esecuzione è polinomiale sia in |I| che in 1/.
33
ASDII:Parte B — Lezione 4
Problemi di ottimizzazione che ammettono PTAS, o FPTAS, possono essere approssimati ad un qualsivoglia fattore arbitrariamente prossimo a 1. Vediamo un esempio di tali
problemi.
Zaino 0-1
• Input:
– insieme S = {a1 , . . . , an } di oggetti;
– funzione peso w : a ∈ S → w(a) ∈ R+ ;
– funzione profitto p : a ∈ S → p(a) ∈ R+ ;
– capacità di carico totale B ∈ R+ .
• Output: sottoinsieme S 0 ⊆ S tale che
P
a∈S 0 p(a) massimo.
P
a∈S 0
w(a) ≤ B, e di profitto p(S 0 ) =
Innanzitutto presentiamo un algoritmo esatto per il problema dello Zaino 0-1, basato
sulla Programmazione Dinamica. Sia P = max a∈S p(a). Per ogni indice i ∈ {1, . . . , n} e
valore p ∈ {1, . . . , nP }, sia Si,p un sottoinsieme di {a1 , . . . , ai } di peso totale minimo, ed
P
il cui profitto totale sia esattamente p. Sia A(i, p) il peso a∈Si,p w(a) dell’insieme Si,p .
Porremo per convenzione A(i, p) = ∞ se un tale insieme S i,p non esiste. Ovviamente per
ogni p ∈ {1, . . . , nP } vale che A(1, p) = w(a 1 ) se p(a1 ) = p, A(1, p) = ∞ altrimenti. In
generale avremo
A(i + 1, p) =
min{A(i, p), w(ai+1 ) + A(i, p − p(ai+1 ))}
A(i, p)
se p(ai+1 ) < p,
altrimenti.
(19)
Il nostro problema originario, ovvero quello di calcolare il massimo profitto ottenibile
con sottoinsiemi di peso totale al più B sarà quindi dato da
max{p : A(n, p) ≤ B}.
(20)
Il calcolo di A(n, p) può essere fatto dal seguente algoritmo, basandoci sulla equazione di
ricorrenza (19). Pertanto tutti i valori A(i, p) possono essere calcolati in tempo O(n 2 P ), e
quindi nello steso tempo possiamo calcolarci (20), e risolvere il problema dello Zaino 0-1.
34
ASDII:Parte B — Lezione 4
Calcolo di A(n, P ) via Programmazione Dinamica
P = maxi p(ai )
for i ← 1 to n do
A(i, 0) ← 0
for p ← 1 to nP do
A(1, p) ← w(ai ) se p(ai ) = p
∞
altrimenti.
for i ← 2 to n do
for p ← 1 to nP do
if p(ai ) ≤ p then
A(i, p) ← min{A(i − 1, p), w(ai ) + A(i − 1, p − p(ai ))}
else
A(i, p) ← A(i − 1, p).
Ciò ovviamente non implica che abbiamo trovato un algoritmo polinomiale per il problema dello Zaino 0-1, che è un problema N P -completo. Infatti, il nostro algoritmo è in
generale esponenziale nella taglia dell’input, che è O(log n + log P ). Se il valore di P
fosse piccolo, ad esempio P = O(n), allora il nostro algoritmo sarebbe sı̀ polinomiale. Il
trucco quindi per ottenere uno P T AS per il problema dello Zaino 0-1 si basa quindi
proprio su questa osservazione: ignoriamo una parte dell’input, ovvero una parte dei bits
che rappresentano i valori dei profitti degli oggetti. La parte che ignoriamo dipenderà dal
valore del parametro di errore che siamo disposti a tollerare. In questo modo, otterremo
una nuova istanza per il problema dello Zaino 0-1in cui i valori dei profitti degli oggetti
possono essere visti come numeri limitati da un polinomio in n e 1/. Ciò ci permetterà
di ottenere una soluzione di profitto almeno (1 − )OP T , in tempo polinomiale in n e 1/.
FPTAS per Zaino 0-1
1. Dato > 0, sia K =
P
n
2. Per ogni oggetto ai , sia p0 (ai ) = bp(ai )/Kc
3. Applica la Programmazione Dinamica sull’istanza di input con questi nuovi profitti,
e sia S 0 la soluzione ottima ottenuta
4. Output S 0
Proviamo innanzitutto che
p(S 0 ) ≥ (1 − )OP T.
Osserviamo che per ogni elemento a ∈ S si ha
p(a)
p(a) ≥ Kp (a) = K
>K
K
0
p(a)
− 1 = p(a) − K,
K
(21)
35
ASDII:Parte B — Lezione 4
in quanto per ogni numero x vale che bxc > x − 1.
Sia O la soluzione ottima al problema originale, ovvero al problema con gli oggetti di
profitto p(a), a ∈ S, e valutiamo il profitto totale di O mediante i nuovi profitti p 0 (a).
Avremo
Kp0 (O) = K
X
p0 (a)
a∈O
>
=
X
a∈O
(p(a) − K) (in virtù della (21))
a∈O
p(a) −
X
X
K
a∈O
≥ p(O) − nK (poichè la somma contiene al più n termini).
Da ciò, e ricordando la definizione di K e di p 0 , abbiamo che
SOL = p(S 0 ) =
≥
X
X
p(a)
a∈S 0
Kp0 (a) (dalla (21))
a∈S 0
0
= Kp (S 0 )
≥ Kp0 (O) (in quanto S è ottima per i profitti p 0 )
≥ p(O) − nK = OP T − nK
= OP T − P
≥ (1 − )OP T,
dove l’ultima diseguaglianza segue dal fatto che ovviamente OP T ≥ P .
Ci resta solo da provare che l’algoritmo proposto è polinomiale in n e 1/. A tal
fine, ricordiamo che abbiamo applicato la programmazione dinamica alla nuova istanza di
input, pertanto il tempo di esecuzione sará
O(n2 P 0 ) = O(n2 bP/Kc) = O(n2 bn/c),
il che è polinomiale in n e 1/.
36
ASDII:Parte B — Lezione 5
Algoritmi e Strutture Dati II: Parte B
Anno Accademico 2004-2005
Lezione 5
Docente: Ugo Vaccaro
In questa lezione inizieremo a studiare gli algoritmi di approssimazione per problemi
di ottimizzazione NP-hard in un contesto piú ampio di quanto fatto finora. Introdurremo
una tecnica di portata molto generale per il progetto di algoritmi di approssimazione e
rivedremo alcuni degli algoritmi che abbiamo studiato, reinterpretandoli all’interno del
nuovo contesto. La tecnica di cui intendiamo parlare è la Programmazione Lineare (PL).
É ragionevole affermare che una gran parte della teoria degli algoritmi di approssimazione,
come è nota a tutt’oggi, è basata sull PL. A titolo di esempio, iniziamo con il considerare
una generalizzazione del problema del Vertex Cover introdotto nella prima lezione.
VERTEX COVER
• Input:
– grafo G = (V, E)
– funzione peso w : V → R+
• Output: sottoinsieme S ⊆ V di minimo peso w(S) =
E valga {u, v} ∩ S 6= ∅.
P
u∈S
w(u) tale che ∀(u, v) ∈
Ritroviamo il vecchio problema del Vertex Cover studiato in precedenza nel caso
in cui la funzione peso cui vertici sia tale che w(u) = 1 per ogni u ∈ V .
Formuliamo il problema appena enunciato attraverso la PL. Per prima cosa descriviamo
una qualsiasi possibile soluzione S ⊆ V = {v 1 , . . . , vn } mediante un vettore binario x =
(x(v1 ), . . . , x(vn )) tale che
1 se vi ∈ S
x(vi ) =
(22)
0 se vi 6∈ S.
Il problema di trovare un Vertex Cover di peso minimo in G può essere quindi
formulato come
minimizzare
X
x(v)w(v)
(23)
v∈V
soggetto a
x(u) + x(v) ≥ 1
x(v) ∈ {0, 1}
∀(u, v) ∈ E
∀u ∈ V .
(24)
(25)
37
ASDII:Parte B — Lezione 5
Il vincolo (25) (comunemente chiamato vincolo di interezza) ci assicura che ogni possibile
vettore soluzione x abbia componenti a valori binari, e quindi x definisca un sottoinsieme
S ⊆ V attraverso la (22). Il vincolo (24) ci assicura che per ogni arco (u, v) ∈ E almeno
una delle due componenti x(u), x(v) del vettore x abbia valore pari a 1. Ciò altresı́ assicura che ∀(u, v) ∈ E almeno uno tra u e v appartenga all’insieme S corrispondente al
vettore x, ovvero che S è un Vertex Cover di G. Pertanto il problema di PL (23) è
perfettamente equivalente al problema del Vertex Cover di minimo peso prima enunciato. Ovviamente, benchè scritto in questa forma il problema continua ad essere difficile,
ovvero non risolvibile in tempo polinomiale (se P 6= N P ). L’approccio che seguiremo
consiste nel derivare un nuovo sistema di PL, differente da (23) (ma ad esso collegato),
di “facile” soluzione, indi dedurre dalla soluzione al nuovo problema di PL una soluzione
(sperabilmente “buona”) al problema originale di PL.
Il nuovo problema di PL che formuliamo lo si ottiene rilassando (ovvero indebolendo)
il vincolo di interezza (25), e sostituendolo con un semplice vincolo di non negatività delle
variabili x(v), ∀v ∈ V . Otteniamo quindi il seguente problema di PL
minimizzare
X
x(v)w(v)
(26)
v∈V
soggetto a
x(u) + x(v) ≥ 1
x(v) ∈ [0, 1]
∀(u, v) ∈ E
∀u ∈ V .
(27)
(28)
Questo è un problema standard di PL, risolubile ottimalmente in tempo polinomiale.
Quindi, in tempo polinomiale nella taglia del sistema è possibile trovare il vettore x ∗ =
P
(x∗ (v1 ), . . . , x∗ (vn )) che massimizza la funzione obiettivo v∈V x(v)w(v) e che rispetta entrambi i vincoli (27) e (28). Denotiamo con OP T R il valore ottimo della funzione obiettivo
P
del sistema di PL (26) in corrispondenza al vettore x ∗ , ovvero OP TR = v∈V x∗ (v)w(v).
Denotiamo con OP T il valore minimo (ottimo) della funzione obiettivo del sistema di PL
a vincoli interi (23), ovvero OP T è il minimo peso di un Vertex Cover di G. Dato che
nel sistema (26) minimizziamo la stessa funzione obiettivo di (23), ma su di una regione
più ampia, avremo che
OP TR ≤ OP T.
(29)
Vogliamo adesso scoprire se è possibile ottenere dal vettore soluzione x ∗ del sistema (26)
un vettore x a componenti binarie che rispetti tutti i vincoli del sistema (23), ed in corrispondenza del quale la funzione obiettivo di (23) assuma un valore al più pari ad αOP T R ,
per qualche valore α ragionevolmente piccolo. Se riuscissimo in ciò, allora in virtù della
(29) avremmo ottenuto un vettore soluzione al sistema (23) per cui la funzione obiettivo
assume valore al più αOP T . In altri termini, avremmo ottenuto un Vertex Cover di
G di peso pari al più ad α per il peso del Vertex Cover ottimo (cioè di minimo peso).
Ovvero, otterremmo un algoritmo di approssimazione per Vertex Cover in grafi pesati
con coefficiente di approssimazione pari ad α.
Procediamo nel seguente modo. Dato il vettore x ∗ , definiamo il vettore x a componenti
38
ASDII:Parte B — Lezione 5
intere nel seguente modo
∀v ∈ V
x(v) =
1
0
se x∗ (v) ≥ 1/2,
se x∗ (v) < 1/2.
(30)
Mostriamo innanzitutto che S = {v : x(v) = 1} è un Vertex Cover di G. Poichè
il vettore x∗ soddisfa i vincoli del sistema di PL (26), (in particolare soddisfa la (27))
abbiamo che ∀(u, v) ∈ E o vale che x∗ (u) ≥ 1/2 oppure che x∗ (v) ≥ 1/2. Pertanto, dalla
(30) abbiamo che per ogni arco (u, v) ∈ E o vale che x(u) = 1, oppure x(v) = 1 (nota che
entrambe le relazioni possono valere). Ovvero, dalla definizione di S si ha che per ogni
arco (u, v) ∈ E o vale che u ∈ S oppure che v ∈ S, cioè S è un Vertex Cover di G.
Valutiamo ora il peso di S. Si ha che esso è pari a
X
u∈V
x(u)w(u) ≤
X
u∈V
2x∗ (u)w(u) = 2OP TR ≤ 2OP T.
In conclusione, l’algoritmo sopra esposto che costruisce un Vertex Cover di G “arrotondando” la soluzione ottima del sistema di PL (26), restituisce una soluzione di valore
al più 2OP T . Ovvero, abbiamo ottenuto un algoritmo di approssimazione per Vertex
Cover in grafi pesati con fattore di approssimazione pari a 2.
Applichiamo ora la stessa tecnica al problema del
SET COVER
• Input:
– insieme U = {e1 , . . . , en }, famiglia S = {S1 , . . . , Sm }, con Si ⊆ U ;
– funzione costo c : S ∈ S → c(S) ∈ R+ ;
• Output: sottofamiglia S 0 ⊆ S di costo c(S 0 ) =
S
S∈S 0 S ⊇ U .
P
S∈S 0
c(S) minimo tale che
Formuliamo innanzitutto il problema mediante la PL. Ad ogni sottofamiglia S 0 ⊆ S
associamo un vettore binario x(S 0 ) = (x(S1 ), . . . , x(Sm )) tale che
x(Si ) =
1
0
se Si ∈ S 0
se Si ∈
6 S 0.
P
(31)
Il costo della famiglia S 0 si esprimerà come S∈S 0 x(S)c(S), il vincolo che gli insiemi in
S 0 coprano tutto U lo si esprime con la diseguaglianza
X
S:e∈S
x(S) ≥ 1
∀e ∈ U.
39
ASDII:Parte B — Lezione 5
Pertanto, il problema del Set Cover lo si potrà esprimere con il seguente sistema di PL
X
minimizzare
x(S)c(S)
(32)
S∈S
X
soggetto a
S:e∈S
x(S) ≥ 1
x(S) ∈ {0, 1}
∀e ∈ U
(33)
∀S ∈ S.
(34)
Il primo passo per derivare un algoritmo di approssimazione consiste nel passare alla
versione rilassata del sistema (32), in cui i vincoli di interezza (34) sulle variabili x(S)
vengono sostituiti con dei semplici vincoli di non negatività. La versione rilassata di (32)
è
X
minimizzare
x(S)c(S)
(35)
S∈S
X
soggetto a
S:e∈S
x(S) ≥ 1
x(S) ∈ [0, 1]
∀e ∈ U
∀S ∈ S.
(36)
(37)
Il problema di PL (35) è risolubile in tempo polinomiale. Sia x ∗ = (x∗ (S1 ), . . . , x∗ (Sm )) il
vettore soluzione che minimizza la funzione obiettivo di (35) sotto i vincoli (36) e (37), e
sia OP TR il valore (minimo) della funzione obiettivo di (35) in corrispondenza al vettore
x∗ . La relazione che sussiste tra OP TR ed il valore OP T della soluzione ottima al sistema
(35) è
OP TR ≤ OP T.
(38)
In generale, la diseguaglianza può essere anche stretta. Consideriamo infatti il seguente
esempio. Sia U = {e, f, g}, S = {S1 , S2 , S3 }, con S1 = {e, f }, S2 = {f, g}, S3 = {e, g}.
Inoltre supponiamo che c(S1 ) = c(S2 ) = c(S3 ) = 1. È impossibile coprire U con un solo
insieme, mentre lo è con due insiemi. Pertanto OP T = 2. È facile inoltre verificare che il
vettore x = (x(S1 ), x(S2 ), x(S3 )) = (1/2, 1/2, 1/2) soddisfa i vincoli di (35). Pertanto,
OP TR ≤
X
x(S)c(S) =
S∈S
1 1 1
3
+ + = < 2 = OP T.
2 2 2
2
Vediamo ora come sia possibile ottenere una soluzione al sistema di PL (32) (non necessariamente ottima, ma per cui il valore della funzione obiettivo non si discosti troppo da
quello ottimo) a partire da x∗ , soluzione ottima del sistema (35). Per ogni elemento e ∈ U
sia f (e) = numero di insiemi S ∈ S che contengono e, e sia f = max e∈U f (e). Definiamo
quindi il vettore x = (x(S1 ), . . . , x(Sm )) nel seguente modo
x(Si ) =
1
0
se x∗ (Si ) ≥ 1/f
se x∗ (Si ) < 1/f .
(39)
In altri termini, stiamo proponendo il seguente algoritmo per il problema del Set Cover
.
40
ASDII:Parte B — Lezione 5
1. Risolvi il sistema di PL rilassato, sia x ∗ la sua soluzione ottima;
2. Output la famiglia S 0 ⊆ S, dove S 0 = {S ∈ S : x∗ (S) ≥ 1/f }.
Proviamo innanzitutto che l’algoritmo produce un Set Cover , ovvero che il vettore
x = (x(S1 ), . . . , x(Sm )) soddisfa i vincoli del sistema (32) (o, come si dice in gergo, che
x è una soluzione ammissibile di (32). Partiamo dal fatto che il vettore x ∗ è soluzione
P
ammissibile a (35). In particolare, ciò implica che S:e∈S x∗ (S) ≥ 1, ∀e ∈ U . D’altra
P
parte, dalla definizione del parametro f , si ha che la somma S:e∈S x∗ (S) contiene al
piú f termini, quindi esiste almeno un S che contiene e ∈ U e per cui x ∗ (S) ≥ 1/f . Di
conseguenza, per la (31) vale che ∀e ∈ U esiste almeno un S ∈ S che contiene e e per cui
il valore x(S) viene posto a 1. Ciò implica che S 0 = {S : x(S) = 1} = {S ∈ S : x∗ (S) ≥
P
1/f } è un Set Cover , ovvero che S:e∈S x(S) ≥ 1, per ogni e ∈ U . Che il vettore x
soddisfi anche il vincolo di interezza è ovvio.
Valutiamo ora il costo della soluzione prodotta dall’algoritmo, ovvero valutiamo il
valore della funzione obiettivo del sistema (32) in corrispondenza al vettore x definito
dalla (39). Ricordiamo che per definizione abbiamo x(S) ≤ f x ∗ (S), ∀S ∈ S. Si ha quindi
SOL =
X
S∈S
= f
x(S)c(S) ≤
X
S∈S
∗
X
f x∗ (S)c(S)
S∈S
x (S)c(S) = f OP TR ≤ f OP T.
Il che implica che abbiamo ottenuto un algoritmo di approssimazione per Set Cover
con fattore di approssimazione pari a f . In alcuni casi, tale fattore di approssimazione è
migliore di quello derivato nella Lezione 2, mediante l’algoritmo greedy.
La tecnica dell’arrotondamento della soluzione ottima alla versione rilassata di un
problema di PL con vincoli di interezza sulle variabili può ovviamente essere applicata
anche in altre situazioni. Consideriamo la seguente generalizzazione del problema del Set
Cover .
SET MULTICOVER
• Input:
– insieme U = {e1 , . . . , en }, famiglia S = {S1 , . . . , Sm }, con Si ⊆ U ;
– funzione costo c : S ∈ S → c(S) ∈ R+ ;
– vettore di interi a = (ae1 , . . . , aen ).
P
• Output: sottofamiglia S 0 ⊆ S di costo c(S 0 ) = S∈S 0 c(S) minimo tale che ogni
elemento e ∈ U appartenga ad almeno a e distinti insiemi in S 0 .
41
ASDII:Parte B — Lezione 5
Ritroviamo il problema del Set Cover nel caso in cui il vettore a sia a = (1, . . . , 1).
Formuliamo Set MultiCover come problema di PL. Avremo
X
minimizzare
x(S)c(S)
(40)
S∈S
X
soggetto a
S:e∈S
x(S) ≥ ae
x(S) ∈ {0, 1}
∀e ∈ U
∀S ∈ S.
(41)
(42)
La versione rilassata del problema (40) sarà
X
minimizzare
x(S)c(S)
(43)
S∈S
X
soggetto a
S:e∈S
x(S) ≥ ae
x(S) ∈ [0, 1]
∀e ∈ U
∀S ∈ S.
(44)
(45)
Sia ora, come in precedenza, f = maxe∈U f (e), dove f (e) = numero di sottoinsiemi S in
S che contengono e ∈ U . Sia x∗ = (x∗ (S1 ), . . . , x∗ (Sm )) una soluzione ottima al problema
(43) rilassato. Vale ovviamente che
OP TR =
X
S∈S
x∗ (S)c(S) ≤ OP T,
dove OP T è il valore ottimo del problema di PL originario (ovvero il costo di un Set
MultiCover di costo minimo). Definiamo il vettore x = (x(S 1 ), . . . , x(Sm )) nel seguente
modo
1 se x∗ (S) ≥ 1/f
∀S ∈ S x(S) =
(46)
0 se x∗ (S) < 1/f .
Il vettore x cosı́ definito è una soluzione ammissibile al sistema (40). Infatti, sappiamo
che per ipotesi
X
x∗ (S) ≥ ae ,
∀e ∈ U,
S:e∈S
∗
S:e∈S x (S)
P
inoltre ∀e ∈ U la somma
consta di al piú f termini, ciascheduno di valore
P
al piú 1. Pertanto, per ogni e ∈ U la somma S:e∈S x∗ (S) contiene almeno ae termini
di valore almeno pari a 1/f . Di conseguenza, il vettore x definito dalla (46) soddisfa la
relazione
X
x(S) ≥ ae
∀e ∈ U,
(47)
S:e∈S
f x∗ (S),
inoltre vale che x(S) ≤
∀S ∈ S. Quindi il costo della famiglia S 0 = {S : x(S) = 1}
(che è un legittimo Set MultiCover in virtú della (47)) è pari a
costo(S 0 ) =
X
S∈S
= f
x(S)c(S) ≤
X
S∈S
∗
X
f x∗ (S)c(S)
S∈S
x (S)c(S) = f OP TR ≤ f OP T.
42
ASDII:Parte B — Lezione 5
Concludiamo la lezione considerando il seguente problema di ottimizzazione:
HITTING SET
• Input:
– insieme U = {e1 , . . . , en }, famiglia S = {S1 , . . . , Sm } di sottoinsiemi di U ,
– funzione costo c : e ∈ U → R+ ,
– vettore di interi a = (a(S1 ), . . . , a(Sm )).
• Output: sottoinsieme H ⊆ U di costo c(H) =
|H ∩ S| ≥ a(S)
P
e∈H
c(e) minimo tale che
∀S ∈ S.
Si noti che il problema è una generalizzazione del problema del Vertex Cover considerato all’inizio di questa Lezione. Si ritrova infatti il problema del Vertex Cover nel
caso in cui ciascun Si ∈ S ha cardinalità |Si | = 2, ed il vettore a è composto da tutti 1.
Vogliamo derivare un algoritmo di approssimazione per Hitting Set con fattore di
approssimazione k, dove k = maxS∈S |S|. Per prima cosa formuliamo il problema mediante
la PL.
minimizzare
X
x(e)c(e)
(48)
e∈U
soggetto a
X
x(e) ≥ a(S)
e∈S
x(e) ∈ {0, 1}
∀S ∈ S
∀e ∈ U .
(49)
(50)
La versione rilassata del problema (48) sarà
minimizzare
X
x(e)c(e)
(51)
e∈U
soggetto a
X
e∈S
x(e) ≥ a(S)
x(e) ∈ [0, 1]
∀S ∈ S
∀e ∈ U .
(52)
(53)
Sia x∗ = (x∗ (e1 ), . . . , x∗ (en )) una soluzione ottima al problema (51) rilasato. Vale
ovviamente che
X
x∗ (e)c(e) ≤ OP T,
OP TR =
e∈U
dove OP T è il valore ottimo del problema di PL originario (ovvero il costo di un Hitting
Set di costo minimo). Definiamo il vettore x = (x(e 1 ), . . . , x(en )) nel seguente modo
∀e ∈ U
x(S) =
1
0
se x∗ (e) ≥ 1/k
se x∗ (e) < 1/k.
(54)
43
ASDII:Parte B — Lezione 5
Il vettore x cosı́ definito è una soluzione ammissibile al sistema (48). Infatti, sappiamo
che per ipotesi
X
x∗ (e) ≥ a(S),
∀S ∈ S,
e∈S
P
inoltre ∀S ∈ S la somma e∈S x∗ (e) consta di al piú k termini, ciascheduno di valore al
P
piú 1. Pertanto, per ogni S ∈ S la somma e∈S x∗ (e) contiene almeno a(S) termini di
valore almeno pari a 1/k. Infatti, se ció non fosse, ovvero se la somma contenesse al piú
a(S) − 1 termini di valore almeno pari a 1/k, avremmo che
X
e∈S
x∗ (e) =
X
e∈S:x(e)∗ ≥1/k
≤ a(S) − 1 +
X
x∗ (e) +
X
x∗ (e)
(55)
e∈S:x(e)∗ <1/k
x∗ (e)
(56)
e∈S:x(e)∗ <1/k
< a(S) − 1 + 1
(in quanto ogni elemento della somma è ≤ 1) (57)
(in quanto la somma contiene al piú k termini(58)
)
= a(S)
(59)
Per quanto appena provato, il vettore x definito dalla (54) soddisfa la relazione
X
e∈S
x(e) ≥ a(S)
∀S ∈ S,
(60)
inoltre vale che x(e) ≤ kx∗ (e), ∀e ∈ U . Quindi il costo dell’insieme H = {e : x(e) = 1}
(che è un legittimo Hitting Set in virtú della (60)) è pari a
c(H) =
X
x(e)c(e)
e∈U
≤
X
kx∗ (e)c(e)
e∈U
= k
X
x∗ (e)c(e)
e∈U
= kOP TR
≤ kOP T.
Concludiamo la lezione riassumendo il principio generale mediante il quale abbiamo
ottenuto gli di algoritmi di approssimazione descritti in questa lezione:
• formulare il problema di ottimizzazione come un problema di PL,
• rilassare i vincoli di interezza ed ottenere un problema di PL risolubile in tempo
polinomiale,
• arrotondare a valori interi le componenti della soluzione ottima al problema di PL
rilassato per ottenere una soluzione ammissibile al problema originale.
ASDII:Parte B — Lezione 5
44
La bontà (o meno) della soluzione al problema originale cosı́ ottenuta dipende in
maniera critica da che criterio si è usato per arrotondare la soluzione ottime del problema rilassato. Il criterio usato in questa lezione è molto semplice ed intuitivo, ma ha
portata limitata (ovvero non sempre produce soluzioni intere di “buon” valore). Nella
lezione prossima vedremo una tecnica di arrotondamento di piú ampia applicabilità .
45
ASDII:Parte B — Lezione 6
Algoritmi e Strutture Dati II: Parte B
Anno Accademico 2004-2005
Lezione 6
Docente: Ugo Vaccaro
Nella lezione scorsa abbiamo introdotto una tecnica basata sulla PL per il progetto di
algoritmi di approssimazione. Essenzialmente, la tecnica è composta da tre passi.
1. Formulare il problema come problema di PL a vincoli interi;
2. Rilassare il vincolo di interezza e ottenere un normale problema di PL, risolubile in
tempo polinomiale;
3. Usare la soluzione ottima al problema rilassato per ottenere in tempo polinomiale una
soluzione ammissibile al problema di PL a vincoli interi. La soluzione al problema
con vincoli interi dovrà essere tale da avere un valore prossimo al valore ottimo del
problema rilassato.
Considerando ad esempio un problema di ottimizzazione di minimo con OP T R pari
al valore della soluzione ottima al problema rilassato, se fossimo in grado di trovare una
soluzione al problema di PL con vincoli interi di valore SOL pari al piú a αOP T R , allora avremmo immediatamente ottenuto un algoritmo di approssimazione con fattore di
approssimazione pari ad α. Infatti, sappiamo che vale la relazione OP T R ≤ OP T =valore
della soluzione ottima al problema di PL con vincoli interi.
La parte complessa della procedura sopra esposta è ovviamente il passo 3. Nella lezione
scorsa abbiamo visto come esso possa essere implementato, in alcuni casi, arrotondando a
valori interi le componenti della soluzione ottima al problema rilassato. Come effettuare
l’arrotondamento dipende criticamente dal problema in questione. In questa lezione vedremo un criterio abbastanza generale su come effettuare l’arrotondamento. Supponiamo
di avere a disposizione una funzione random(·) cosı́ definita:
random:p ∈ [0, 1] → random(p) ∈ {0, 1}
random(p) = 1 con probabilità p
random(p) = 0 con probabilità 1 − p.
La tecnica in questione per convertire una soluzione ottima al problema di PL rilassato
in una “buona” soluzione al problema di PL a vincoli interi è la seguente
46
ASDII:Parte B — Lezione 6
Arrotondamento probabilistico
1. Formula il problema in esame mediante un sistema di PL a vincoli interi e con
variabili xi ∈ {0, 1} nella funzione obiettivo.
2. Rilassa il problema, risolvilo ottimalmente e siano x ∗i ∈ [0, 1] i valori del vettore
ottimo di soluzione.
3. Assegna valori interi alle variabili x i nel modo seguente
if random(x∗i ) = 1 then xi ← 1;
else xi ← 0.
In altri termini, interpretiamo i valori x ∗i come probabilitá di porre le variabili x i a 0
oppure a 1 nel sistema di PL a vincoli interi. Il passo successivo consiste nello stimare di
quanto il valore della soluzione intera si discosta da OP T R .
Applichiamo questa tecnica al problema del Set Cover . Ricordiamo il problema
SET COVER
• Input:
– insieme U = {e1 , . . . , en }, famiglia S = {S1 , . . . , Sm }, con Si ⊆ U , per i = 1, . . . , m;
– funzione costo c : S ∈ S → c(S) ∈ R+ ;
• Output: sottofamiglia S 0 ⊆ S di costo c(S 0 ) =
S
S∈S 0 S ⊇ U .
P
S∈S 0
c(S) minimo tale che
la sua formulazione via PL a vincoli interi
minimizzare
X
x(S)c(S)
(61)
S∈S
soggetto a
X
S:u∈S
x(S) ≥ 1
x(S) ∈ {0, 1}
∀u ∈ U
(62)
∀S ∈ S.
(63)
e la corrispondente versione rilassata del problema di PL
minimizzare
X
S∈S
x(S)c(S)
(64)
47
ASDII:Parte B — Lezione 6
X
soggetto a
S:u∈S
x(S) ≥ 1
x(S) ∈ [0, 1]
∀u ∈ U
∀S ∈ S.
(65)
(66)
Sia x = (x(S1 ), . . . , x(Sm )) una soluzione ottima al problema rilassato (64). In accordo
allo schema di algoritmo prima enunciato, costruiamo una possibile soluzione intera al
sistema (61), ponendo la i-esima variabile a 1 con probabilità pari al valore x(S i ), ed
a 0 con probabilità pari a 1 − x(Si ). In altri termini, stiamo costruendo una possibile
soluzione S 0 ⊆ S al problema del Set Cover inserendo l’insieme S i in S 0 con probabilità
pari a x(Si ), per ogni i = 1, . . . , m. Di conseguenza, il costo medio E[c(S 0 )] della soluzione
S 0 ⊆ S è :
E[c(S 0 )] =
X
S∈S
=
X
c(S)P r{S sia stato inserito in S 0 }
c(S)x(S) = OP TR .
S∈S
Stimiamo ora la probabilitá che S 0 sia effettivemente un cover, ovvero stimiamo la probabilitá che valga l’evento
[
U⊆
S.
S∈S 0
Dato un generico u ∈ U , siano Si1 , . . . , Sik tutti e solo gli elementi di S che contengono u.
Avremo allora
P r{u sia coperto da S 0 } = 1 − P r{u non sia coperto da S 0 }
= 1 − P r{nessun insieme Sij , j = 1, . . . , k è stato inserito in S 0 }
= 1 − P r{Si1 non è stato inserito in S 0 } · . . .
. . . · P r{Sik non è stato inserito in S 0 }
= 1 − (1 − x(Si1 ))(1 − x(Si2 )) . . . (1 − x(Sik )).
Ricordiamo ora che i valori x(Si1 ), x(Si2 ) . . . x(Sik ) soddisfano la condizione
k
X
j=1
x(Sij ) ≥ 1,
ció in quanto gli insiemi Si1 , Si2 , . . . Sik sono tutti quelli e solo che contengono u ∈ U ,
e pertanto i valori x(Si1 ), x(Si2 ) . . . x(Sik ) soddisfano la (65). Sotto tale ipotesi, faremo
vedere che
1 k
(1 − x(Si1 ))(1 − x(Si2 )) . . . (1 − x(Sik )) ≤ 1 −
.
(67)
k
Per il momento, assumiamo che la diseguaglianza di sopra valga, pertanto abbiamo che
1
P r{u sia coperto da S } ≥ 1 − 1 −
k
0
k
1
≥1− ,
e
48
ASDII:Parte B — Lezione 6
dove abbiamo usato il fatto che
lim
k→∞
ed inoltre
1
1−
k
Abbiamo quindi ottenuto che
k
1−
1
k
k
=
1
≤ 1−
k+1
1
,
e
k+1
1
≤ .
e
P r{u non sia coperto da S 0 } ≤
1
.
e
Ripetiamo d log n volte l’esperimento casuale di scegliere S 0 in accordo alle probabilitá
x(Si ), dove d è scelto in modo tale che (1/e) d log n ≤ 1/(4n). In altre parole, costruiamo
indipendentemente l’una dall’altra d log n sottofamiglie S 10 , . . . , Sd0 log n , dove la probabilitá
che il generico insieme Si ∈ S venga inserito nella generica sottofamiglia S t0 , è sempre
x(Si ), per ogni i = 1, . . . , m e per ogni t = 1, . . . , d log n. Sia infine C = S 10 ∪ . . . ∪ Sd0 log n .
Dato un generico elemento u ∈ U , avremo che
P r{u non è coperto da C} = P r{u non è coperto da nessun S i0 , i = 1, . . . , d log n}
=
≤
≤
dY
log n
P r{u non è coperto da Si0 }
i=1
dY
log n i=1
1
4n
1
e
(per come abbiamo scelto d)
Di conseguenza, la probabilitá che C non sia un cover per U è
P r{C non è un cover per U } = P r{ui non è coperto da C, per qualche i = 1, . . . , d log n}
≤
≤
n
X
i=1
n
X
i=1
P r{ui non è coperto da C}
1
1
=
4n
4
Inoltre, il costo medio E[c(C)] di C soddisfa
E[c(C)] =
dX
log n
i=1
E[c(Si0 )] ≤ d log nOP TR ,
dove abbiamo usato il fatto che la media di una somma di variabili casuali è la somma
delle medie delle singole variabili casuali. Ricordiamo ora la diseguaglianza di Markov, la
49
ASDII:Parte B — Lezione 6
quale afferma che data una variabile casuale X, con media E[X] ed un valore t, vale:
P r{X ≥ t} ≤
E[X]
.
t
Applicando la diseguaglianza di Markov alla variabile casuale c(C), con t = 4d log nOP T R ,
otteniamo
1
E[c(C)]
≤ .
P r{c(C) ≥ 4d log nOP TR } ≤
4d log nOP TR
4
Mettendo tutto insieme possiamo valutare la probabilitá degli eventi a noi sfavorevoli,
ovvero che C non sia un cover o che C abbia un costo > 4d log nOP T R . Avremo quindi
P r{C non è un cover per U oppure abbia costo c(C) ≥ 4d log nOP T R }
≤ P r{C non è un cover per U } + P r{c(C) ≥ 4d log nOP T R }
1
1 1
≤ + =
4 4
2
Di conseguenza, la probabilitá che l’algoritmo ci dia l’output che noi desideriamo, ovvero
che C sia un cover e che contemporaneamente abbia un costo ≤ 4d log nOP T R è pari a 1
meno la probabilità di sopra, ovvero è almeno 1/2.
Quindi, la probabilitá che le scelte casuali effettuate dall’algoritmo abbiano prodotto un
cover di U di costo totale ≤ 4d log nOP T R è almeno 1/2. Ciò implica che il numero medio
di volte che dobbiamo eseguire l’algoritmo per avere il cover di U che desideriamo, ovvero di
di costo ≤ 4d log nOP TR , è al piú 2. Questo conclude la prova che l’arrotondamento probabilistico produce un Set Cover di U di costo O(log n)OP T R = O(log n)OP T , quindi
confrontabile con il costo della soluzione prodotta dall’algoritmo greedy che, ricordiamo,
era limitato superiormente da (log n + 1)OP T .
50
ASDII:Parte B — Lezione 6
Prova della diseguaglianza di Markov per variabili casuali discrete.
Supponiamo che la variabile casuale X assuma al piú n valori. Si ha
E[X] =
n
X
k=1
kP r{X = k} ≥
n
X
k=t
kP r{X = k} ≥ t
Prova della (67). Vogliamo massimizzare
Si ha
k
Y
1
log (1 − xi ) =
k
i=0
n
X
k=t
Qk
i=0 (1 − xi )
P r{X = k} = tP r{X ≥ t}.
sotto il vincolo che
Pk
i=0 xi
≥ 1.
k
1X
log(1 − xi )
k i=0
≤ log
= log
k
1X
(1 − xi ) (dalla diseguaglianza di Jensen)
k i=0
k−
Pk
i=0 (1
− xi )
k 1
k−1
.
= log 1 −
≤ log
k
k
Ovvero,
log
k
Y
i=0
(1 − xi ) ≤ k log 1 −
1
k
= log 1 −
1
k
k
,
il che è perfettamente equivalente a ciò che intendevamo provare.
Nel resto di questa lezione applicheremo la tecnica dell’arrotondamento probabilistico
al problema del MaxSat .
MAX SAT
• Input:
– n variabili booleane x1 , . . . , xn
– m clausole C1 , . . . , Cm , dove ogni Ci è un OR di letterali, ovvero Ci = αi1 ∨. . .∨αik ,
ed ogni αij è una qualche variabile booleana x s od un suo negato
– pesi w(Ci ) ≥ 0 per ogni clausola Ci
• Output: un assegnamento di valori di verità VERO/FALSO alle x i che massimizzi
la somma dei pesi delle clausole Ci soddisfatte.
51
ASDII:Parte B — Lezione 6
Ricordiamo i 3 passi costituenti la tecnica dell’arrotondamento probabilistico, ed applichiamoli a MaxSat .
Passo 1. Modelliamo MaxSat con il seguente problema di PL a variabili intere, in cui
introduciamo una variabile z(C) per ogni clausola C, ed una variabile y i per ogni variabile
booleana xi . Il significato delle variabili z(C) e y i è il solito
∀C
∀i
z(C) =
yi =
1
0
1
0
se C è soddisfatta,
se C non è soddisfatta,
se xi ha valore di verità VERO,
se xi ha valore di verità FALSO.
Inoltre, per ogni clausola C, sia I + (C) l’insieme degli indici i delle variabili booleane
xi che compaiono in C in forma non negata, e sia analogamente I − (C) l’insieme degli
indici j delle variabili booleane x j che compaiono in C in forma negata. Pertanto, la
clausola C è soddisfatta se e solo se almeno una delle variabili x i con i ∈ I + (C) assume
valore VERO, oppure almeno una delle variabili x j con j ∈ I − (C) assume valore FALSO.
Ricordando le definizioni delle variabili z(C) e y i , avremo che z(C) = 1 se e solo se almeno
una delle variabili yi con i ∈ I + (C) assume valore 1, oppure almeno una delle variabili y i
con i ∈ I − (C) assume valore 0.
Avremo allora il seguente sistema di PL che formalizza il problema MaxSat .
massimizzare
X
w(C)z(C)
C
soggetto a
X
yi +
i∈I + (C)
yi ∈ {0, 1}
(68)
X
i∈I − (C)
0 ≤ z(C) ≤ 1
∀i
(1 − yi ) ≥ z(C)
∀C
(69)
(70)
∀C.
(71)
Passo 2. Rilassa i vincoli yi ∈ {0, 1}, ∀i a 0 ≤ yi ≤ 1, ∀i.
Passo 3. Applicando la tecnica dell’arrotondamento probabilistico otteniamo il seguente
algoritmo
Arrotondamento probabilistico per MAX SAT
Risolvi il sistema (68), sia (y ∗ , z∗ ) la soluzione ottima
for i ← 1 to n do
if random(yi∗ ) = 1
xi ← 1
else
xi ← 0
In altri termini, nel precedente algoritmo interpretiamo ciascun valore y i∗ come la
probabilità di porre la variabile booleana x i ad 1 (ovvero, a valore TRUE).
52
ASDII:Parte B — Lezione 6
Per definizione, avremo che la clausola C non sarà soddisfatta se e solo se tutte le
variabili con indici in I + (C) sono state poste a valore 0 e tutte le variabili con indici in
I − (C) a valore 1. Abbiamo quindi
P r{C soddisfatta } = 1 − P r{C non soddisfatta } = 1 −
Y
j∈I + (C)
(1 − yj∗ ) ·
Y
yj∗ . (72)
j∈I − (C)
Per valutare in maniera precisa la espressione (72) abbiamo bisogno di alcuni risultati
intermedi. Supponiamo di avere un insieme di numeri S, con |S| = k, e sia data una
partizione di S in due insiemi A, B ⊆ S, con A ∪ B = S, e A ∩ B = ∅. Supponiamo che
valga
X
X
(1 − b) ≥ z,
(73)
a+
b∈B
a∈A
per qualche numero z. Allora vale anche
k−z ≥
=
X
a∈A
X
b∈B
1+
X
a∈A
X
b∈B
a∈A
=
X
a+
(1 − b)
1−
(1 − a) +
X
a∈A
X
b∈B
−
X
b∈B
(1 − b)
(1 − 1 + b) =
X
a∈A
(1 − a) +
X
b.
b∈B
Ricordando la diseguaglianza tra la media aritmetica e geometrica, che ci dice
Pk
i=1 βi
≥
k
otteniamo
ovvero
k
Y
βi
i=1

! k1

∀βi ,
,

1
k
X
Y
Y
1 X
k−z
≥ 
(1 − a) +
b ≥ 
(1 − a) ·
b ,
k
k a∈A
a∈A
b∈B
b∈B
1−
z
k
k
≥
Y
a∈A
(1 − a) ·
Y
b.
b∈B
Ritorniamo al nostro problema originario ed applichiamo i risultati appena ottenuti. Abbiamo che
P r{C soddisfatta } = 1 −
Y
j∈I + (C)
≥ 1− 1−
≥ 1−
(1 − yj∗ ) ·
z ∗ (C)
k
z ∗ (C)
1
e
,
k
Y
j∈I − (C)
yj∗
53
ASDII:Parte B — Lezione 6
dove k è il numero di variabili nella clausola C. Possiamo osservare che per valori di x
compresi tra 0 ed 1 la curva di equazione 1 − (1/e) x è sempre maggiore della retta di
equazione (1 − 1/e)x (vedi figura).
(1−1/e)x
1−1/e
1−(1/e)
x
1
Pertanto
P r{C soddisfatta } ≥ 1 −
1 ∗
z (C).
e
Il costo medio della soluzione prodotta dall’algoritmo, ovvero il valor medio E della somma
dei pesi delle clausole soddisfatte in conseguenza dell’assegnamento di valori di verità sopra
definito sarà quindi
E =
X
C
≥
=
P r{C soddisfatta }w(C)
1−
1−
1 X
w(C)z ∗ (C)
e C
1
1
OP TR ≥ 1 −
OP T.
e
e
Concludendo, l’algoritmo basato sulla tecnica dell’arrotondamento probabilistico produce una soluzione che si discosta da quella ottima di un fattore moltiplicativo pari a
(1 − 1/e). Ci limitiamo qui a menzionare che molti degli algoritmi ottenibili con la tecnica dell’arrotondamento probabilistico possono essere “derandomizzati”, ovvero possono
essere trasformati in algoritmi completamente deterministici, senza alcuna perdita di performance.
54
ASDII:Parte B — Lezione 7
Algoritmi e Strutture Dati II: Parte B
Anno Accademico 2004-2005
Lezione 7
Docente: Ugo Vaccaro
Nelle lezioni scorse abbiamo visto varie applicazioni della PL al progetto di algoritmi
di approssimazione per problemi di ottimizazione NP-hard. Essenzialmente abbiamo visto
che in certi casi, arrotondando a valori interi la soluzione ottima del sistema di PL rilassato, si poteva ottenere una soluzione abbastanza buona al sistema di PL a vincoli interi.
L’arrotondamento poteva essere sia deterministico che probabilistico. In questa lezione e
nella prossima vedremo altre tecniche per ottenere algoritmi di approssimazioni, sempre
basati sull PL, ma che sfruttano altri principi. Le tecniche che studeriemo hanno una
validità piú generale rispetto a quelle viste nelle lezioni precedenti, e sono basate su Principio delle Dualità della Programmazione Lineare. Illustriamo tale principio attraverso un
semplice esempio. Supponiamo di avere il seguente sistema di PL:
minimizzare 7x1 + x2 + 5x3
soggetto a x1 − x2 + 3x3 ≥ 10
5x1 + 2x2 − x3 ≥ 6
x1 , x 2 , x 3 ≥ 0
(74)
(75)
(76)
(77)
Notiamo che in questo esempio tutti i vincoli sono del tipo “≥ ” e tutte le variabili sono
vincolate ad essere non negative. Questa è la forma standard dei problemi di minimizzazione di PL e una semplice trasformazione ci permette di scrivere ogni sistema di PL di
minimo in questo modo. Ricordiamo che ogni soluzione del sistema, ovvero ogni assegnamento di valori alle variabili che soddisfi tutti i vincoli è detta una soluzione ammissibile.
Sia z∗ il valore ottimo del sistema di PL di sopra, e chiediamoci “ É z∗ ≤ α?”, dove α è
un dato numero. Per esempio, chiediamoci se z ∗ è minore o tutt’al piú uguale a 30. Un
certificato Si per questa domanda è semplicemente una soluzione ammissibile al sistema
di PL per cui la funzione obiettivo assume valore 30. Ad esempio, x = (2, 1, 3) costituisce
un tale certificato in quanto x soddisfa tutti i vincoli del sistema di PL ed il valore della
funzione obiettivo in corrispondenza ad x è 7 · 2 + 1 + 5 · 3 = 30. Pertanto, ogni certificato
Si fornisce una limitazione superiore al valore z ∗ .
Come possiamo fornire un certificato No alla stessa domanda “ É z∗ ≤ α?”, ovvero
come possiamo determinare una limitazione inferire al valore z ∗ ? Nel nostro esempio una
limitazione inferiore la possiamo ottenere dal primo vincolo del sistema. Infatti, poichè
le variabili sono costrette ad assumere valori non negativi e poichè la funzione obiettivo è
tale che
7x1 + x2 + 5x3 ≥ x1 − x2 + 3x3
≥ 10
(dalla (75))
55
ASDII:Parte B — Lezione 7
abbiamo sicuramente che la funzione obiettivo vale almeno 10 in corrispondenza ad ogni
soluzione ammissibile del sistema. Ciò ovviamente implica che z ∗ ≥ 10. Una migliore
limitazione inferiore a z∗ la si può ottenere considerando entrambi i vincoli del sistema
di PL (74). Infatti possiamo dedurre che
7x1 + x2 + 5x3 ≥ (x1 − x2 + 3x3 ) + (5x1 + 2x2 − x3 )
≥ 10 + 6
(dalla (75) e (76))
Pertanto, per ogni soluzione ammissibile la funzione obiettivo vale almeno 16, e quindi
possiamo affermare che z∗ ≥ 16. L’idea può ovviamente essere estesa. Per esempio,
possiamo osservare che
7x1 + x2 + 5x3 ≥ 2 · (x1 − x2 + 3x3 ) + (5x1 + 2x2 − x3 )
≥ 2 · 10 + 6
(dalla (75) e (76))
ed ottenere, per lo stesso ragionamento di prima, che z ∗ ≥ 26. Nulla ci impedisce di
procedere ulteriormente ed andare quindi alla ricerca di moltiplicatori y 1 e y2 , uno per
ciascun vincolo del sistema (74), in modo tale che
7x1 + x2 + 5x3 ≥ y1 · (x1 − x2 + 3x3 ) + y2 · (5x1 + 2x2 − x3 )
≥ y1 · 10 + y2 · 6
(dalla (75) e (76))
Ovviamente, affinchè y1 · 10 + y2 · 6 sia una valida limitazione inferiore al valore della
funzione obiettivo 7x1 + x2 + 5x3 occorre che i valori y1 e y2 soddisfino i vincoli
y1 + 5y2 ≤ 7
−y1 + 2y2 ≤ 1
3y1 − y2 ≤ 5
Visto che vogliamo trovare la migliore limitazione inferiore al valore della funzione obiettivo
7x1 + x2 + 5x3 , ci troviamo di fronte al seguente problema
massimizzare 10y1 + 6y2
(78)
soggetto a y1 + 5y2 ≤ 7
(79)
3y1 − y2 ≤ 5
(81)
−y1 + 2y2 ≤ 1
(80)
y1 , y 2 ≥ 0
(82)
Ovvero, abbiamo un ulteriore sistema di PL, chiaramente collegato a (74). Chiamiamo il
sistema (74) il programma primale ed il sistema (78) il programma duale. Per costruzione,
ogni soluzione ammissibile al duale fornisce una limitazione inferiore al valore ottimo del
primale. Ovviamente vale anche il viceversa: ogni soluzione ammissibile al primale fornisce una limitazione superiore al valore ottimo del duale. Pertanto, se potessimo trovare
56
ASDII:Parte B — Lezione 7
ottimo duale= ottimo primale
26
0
soluzioni del primale
soluzioni del duale
soluzioni ammissibili al duale ed al primale le cui funzioni obiettivo coincidono di valore,
allora entrambe le soluzioni devono essere ottime. Nel nostro esempio, x = (7/4, 0, 11/4),
e y = (2, 1) entrambe fanno assumere valore 26 alle relative funzioni obiettivo; pertanto
sono entrambe ottime. La figura di sotto esemplifica la situazione:
Le considerazioni appena svolte ovviamente non si applicano solo all’esempio discusso,
ma valgono per ogni sistema di PL, e costituiscono il teorema centrale della programmazione lineare: il teorema delle dualità della programmazione lineare.
Per poterlo enunciare formalmente, consideriamo il seguente problema di minimo come
programma primale (avremmo potuto equivalentemente partire con un programma di
massimo come primale).
minimizzare
soggetto a
n
X
j=1
n
X
j=1
cj xj
aij xj ≥ bi
xj ≥ 0
i = 1, . . . , m
j = 1, . . . , n
dove aij , bi e cj sono numeri. Introduciamo la variabile duale y i per ogni disequazione nel
sistema di sopra. Otteniamo quindi il seguente programma duale
m
X
massimizzare
bi yi
i=1
m
X
soggetto a
i=1
aij yi ≤ cj
yi ≥ 0
j = 1, . . . , n
i = 1, . . . , m
Teorema 5 (della dualità nella PL) Il programma primale assume valore ottimo finito
se e solo se il programma duale assume valore ottimo finito. Se x ∗ = (x∗1 , . . . , x∗n ) e
∗ ) sono soluzioni ottime per il primale e per il duale, rispettivamente,
y∗ = (y1∗ , . . . , ym
allora
n
m
X
j=1
cj x∗j =
X
i=1
bi yi∗ .
57
ASDII:Parte B — Lezione 7
Ritornando all’esempio con cui abbiamo iniziato la lezione, abbiamo osservato che per
costruzione valeva che ogni soluzione ammissibile al programma duale fornisce una limitazione inferiore al valore ottimo del primale. In effetti, ogni soluzione ammissibile al
programma duale fornisce una limitazione inferiore al valore assunto dalla funzione obiettivo in corrispondenza ad ogni soluzione ammissibile del primale. Proviamolo formalmente
ed in generale.
Teorema 6 (della dualità debole) Se x = (x 1 , . . . , xn ) e y = (y1 , . . . , ym ) sono
soluzioni ammissibili al programma primale e duale, rispettivamente, allora
n
X
j=1
cj xj ≥
m
X
bi yi .
(83)
i=1
Dim. Poichè y è una soluzione ammissibile al duale, e le variabili x j sono non negative,
abbiamo
!
m
n
n
X
j=1
cj xj ≥
X X
j=1
aij yi xj .
(84)
i=1
Analogamente, poichè x è una soluzione ammissibile al primale, e le variabili y i sono non
negative, abbiamo


m
X
i=1

n
X
j=1
m
X
aij xj  yi ≥
bi yi .
(85)
i=1
Il teorema è quindi dimostrato una volta che si osservi che
n
m
X
X
j=1
i=1
!
aij yi xj =
m
X
i=1


n
X
j=1

aij xj  yi .
Dal teorema della dualità della PL, x e y sono entrambe soluzioni ottime se e solo se
(83) vale con il segno della eguaglianza. Ciò accade se e solo sia (84) e (85) valogono con
il segno dell’eguaglianza. Pertanto, otteniamo il seguente risultato circa la struttura delle
soluzioni ottime:
Teorema 7 (Condizioni complementari di slackness) Siano x e y soluzioni ammissibili al primale ed al duale, rispettivamente. Allora, x e y sono entrambe ottimali se e
solo se entrambe le seguenti condizioni sono soddisfatte:
Condizioni complementari di slackness primali
P
Per ogni 1 ≤ j ≤ n: o vale che xj = 0 oppure m
i=1 aij yi = cj ; e
Condizioni complementari di slackness duali
P
Per ogni 1 ≤ i ≤ m: o vale che yi = 0 oppure ni=1 aij xj = bj ;
58
ASDII:Parte B — Lezione 7
Le condizioni complementari di slackness giocheranno un ruolo fondamentale nel progetto
di algoritmi di approssimazione basati su PL, come vedremo nella prossima lezione.
Nel resto di questa lezione useremo le tecniche appena introdotte per presentare una
analisi alternativa (e leggermente migliorata) dell’algoritmo greedy per Set Cover visto
nella Lezione 2. Risulterà anche maggiormente chiaro il procedimento mediante il quale
veniva stimato il valore OP T della soluzione ottima a Set Cover .
Il procedimento che seguiremo è il seguente. 5 Dato un algoritmo per Set Cover
cercheremo di dedurre da esso una assegnazione di valori alle variabili del sistema duale al
sistema di PL che descrive Set Cover . Tale assegnazione dovrà rendere il valore della
funzione obiettivo del duale uguale al valore SOL prodotto dall’algoritmo greedy, ovvero
pari al valore della funzione obiettivo del primale in corrispondenza al vettore ammissibile
ottenibile dalla soluzione prodotta da greedy. Ricordiamo che la relazione tra le quantità
OP TR , OP T , e SOL è raffigurata nella figura di sotto
OPT
OPT
R
SOL
soluzioni al primale intero
soluzioni al duale rilassato
soluzioni al primale rilassato
La soluzione di valore SOL cosı́ ottenuta al duale non sarà in generale ammissibile
(si trova infatti al di fuori della regione di ammissibilità del duale). Il passo successivo
consisterà nel dividere la soluzione del duale per un fattore F in modo tale da diminuirne
il valore da SOL a SOL/F e renderla ammissibile (vedi figura di sotto)
Avremmo pertanto mostrato che per il valore SOL della soluzione a Set Cover
prodotta dall’algoritmo greedy vale che
SOL = F ·
SOL
F
≤ F · OP TR ≤ F · OP T
a causa del Teorema debole della dualità e del fatto che la soluzione al duale di valore
SOL/F è adesso ammissibile, e quindi di valore ≤ OP T R ≤ OP T . Mettendo tutto
insieme, abbiamo di fatto provato che il nostro procedimento è in effetti un algoritmo di
approssimazione con fattore di approssimazione F .
5
Illustreremo il procedimento usando Set Cover come sempio. Si noti però che esso, in linea di
principio, è applicabile ad ogni problema.
59
ASDII:Parte B — Lezione 7
OPT
OPT
R
SOL/F
SOL
soluzioni al primale intero
soluzioni al duale rilassato
soluzioni al primale rilassato
Applichiamo ora quanto detto. Ricordiamo la formulazione di Set Cover via PL
X
minimizzare
x(S)c(S)
S∈S
X
soggetto a
S:e∈S
x(S) ≥ 1
x(S) ∈ {0, 1}
∀e ∈ U
∀S ∈ S.
La versione rilassata del sistema di PL è
minimizzare
X
x(S)c(S)
S∈S
soggetto a
X
S:e∈S
x(S) ≥ 1
x(S) ≥ 0
∀e ∈ U
∀S ∈ S.
Introducendo una variabile ye per ogni vincolo del problema otteniamo il programma duale
massimizzare
X
ye
(86)
e∈U
soggetto a
X
e:e∈S
ye ≤ c(S)
ye ≥ 0
∀e ∈ U .
∀S ∈ S
(87)
(88)
Come possiamo interpretare il duale? Un modo intuitivo di pensare il sistema di PL (86) è
quello di immaginare che le variabili y e rappresentino “roba” che abbiamo “impacchettato”
in ogni elemento corrispondente e ∈ U , e stiamo cercando di massimizzare l’ammontare
di roba impacchettata, sotto la condizione che l’ammontare di roba impacchettata in
P
ogni S ∈ S, data da e:e∈S ye , non debba superare la “capacità ” dell’insieme S, qui
rappresentata dal suo costo c(S).
60
ASDII:Parte B — Lezione 7
L’interpretazione appena data al duale di dovrebbe aiutare nel nostro primo obiettivo che, ricordiamo, consiste nel dedurre dall’algoritmo greedy per Set Cover
un’assegnazione di valori alle variabili del sistema duale di PL, ovvero alle y e , ∀e ∈ U , tale
che il valore SOL della soluzione prodotta dall’algoritmo greedy sia esprimibile come
SOL =
X
ye .
e∈U
É il momento di ricordare l’algoritmo greedy per Set Cover
Algoritmo Greedy per Set Cover
1. C ← ∅, S 0 ← ∅ % (S 0 è il cover che vogliamo costruire, C è l’insieme degli elementi di U finora
coperti)
2. While C 6= U do
• Trova l’insieme S ∈ S con il valore c(S)/(|S − C|) minimo
• Sia α ← c(S)/(|S − C|) tale valore minimo
• Per ogni e ∈ S − C poni prezzo(e) ← α
• Poni S 0 ← S 0 ∪ {S}, C ← C ∪ S
3. Output S 0
Più importante ancora è ricordare che nella Lezione 2 abbiamo provato che
SOL =
X
prezzo(e).
e∈U
Ecco quindi trovato l’assegnamento alle variabili y e che cercavamo: basterà porre
ye = prezzo(e)
ed avremo
SOL =
X
∀e ∈ U
ye .
e∈U
Ovviamente, non è certo detto che tale assegnazione di valori alle variabili y e sia ammissibile, ovvero che rispetti il vincolo (87). Infatti, dall’analisi dell’algoritmo greedy effettuate
nella Lezione 2 ciò che sappiamo valere è
e
X
prezzo(e) =
ricoperti per la prima volta da
S
e
X
ye =
ricoperti per la prima volta da
c(S)
S
∀S ∈ S
il che implica che la diseguaglianza del vincolo (87) in generale non vale (essendo il membro
sinistro della diseguaglianza (87) una somma su di un numero di termini in generale maggiore). Eseguiamo allora il secondo passo del procedimento che abbiamo prima descritto,
61
ASDII:Parte B — Lezione 7
ovvero dividiamo i valori ye per un’opportuna quantità in modo da ottenere una soluzione
ammissibile per il duale (86). Poniamo
∀e ∈ U
P
ze =
ye
prezzo(e)
=
.
Hn
Hn
(89)
dove Hn = ni=0 1/i. Proviamo ora che il vettore z definito dalla (89) è una soluzione
ammissibile al sistema di PL duale dato da (86). A tal fine occorre provare che
X
e∈S
ze ≤ c(S)
∀S ∈ S.
(90)
La prova ricalca una analoga prova effettuata nella Lezione 2. Consideriamo un generico
S ∈ S e numeriamo i suoi elementi nell’ordine in cui essi vengono coperti per la prima volta
dall’algoritmo greedy. Sia quindi S = {e 1 , . . . , ek }. Consideriamo l’istante in cui l’elemento
ei viene ricoperto per la prima volta. Ciò vuol dire che in questo passo dell’algoritmo
l’insieme S contiene almeno k − i + 1 elementi non coperti. Pertanto, l’insieme S stesso
potrebbe coprire ei ad un costo medio ≤ c(S)/(k − i + 1). Poichè l’algoritmo greedy
sceglie ad ogni passo l’insieme X ∈ S che ha il miglior rapporto costo/(numero di elementi
che ricopre per la prima volta), ne segue che l’insieme scelto dall’algoritmo greedy avrà
un costo medio di copertura sicuramente ≤ c(S)/(k − i + 1). Ciò implica anche che
prezzo(ei ) ≤ c(S)/(k − i + 1), ovvero
z ei ≤
c(S)
1
·
.
Hn k − i + 1
Sommando su tutti gli elementi di S otteniamo
k
X
z ei
i=1
≤
c(S)
·
Hn
=
Hk
c(S) ≤ c(S)
Hn
1
1
+
+ ... +1
k k−1
e quindi il vettore z è ammissibile per il duale.
A questo punto, ricordando dalla Lezione 2 che il valore SOL della soluzione ritornata
P
dall’algoritmo greedy è pari a e∈U prezzo(e), otteniamo
SOL =
X
e∈U
= Hn
prezzo(e)
X
ze
(dalla (89))
e∈U
≤ Hn OP TR
(in quanto z è ammissibile per il duale)
≤ Hn OP T.
Abbiamo quindi (ri)provato che l’algoritmo greedy ha un fattore di approssimazione H n .
Due osservazioni sono in ordine
62
ASDII:Parte B — Lezione 7
1. Questo modo di procedere ci fà meglio capire qual è la limitazione inferiore a OP T
che usammo a suo tempo per valutare il fattore di approssimazione dell’algoritmo
P
greedy per Set Cover . Infatti, che ( e∈U prezzo(e)/Hn ) sia ≤ OP T discende
P
ora immediatamente dal fatto che ( e∈U prezzo(e)/Hn ) è il valore della funzione
obiettivo del duale in corrispondenza ad una soluzione ammissibile di esso. Pertanto
P
la diseguaglianza ( e∈U prezzo(e)/Hn ) ≤ OP T è una immediata conseguenza del
Teorema della dualità debole .
2. Questo modo di procedere ci permette di ottenere in maniera semplice anche una
analisi piò precisa dell’algoritmo greedy. Poniamo infatti
t = max |S| e Ht =
S∈cS
t
X
1
i=1
i
.
É semplice verificare che il vettore z definito dalle componenti
ze =
prezzo(e)
Ht
∀e ∈ U
è soluzione ammissibile al duale (86). Da ciò segue che
SOL =
X
e∈U
prezzo(e) ≤ Ht OP T
che è in generale migliore della limitazione SOL ≤ H n OP T.
In quel che segue illustreremo brevemente una tecnica per derivare un algoritmo di
approssimazione direttamente dal duale del sistema di PL che descrive il problema in questione (invece di usare il duale solo come strumento nell’analisi di un algoritmo ottenuto a
parte, come abbiamo appena fatto di sopra). Descriveremo la tecnica prendendo come esempio il problema dell’ Hitting Set . Ricordiamo innanzitutto il problema dell’Hitting
Set (in versione semplificata).
HITTING SET
• Input:
– insieme U = {e1 , . . . , en }, famiglia S = {S1 , . . . , Sm } di sottoinsiemi di U ,
– funzione costo c : e ∈ U → R+ ,
• Output: sottoinsieme H ⊆ U di costo c(H) =
|H ∩ S| ≥ 1
P
e∈H
∀S ∈ S.
c(e) minimo tale che
63
ASDII:Parte B — Lezione 7
La sua formulazione via PL è la seguente.
X
minimizzare
x(e)c(e)
(91)
e∈U
X
soggetto a
e∈S
x(e) ≥ 1
x(e) ∈ {0, 1}
∀S ∈ S
(92)
∀e ∈ U .
(93)
La versione rilassata del problema (91) è
minimizzare
X
x(e)c(e)
(94)
e∈U
soggetto a
X
e∈S
x(e) ≥ 1
x(e) ≥ 0
∀S ∈ S
∀e ∈ U .
(95)
(96)
Introducendo una variabile yS per ogni vincolo (95), otteniamo il seguente sistema di PL
duale a (94)
massimizzare
X
yS
(97)
S∈S
soggetto a
X
S:e∈S
yS ≤ c(e)
yS ≥ 0
∀e ∈ U
∀S ∈ S.
(98)
(99)
L’algoritmo che proponiamo per Hitting Set costruisce una soluzione H ⊆ U nel
seguente modo: vengono messi in H tutti e solo quegli elementi e ∈ U per cui il corrispondente vincolo
X
yS ≤ c(e)
S:e∈S
del duale è soddisfatto con il segno di eguaglianza. Qual’e la intuizione dietro questo modo
di procedere? Ciò che stiamo in effetti facendo è la cosa seguente: stiamo costruendo una
soluzione a Hitting Set (ovvero al sistema di PL che lo rappresenta) ponendo la variabile
P
x(e) al valore 1 (ovvero ponendo e nella soluzione) se e solo se S:e∈S yS = c(e). In altri
termini, stiamo costruendo una soluzione a valori interi al primale imponendo inoltre che
essa soddisfi la condizione di slackness primale. Abbiamo quindi il seguente algoritmo
Algoritmo per Hitting Set via Duale
1. Risolvi il sistema di PL duale (97), e sia y la sua soluzione ottima
2. H ← ∅
3. for e ∈ U
P
if S:e∈S yS = c(e)
then H ← H ∪ {e}
4. Return H
64
ASDII:Parte B — Lezione 7
Mostriamo che l’insieme H ritornato dall’algoritmo, ovvero l’insieme
H = {e ∈ U :
X
yS = c(e)}
(100)
S:e∈S
è una soluzione per il probleam dell’Hitting Set , ovvero vale che ∀S ∈ cS si ha H∩S 6= ∅.
Se infatti fosse H ∩ T = ∅ per qualche T ∈ S, ciò vorrebbe dire che
X
∀u ∈ T
yS < c(u).
S:u∈S
Osserviamo ora che la variabile yT compare nei vincoli di sopra del sistema di PL duale
(97) e solo nei vincoli di sopra. Quindi, potremmo aumentare il valore della variabile y T ,
in modo tale che i corrispondenti vincoli del sistema (97) siano ancora soddisfatti. Ma
ciò equivale a dire che possiamo trovare un’altra soluzione al sistema (97) migliore di y,
contro la ipotizzata ottimalità della soluzione y.
Stimiamo infine il fattore di approssimazione dell’algoritmo proposto. Abbiamo
SOL =
X
c(e)
e∈H
=
X X
yS
(dalla (100))
e∈H S:e∈S
=
X
S
|H ∩ S|yS .
L’ultima uguaglianza la si ottiene in quanto ogni y S compare nella somma una volta per
ogni e ∈ H che appartiene anche a S. Se quindi sapessimo che esiste un α tale che ogni
qualvolta yS > 0 allora |H ∩ S| ≤ α, allora otterremmo
SOL =
X
S
≤ α
|H ∩ S|yS
X
yS
S
≤ αOP TR
(in quanto y è ammissibile per il duale)
≤ αOP T
Per esempio, detto k = max{|S| : S ∈ S}, otteniamo banalmente che α ≤ k, e quindi
otterremmo un’altro algoritmo di approssimazione per Hitting Set con fattore di approssimazione k, diverso dall’algoritmo considerato nella Lezione 5.
65
ASDII:Parte B — Lezione 8
Algoritmi e Strutture Dati II: Parte B
Anno Accademico 2004-2005
Lezione 8
Docente: Ugo Vaccaro
Ricordiamo ancora una volta il nostro “meta-algoritmo” per il progetto di algoritmi di
approssimazione:
1. Formula il problema in questione come un problema di PL a vincoli interi
2. Rilassa il problema, sostituendo i vincoli di interezza con semplici vincoli di non
negatività
3. Usa la soluzione al problema rilassato per ottenere una soluzione al problema a
vincoli interi, di valore prossimo all’ottimo
Nelle lezioni scorse abbiamo visto vari metodi per implementare il passo 3. In questa
lezione ne vedremo un’ulteriore, abbastanza simile all’ultimo metodo visto ed applicato
nella lezione scorsa. Intendiamo riferirci all’uso diretto del duale e delle condizioni complementari di slackness, cosı́ come usate per progettare un algoritmo di approssimazione per
Hitting Set . La differenza con il metodo che vedremo oggi consiste nel fatto che esso,
invece di risolvere innanzitutto il sistema duale e poi usare la sua soluzione e le condizioni
complementari di slackness per costruire una soluzione intera al primale, costruisce passo
passo una soluzione al duale, imponendo che la condizione complementare di slackness
duale sia sempre soddisfatta. Il metodo risulta essere maggiormente flessibile di quello
visto nella lezione scorsa, in quanto nella costruzione della soluzione del duale (visto che
la realizziamo noi) ci permette in linea teorica di poter conto di addizionali informazioni
che potremmo eventualmente avere sulla struttura del problema di ottimizzazione sotto
esame. Inotre risulta essere più veloce in pratica, in quanto non richiede la soluzione preliminare di un sistema di PL. Useremo la formulazione di PL del problema in questione
solo come una guida per il progetto e l’analisi dell’algoritmo. Il metodo generale (che va
sotto il nome di Primale-Duale) lo possiamo descrivere nella Figura 1 appresso presentata.
Per illustrare l’applicazione dello schema della Figura 1 ad algoritmi di approssimazione, applichiamo il metodo al primo problema di ottimizzazione visto in questo corso:
Vertex Cover . Ricordiamo il problema
66
ASDII:Parte B — Lezione 8
y
0, x
0
Esiste x soluzione
intera ammissibile per il
primale e slack. complementare
a y?
SI
Stop
Output x
NO
Incrementa y mantenendo
la sua ammissibilita’
Figure 1: Schema generale per Primale Duale
VERTEX COVER
• Input:
– grafo G = (V, E), V = {v1 , . . . , vn }, E = {e1 , . . . , em }
• Output: sottoinsieme S ⊆ V di minima cardinalità |S| tale che ∀(u, v) ∈ E valga
{u, v} ∩ S 6= ∅.
La sua formulazione via PL è :
minimizzare
X
xv
(101)
v∈V
soggetto a
x u + xv ≥ 1
xv ∈ {0, 1}
∀e = (u, v) ∈ E
∀u ∈ V .
(102)
(103)
Il corrispondente sistema rilassato è
minimizzare
X
v∈V
xv
(104)
67
ASDII:Parte B — Lezione 8
soggetto a
x u + xv ≥ 1
xv ∈ [0, 1]
∀e = (u, v) ∈ E
∀u ∈ V .
(105)
(106)
Per derivare il duale di (104) introduciamo una variabile duale y e per ogni vincolo (105),
ottenendo il sistema
X
massimizzare
ye
(107)
e∈E
X
soggetto a
e:v∈e
ye ≤ 1
ye ∈ [0, 1]
∀v ∈ V
(108)
∀e ∈ E.
(109)
É istruttivo, per un momento, osservare una particolare classe di soluzioni al sistema (107).
Sia M un matching del grafo G, ovvero una collezione di archi disgiunti di G. Definiamo
il vettore z = (ze1 , . . . , zem ) come
z ei =
1
0
se ei ∈ M ,
se ei ∈
/ M.
É semplice verificare che z è una soluzione ammissibile al sistema (107), in particolare che
soddisfa i vincoli (108), proprio perchè , corrispondendo z ad un matching in G, su ogni
vertice v ∈ V incide uno ed un solo arco di M . Pertanto, dal Teorema debole della dualità
abbiamo che
X
ze ≤ OP T,
|M | =
e∈E
dove OP T è il valore della soluzione ottima al sistema di PL (101), ovvero OP T è la
cardinalità di un Vertex Cover per G di cardinalità minima. Ritroviamo quindi, come
conseguenza del Teorema della dualità debole, la limitazione inferiore alla cardinalità del
VC di cardinalità minima derivato nella Lezione 1.
Vediamo ora come è possibile derivare un algoritmo di approssimazione per Vertex
Cover usando lo schema generale Primale-Duale illustrato nella Figura 1. Lo schema ci
suggerisce di partire con una soluzione y ammissibile per il duale (107), in cui tutte le
componenti sono poste a 0, ed una soluzione non ammissibile x al primale intero (101) in
cui tutte le componenti sono poste a 0. Se x non è ammissibile (e fin quando lo sarà ),
vuol dire che ∃e = (u, v) ∈ E tale che il relativo vincolo (102) non è soddisfatto, ovvero
vale
xu + xv = 0.
(110)
Allora, sempre in accordo allo schema generale Primale-Duale della Figura 1, incrementiamo la corrispondente variabile duale y e il più possibile, mantenendo il rispetto dei vincoli
del duale, ed in modo tale che il vincolo (108) sia soddisfatto con l’uguaglianza. In altri
termini, nel nostro caso specifico del Vertex Cover , stiamo ponendo y e = 1. Poichè lo
68
ASDII:Parte B — Lezione 8
schema generale Primale-Duale ci impone che le condizioni di slackness primali debbano
essere sempre soddisfatte, dobbiamo porre anche
xu = xv = 1,
in quanto adesso i vincoli duali
X
e:v∈e
ye ≤ 1
sono soddisfatti con l’uguaglianza sia per il vertice u che per il vertice v. In altre parole,
stiamo mettendo sia il vertice u che il vertice v nella costruenda soluzione S per il problema
del Vertex Cover .
Iteriamo il procedimento: ogni qualvolta troviamo un vincolo nel primale intero che
il vettore x non soddisfa ancora (ovvero, troviamo un qualche e = (u, v) ∈ E per cui
xu + xv = 0), poniamo ye = 1 nella soluzione del duale, imponendo poi che le condizioni
di slackness primali siano soddisfatte per x, cioè incrementiamo le componenti x u e xv a
1.
Prima o poi otterremo una soluzione intera x che soddisfa tutti i vincoli del primale
(101), con x che soddisfa anche
X
xv =
v∈V
=
X
X
v∈V
e:v∈e
X
X
e∈E
≤ 2
X
v∈e
!
ye xv
!
xv ye
(111)
(112)
ye
(113)
≤ 2OP TR ≤ 2OP T
(114)
e∈E
P
dove la uguaglianza (111) vale in quanto e:v∈e ye = 1 per costruzione, per tutti i vertici
v per cui xv = 1; la uguaglianza (112) la si ottiene riorganizzando la somma; la diseguaglianza (113) vale in quanto il vettore x è ammissibile per il primale e quindi soddisfa
tutti i vincoli di (101).
Abbiamo quindi (ri)ottenuto un algoritmo di approssimazione con fattore 2 per il
problema del Vertex Cover . Una analisi attenta di esso ci informa che l’algoritmo
appena ottenuto, mediante il metodo Primale-Duale, è perfettamente analogo a quello
presentato nella Lezione 1. Infatti, nell’algoritmo appena derivato noi inseriamo nella
soluzione S, ad ogni passo dell’algoritmo, entrambi i vertici u e v corrispondenti ad un
arco e = (u, v) per cui abbiamo posto y e = 1 nel duale. D’altra parte, il vettore y è sempre
costretto ad essere una soluzione ammissibile a (107), ovvero deve rispettare i vincoli
X
e:v∈e
ye ≤ 1
∀v ∈ V.
69
ASDII:Parte B — Lezione 8
Questo implica che ∀v ∈ V uno solo degli y e nella somma di sopra può assumere valore 1,
cioè uno solo degli ye corrispondenti ad archi e incidenti su v può essere posto ad 1. Di
conseguenza, l’insieme A = {e : ye = 1} è una collezione di archi disgiunti (un matching),
per ogni iterazione dell’algoritmo. Al completamento dell’algoritmo l’insieme A è ovviamente un matching massimale, mentre la soluzione S prodotta dall’algoritmo corrisponde
evidentemente all’insieme dei vertici incidenti sugli archi del matching massimale A.
Applichiamo ora la tecnica Primale-Duale al problema del Set Cover . Ricordiamo
la sua formulazione
SET COVER
• Input:
– insieme U = {e1 , . . . , en }, famiglia S = {S1 , . . . , Sk }, con Si ⊆ U , per i = 1, . . . , k;
– funzione costo c : S ∈ S → c(S) ∈ R+ ;
• Output: sottofamiglia S 0 ⊆ S di costo c(S 0 ) =
S
S∈S 0 S ⊇ U .
P
S∈S 0
c(S) minimo tale che
La sua formulazione via PL è
X
minimizzare
x(S)c(S)
(115)
S∈S
X
soggetto a
S:e∈S
x(S) ≥ 1
x(S) ∈ {0, 1}
∀e ∈ U
(116)
∀S ∈ S.
(117)
La versione rilassata del sistema di PL è
minimizzare
X
x(S)c(S)
(118)
S∈S
soggetto a
X
S:e∈S
x(S) ≥ 1
x(S) ≥ 0
∀e ∈ U
∀S ∈ S.
(119)
(120)
Introducendo una variabile ye per ogni vincolo del problema otteniamo il programma duale
massimizzare
X
ye
(121)
e∈U
soggetto a
X
e:e∈S
ye ≤ c(S)
ye ≥ 0
∀e ∈ U .
∀S ∈ S
(122)
(123)
70
ASDII:Parte B — Lezione 8
Il seguente algoritmo implementa la strategia Primale-Duale per il problema del Set
Cover .
Algoritmo Primale-Duale per SET COVER
I←∅
ye ← 0
∀e ∈ U
While ∃e ∈ U tale che e ∈
/
S
j∈I
Sj do
Sia ` l’indice per il quale il minh:e∈Sh {c(Sh ) −
` ← c(S` ) −
ye ← y e + `
P
a∈S`
ya
P
a∈Sh
ya } è raggiunto
I ← I ∪ {`}
Return I
Lemma 6 L’algoritmo Primale-Duale ritorna un set cover.
Proof. Ovvio, in quanto la condizione di terminazione per il ciclo While è proprio che
si sia raggiunto un set cover.
Lemma 7 L’algoritmo Primale-Duale costruisce una soluzione y ammissibile per il duale.
Proof. Procediamo per induzione sui cicli dell’algoritmo. Il caso base è ovvio in quanto
all’inizio vale ovviamente
X
a∈Sh
ye = 0 ≤ c(Sh ),
∀h = 1, . . . m.
Per il passo induttivo, assumiamo induttivamente che entrando in una iterazione del ciclo
While avevamo
X
ye ≤ c(Sh ), ∀h = 1, . . . m.
(124)
a∈Sh
La sola variabile duale il cui valore è incrementato dall’esecuzione del ciclo While è la
S
variabile ye (dove e è un elemento per cui e ∈
/ j∈I Sj ), pertanto tutte le diseguaglianze
(124) per gli insiemi Sh tali che e ∈
/ Sh rimangono inalterate e quindi continuano a valere.
Se invece e ∈ Sh , allora per la nostra scelta dell’indice ` abbiamo che
X
ya + ` =
X
a∈Sh
a∈Sh
≤
X
a∈Sh
ya + (c(S` ) −
ya + (c(Sh ) −
= c(Sh )
X
ya )
a∈S`
X
a∈Sh
ya )
71
ASDII:Parte B — Lezione 8
Pertanto, i vincoli del duale sono soddisfatti anche dopo l’esecuzione del ciclo While, e
quindi, per il principio di induzione, anche alla terminazione dell’algoritmo.
Lemma 8 Se j ∈ I allora
P
e∈Sj
ye = c(Sj ).
Proof. Se in un qualche passo abbiamo aggiunto j a I, allora per come opera l’algoritmo,
P
abbiamo anche aumentato ye , per qualche e ∈ Sj , tale da rendere il vincolo e∈Sj ye ≤
c(Sj ) soddisfatto con l’uguaglianza.
Teorema 8 L’algoritmo Primale-Duale per Set Cover è un algoritmo di approssimazione con fattore di approssimazione f , dove f = max e∈U f (e), e f (e) = numero di
insiemi S ∈ S che contengono e.
Proof. Abbiamo
SOL =
X
c(Sj )
j∈I
=
XX
ye
j∈I e∈Sj
=
X
e∈U
≤ f·
ye |{j : e ∈ Sj }
X
ye
e∈U
≤ f · OP T
dove la prima uguaglianza è per definizione, la seconda a causa del Lemma 8, la terza
in quanto la variabile ye compare nella somma tante volte quanti sono gli insiemi S j che
contengono il corrispondente e, la quarta per definizione di f , e l’ultima per il Teorema
della dualità debole.
Concludiamo la lezione affermando che la tecnica Primale-Duale sembra essere a
tutt’oggi la metodologia più efficace per il progetto di algoritmi di approssimazione. Si è
visto in tempi recenti che vari algoritmi derivati negli anni scorsi mediante procedimenti
ad hoc, sono in realtà interpretabili come esempi di algoritmi Primale-Duale. Inoltre, in
molti casi, la tecnica Primale-Duale ha prodotto algoritmi di approssimazione che risultano
essere, a tutt’oggi, i migliori sia in termini di fattore di approssimazione, che in termini di
complessità computazionale.
72
ASDII:Parte B — Lezione 9
Algoritmi e Strutture Dati II: Parte B
Anno Accademico 2004-2005
Lezione 9
Docente: Ugo Vaccaro
Finora abbiamo studiato algoritmi di approssimazione per problemi computazionalmente difficili, (ovvero problemi di ottimizzazione la cui la versione decisionale è NPHARD). Per questi problemi ad ogni possibile istanza di input è associato un insieme di
possibili soluzioni e la difficoltà consiste nel trovare per ogni istanza di input una soluzione
che abbia costo minimo, se il problema è un problema di minimizzazione, o profitto massimo (se il problema è un problema di massimizzazione). Per tali problemi di ottimizzazione abbiamo studiato varie tecniche per produrre soluzioni che non si discostassero
molto da quella ottima. Tutta la teoria finora sviluppata si basa su una assunzione cruciale, e cioè che l’input al problema sia interamente noto prima che l’algoritmo inizi le
sua computazioni. Tuttavia, in pratica non è sempre cosı́, ovvero in molte situazioni di
interesse pratico l’ input al problema ci viene data sequenzialmente, poco per volta, ed
ogni volta che riceviamo una nuova parte dell’input, ci viene anche richiesto di produrre
una parte dell’ output, senza ovviamente conoscere il resto della sequenza input.
Il primo esempio di problemi siffatti che studieremo è il problema della gestione efficiente della paginazione della memoria cache. Ricordiamo che i calcolatori sono dotati di
memorie (secondaria, cache, RAM) aventi caratteristiche diverse, sia per quanto riguarda
la dimensione che i tempi di accesso. Inoltre, dal punto di vista concettuale i diversi tipi di
memoria possono essere organizzati in una gerarchia realizzata in base al principio di localizzazione temporale e spaziale: le pagine (ovvero unità di memoria a dimensione fissata)
usate più spesso e quelle che si trovano in locazioni di memoria vicine, vengono caricate
nella memoria più veloce (la cache) in modo da garantirne un recupero immediato al momento in cui vengono richieste. In particolare, quando la CPU richiede un dato, inizia la
sua ricerca dalla memoria cache (la più veloce ma anche la più piccola). Nel caso di esito
negativo della ricerca (ciò avviene quando il dato non è recuperato in quanto la pagina che
lo contiene non è presente nella cache), la ricerca continua alla memoria RAM, la quale si
occupa di inviare alla cache la pagina contenente il dato richiesto. Una richiesta di accesso
ad un dato non presente in alcuna pagina nella cache (ed il conseguente spostamento della
pagina che contiene il dato dalla RAM alla cache) viene denominato page fault. La gestione di un page fault è generalmente onerosa, in quanto richiede la esecuzione di svariate
operazioni ausiliarie, e sarebbe quindi opportuno avere politiche di gestione della memoria
che minimizzino il numero di page fault. Il problema della paginazione della cache entra in
gioco proprio nella gestione di page faults, in quanto se la cache è piena occorre eliminare
qualche pagina da essa per far posto alla nuova pagina, contenente il dato richiesto, che
occorre spostare dalla RAM alla cache. Sarebbe desiderabile avere un metodo che ci dica,
all’occorrere di page faults, quale pagina espellere dalla cache in modo tale che il numero
73
ASDII:Parte B — Lezione 9
totale dei page faults venga minimizzato.
Per vedere come ciò possa essere realizzato, occorre precisare ulteriormente i termini
del problema.
Abbiamo
• una memoria RAM contenente n pagine P 1 , . . . , Pn
• una memoria cache che può contenere ad ogni istante k pagine, dove si assume in
generale che k sia molto più piccolo di n
• una sequenza di richieste σ = σ(1)σ(2) . . . σ(m), dove ogni σ(i), per i = 1, . . . m,
rappresenta una richiesta ad una qualche pagina P ∈ {P 1 , . . . , Pn }
• un costo costo(σ(i)) per soddisfare la richiesta σ(i), definito come
costo(σ(i)) =
1
0
se la pagina richiesta ∈
/ gà alla cache
se la pagina richiesta ∈ già alla cache.
Il problema è progettare un algoritmo che decida, ad ogni page-fault, quale pagina espellere
dalla memoria cache, al fine di minimizzare il numero totale di page-fault della sequenza
delle pagine richieste. In altri temini, cerchiamo un un algoritmo che minimizzi il costo
costo(σ) =
m
X
costo(σ(i))
i=1
per ogni possibile sequenza di richieste σ.
Si presentano immediatamente due difficoltà di tipo pratico e concettuale:
1. Come progettare l’algoritmo?
Stiamo chiedendo di sviluppare un algoritmo che minimizzi il numero di page-fault
senza però conoscere quali sono le richieste future alla richiesta che ha creato il
fault. Noi non conosciamo la parte dell’input dell’algoritmo successiva alla richiesta
corrente di pagina, quindi in base a quale criterio l’algoritmo dovrà sceglierà la
pagina da espellere? Ciò che in altri termini stiamo dicendo, è che in generale
sembra difficile progettare algoritmi che operino bene sul futuro senza conoscerlo.
2. Come misurare la “bontà” dell’algoritmo, ovvero il costo in cui incorre?
Finora abbiamo sempre analizzato la complessità computazionale degli algoritmi
sulla base del loro comportamento nel caso peggiore. Ma nella situazione che stiamo
ora studiando ciò non ha ovviamente senso. Infatti, tutti gli algoritmi, anche il
migliore, nel caso peggiore andranno malissimo. Supponiamo infatti di avere un
generico algoritmo A e supponiamo che in corrispondenza di un dato page fault
l’algoritmo A decida di espellere la pagina P dalla cache. Nel caso peggiore potrebbe
accadere che la stessa pagina P venga immediatamente richiesta, creando quindi un
nuovo page fault. Allora l’algoritmo A per poter portare di nuovo P nella memoria
74
ASDII:Parte B — Lezione 9
cache dovrà espellere un’altra pagina, sia essa P 0 , che potrebbe essere ovviamente
la nuova pagina richiesta all’istante successivo, creando un nuovo page fault, e cosı̀
via. In altri termini, qualunque sia il comportamento dell’algoritmo A, è sempre
vero che nel caso peggiore esiste una sequenza σ di m richieste a pagine che possono
costringere A ad effettuare m page fault. Il succo di queste considerazioni è che se
adottassimo il comportamento dell’algoritmo nel caso peggiore come misura della
complessità dell’algoritmo, allora tutti gli algoritmi avrebbero la stessa complessità
.
É chiaro che quanto appena detto (ovvero valutare gli algoritmi in questo scenario secondo
il loro comportamento nel caso peggiore) non ha alcun senso.
In pratica, inoltre, si vede che gli algoritmi di paging realmente usati dai sistemi
operativi innanzitutto non incorrono in numeri elevati di page fault, ed inoltre differenti
algoritmi tendono ad esibire diverse compessità . Possiamo quindi dire che nel caso di
algoritmi che devono operare ed effettuare scelte sotto la ipotesi di conoscenza parziale
dell’input e di ignoranza dell’input futuro che si possa presentare, è errato utilizzare il loro
comportamento nel caso peggiore come misura della loro efficienza.
L’aspetto realmente nuovo di questo scenario risiede nel fatto che l’input all’algoritmo
viene fornito passo-passo, in maniera sequenziale. Pertanto, sarebbe sensato valutare
quanto si perde, ovvero quanto costa, passare dalla conoscenza totale dell’input a quella
parziale o sequenziale. Introduziamo quindi un nuovo strumento per l’analisi degli algoritmi, chiamata
Analisi Competitiva di Algoritmi.
Un algoritmo on-line è un algoritmo A cui viene presentata una sequenza
σ = σ(1)σ(2). . . σ(m)
di richieste, e l’algoritmo effettua una decisione in corrispondenza di ogni richiesta σ(i),
basandosi solo sulla conoscenza delle richieste note finora σ(j), j ≤ i.
Sia costA (σ) il costo dell’algoritmo on-line A sulla sequenza σ (il costo dipenderà dalla
particolare applicazione sotto esame, ad esempio nel caso del paging sarà il numero totale di
page-fault cui l’algoritmo A incorre quando la sequenza σ di richieste a pagine di memoria
gli viene data in input). L’algoritmo on-line A è detto un algoritmo c-competitivo se
esistono delle costanti c ed a tali che per ogni sequenza input σ vale che:
costA (σ) ≤ c × costOP T (σ) + a
dove costOP T (σ) è il costo del migliore algoritmo off-line, ovvero del miglior algoritmo che
opera conoscendo tutta la sequenza input σ fin dall’inizio.
La definizione appena data misura correttamente quanto costa “l’ ignoranza” del futuro, ovvero il passaggio da algoritmi off-line ad algoritmi on-line. Quindi se A è un
75
ASDII:Parte B — Lezione 9
algoritmo on-line c-competitivo, allora esso, su ogni sequenza input, non costerà mai più
di c volte l’algoritmo ottimo off-line che conosce fin dall’inizio tutta la sequenza (che
costituisce ovviamente la situazione ideale ma irrealistica in pratica).
Torniamo al problema del paging. Il problema centrale dell’algoritmo è decidere ad
ogni istante quale pagina eventualmente espellere dalla memoria cache, senza conoscere le
richieste future. Vari algoritmi sono stati proposti per risolvere il problema, tra i più noti
ed usati nella pratica vi sono i tre seguenti:
• LRU (Least Recently Used): ad ogni page-fault espelli dalla cache la pagina la
cui ultima richiesta è più antica;
• FIFO (First In First Out): ad ogni page-fault espelli la pagina che è stata posta,
nella memoria cache, per prima tra le k che adesso vi risiedono;
• LIFO (Last In First Out): ad ogni page-fault espelli dalla cache la pagina che vi
è stata posta per ultima tra le k che adesso vi risiedono.
Vale il seguente risultato.
Teorema 9 L’algoritmo LRU è k-competitivo, ovvero vale che
costLRU (σ) ≤ k × costOP T (σ),
per ogni possibile sequenza di richieste σ.
Tale teorema è valido anche per l’algoritmo FIFO. Per questi due algoritmi è dunque
dimostrabile che i rapporti costLRU (σ)/costOP T (σ) e costFIFO (σ)/costOP T (σ) non divergono, ma sono limitati dal valore k (la dimensione della memoria cache), qualunque sia la
sequenza di richieste σ, e comunque essa sia lunga.
È inoltre dimostrabile, invece, che l’algoritmo LIFO non è c-competitivo, per qualsiasi
valore del parametro c. In altri termini, l’algoritmo LIFO, su particolari sequenze di
richieste σ, può produrre un numero di page-fault che non è limitato superiormente dal
numero di page-fault dell’algoritmo ottimo moltiplicato per c, qualunque sia il valore c.
Ovvero, il rapporto costLIFO (σ)/costOP T (σ), per particolari sequenze di richieste σ, cresce
indefinitamente, al crescere della lunghezza di σ.
Passiamo ora alla dimostrazione del teorema.
Sia σ = σ(1)σ(2) . . . σ(m) la sequenza di richieste di input. Partizioniamo la sequenza
di richieste σ in fasi
F (0), F (1), . . . , F (i), . . .
La definizione di fase è la seguente. Ogni fase F (i) è composta da una sottosequenza di
richieste consecutive di σ su cui l’algoritmo LRU ha esattamente k page-fault, e ciò è vero
∀i ≥ 1, mentre nella fase F (0) l’algoritmo LRU ha al più k page-fault. La suddivisione
76
ASDII:Parte B — Lezione 9
in fasi è effettuabile nel seguente modo: esaminiamo la sequenza σ dalla fine e, in base
alle richieste, contiamo il numero di page-fault in cui LRU è incorso. Appena ne abbiamo
esattamente k allora raggruppiamo le richieste in una fase, e continuiamo in questo modo
con ulteriori raggruppamenti. Alla fine otterremo una ultima fase, F (0), corrispondente
ad un numero di page fault ≤ k, (infatti se ciò non fosse potremmo dividere le richieste
in due fasi diverse).
Mostriamo innanzitutto la seguente asserzione circa il comportamento dell’algoritmo offline ottimo (qualunque esso sia) sulle fasi sopra definite.
In ogni fase F (i), i ≥ 0, l’algoritmo ottimo ha almeno un page-fault.
(125)
Prima di provare la asserzione (125) esaminiamone una sua immediata ed importante
conseguenza. Calcoliamo costLRU (σ). Avremo
costLRU (σ) =
numero di page fault che LRU commette su σ
≤ k × numero di fasi
= k × (s + 1)
D’altra parte la (125) implica che l’algoritmo ottimo ha almeno s+1 faults in totale quando
gli viene sottoposta la sequenza di richieste σ (infatti l’algoritmo ottimo avrà almeno un
page fault per ognuna delle (s + 1) fasi F (i)), pertanto
costOP T (σ) ≥ (s + 1).
Mettendo insieme le due diseguaglianze di sopra otteniamo:
costLRU (σ) ≤ k × (s + 1)
≤ k × costOP T (σ),
che è esattamente l’enunciato del Teorema che vogliamo dimostrare. Ci resta quindi da
provare la (125).
A tal fine supporremo, senza perdita di generalità che sia LRU che l’algoritmo offline ottimo partano con lo stesso contenuto nella memoria cache. Quindi appena una
richiesta in σ provocherà il primo page fault per LRU (ed ovviamente ciò avverrà in
corrispondenza ad una richiesta nella fase F (0)), tale richiesta provocherà anche il primo
page fault per l’algoritmo off-line ottimo. Ci resta quindi da provare la (125) solo per
i ≥ 1. Più precisamente, proveremo che in corrispondenza alle richieste della fase F (i),
un qualunque algoritmo, e quindi in particolare anche quello ottimo, avrà almeno un page
fault.
Consideriamo la generica fase F (i), i ≥ 1. Essa corrisponderà alle richieste che vanno
da σ(ti ) a σ(ti+1 − 1), per qualche coppia di indici ti e ti+1 . Sia P l’ultima pagina richiesta
nella fase F (i − 1), quindi la pagina P sicuramente risiede nella memoria cache all’inizio
della fase F (i). Proviamo che:
77
ASDII:Parte B — Lezione 9
F (i) contiene almeno k richieste a pagine distinte e diverse da P
(126)
Di nuovo, prima di provare la (126) discutiamo una sua immediata conseguenza. Se
vale la (126), allora un qualunque algoritmo avrà necessariamente un page fault nella
fase F (i). Infatti, all’inzio della fase F (i) la pagina P è presente nella cache, e quindi le
altre k pagine distinte e diverse da P che sono richieste in F (i) non potranno essere tutte
presenti, almeno una di esse non lo sarà , e quindi almeno un page fault avverrà appena
la corrispondente richiesta a tale pagina si presenta nella fase F (i). Pertanto, la (126)
implica la (125), e conseguentemente il Teorema.
Dimostriamo quindi la (126) al fine di completare la dimostrazione del Teorema.
Ricordiamo che nella fase F (i) sono avvenute k page-fault per l’algoritmo LRU. Ciò
vuol dire che ci sono state k richieste a pagine che non risiedevano nella memoria cache
di LRU. Se tali pagine richieste sono tutte distinte tra loro e tutte diverse da P , allora la
(126) è ovviamente dimostrata. Altrimenti si può verificare uno dei seguenti due casi.
1. Le k richieste che hanno provocato i k page fault di LRU nella fase F (i) sono tutte
diverse da P ma non sono tutte distinte tra loro. In altri termini, l’algoritmo LRU
tra i suoi k page-fault ne ha due su una stessa pagina Q.
Ragioniamo allora nel seguente modo. Nella sequenza di richieste in F (i) ad un
certo punto ci sarà il primo page-fault sulla pagina Q, e quindi la pagina Q verrà
portata nella memoria cache. Dopodiché avremo il secondo page-fault sulla stessa
pagina Q . Ciò vuol evidentemente dire che la pagina Q era stata precedentemente
espulsa dalla memoria cache. Come era potuto accadere ciò ? Era ovviamente accaduto che ad un certo momento tra tutte le k pagine residenti nella memoria cache,
la pagina Q era diventata la pagina acceduta più lontanamente (ciò perché stiamo
usando applicato l’algoritmo LRU). Detto in altre parole, ciò che è accaduto è che
dopo aver portato Q in memoria cache per la prima volta in conseguenza di una
richiesta alla pagina Q, vi sono state k − 1 richieste a pagine distinte da Q e tra di
loro (che hanno fatto diventare Q la pagina acceduta più lontanamente nel passato
tra tutte le k residenti nella cache) poi vi è stata l’espulsione di Q in conseguenza di
un’altra richiesta distinta ad una pagina non risiedente in memoria. In totale, dal
momento dell’inserimento di Q per la prima volta in memoria cache fino alla sua
espulsione vi sono state almeno 1 + (k − 1) + 1 = k + 1 richieste a pagine distinte.
Tra tali k + 1 richieste a pagine tutte distinte tra di loro, una sola può essere per
la pagina P , ovvero ve ne sono sicuramente almeno k distinte tra di loro e distinte
dalla pagina P , provando la (126).
2. Le k richieste che hanno provocato le k page fault di LRU nella fase F (i) sono
tutte distinte tra di loro ma non sono tutte diverse da P. In altri termini, l’algoritmo
LRU nella fase F (i) non fallisce mai due volte sulla stessa pagina, ma tra i suoi
k page fault è incluso anche uno causato da una richiesta alla pagina P.
78
ASDII:Parte B — Lezione 9
Il ragionamento è analogo a prima. Abbiamo che all’inzio della fase F (i) la pagina P è in memoria cache, per ipotesi. Poi vi è un page fault causato da una
richiesta alla pagina P . Ciò vuol evidentemente dire che P era stata nel frattempo
espulsa dalla cache. Ovvero, in accordo alla regola LRU, era accaduto che dopo
l’inserimento di P nella memoria cache alla fine della fase F (i − 1), vi erano state
k − 1 richieste a pagine distinte da P e tra di loro (che hanno fatto diventare P la
pagina acceduta più lontanamente nel passato tra tutte le k residenti nella cache)
poi vi è stata l’espulsione di P in conseguenza di un’altra richiesta distinta da P ad
una pagina non risiedente in memoria. La cosa importante è che la pagina P alla
fine della fase F (i − 1) è in memoria cache, e poi durante la fase F (i) vi sono state
almeno k richieste a pagine non risiedenti in memoria e distinte da P , provando la
(126) in tutti i possibili casi.
Si può dimostrare che anche l’algoritmo FIFO è k-competitivo, procedendo in maniera
perfettamente analoga a quanto fatto nel teorema appena dimostrato. Un altro risultato
interessante è il seguente, che mostra la ottimalità di LRU e FIFO tra gli algoritmi
competitivi per il problema del paging.
Teorema 10 Non esistono algoritmi per il problema del paging che siano k 0 -competitivi,
per un qualsiasi k 0 < k.
Proof. Definiamo il seguente algoritmo off-line per il problema del paging:
• LFD: ad ogni page-fault espelli la pagina la cui prossima richiesta è più lontana nel
futuro.
In particolare, si può dimostrare che questo è il miglior algoritmo off-line, ma ciò non è
necessario ai fini della dimostrazione del teorema. Proviamo invece che per ogni sequenza
σ di richieste a pagine nell’insieme {P 1 , . . . , Pk , Pk+1 } vale che:
costLFD (σ) ≤
|σ|
k
(127)
dove |σ| è il numero di richieste nella sequenza σ.
Dimostriamo la (127). A tal fine, notiamo innanzitutto che ogni richiesta σ(i) si
riferisce o ad una pagina già nella memoria cache o all’unica pagina correntemente al di
fuori della memoria cache. Ciò a causa della ipotesi che σ è una sequenza di richieste
a pagine nell’insieme {P1 , . . . , Pk , Pk+1 }. Supponiamo ora che ad un certo istante, in
corrispondenza alla richiesta σ(i), l’algoritmo LFD espella P . Ricordiamo che l’algoritmo
LFD espelle sempre la pagina che verrà richiesta più lontanamente nel tempo futuro, tra
tutte le pagine attualmente risiedenti nella memoria cache. Quindi, se P viene espulsa da
LFD in conseguenza della richiesta σ(i), ciò implica che vi saranno, dopo la richiesta σ(i),
79
ASDII:Parte B — Lezione 9
necessariamente almeno k − 1 successive richieste alle altre pagine diverse da P , che per
ipotesi stanno già in memoria, e quindi non creano alcun page fault. In altri termini, per
ogni k richieste consecutive della sequenza σ, possiamo avere al più un page fault. Ovvero,
il numero di page fault di LFD è ≤
|σ|
k
e quindi la (127) è dimostrata. Possiamo ora completare la dimostrazione del teorema.
Supponiamo di avere una generica sequenza σ = σ(1)σ(2) . . . σ(n) e consideriamo un
arbitrario algoritmo on-line ALG. Specificheremo le σ(i) passo passo, a seconda delle decisioni su quali pagine da espellere dalla cache abbia preso l’algoritmo ALG. Supponiamo
quindi che in corrispondenza ad una richiesta σ(i) alla pagina P k+1 , l’algoritmo ALG
abbia deciso di di espellere dalla cache la pagina P i . Assumiamo che σ(i + 1) corrisponda
ad una richiesta alla pagine appena espulsa P i . Vi sarà ovviamente un page fault, con
conseguente espulsione di una qualche pagina P j . Possiamo allora assumere che σ(i + 2)
corrisponda ad una richiesta a Pj , e cosı̀ via, nel senso che in σ ogni successiva richiesta
ad un page fault si riferisce alla pagina precedentemente espulsa. Su una tale sequenza di
richieste σ l’algoritmo ALG avrà n − k page fault (per le prime k richieste non ci sono
page-fault) e quindi
costALG (σ) = n − k.
Se per assurdo l’algoritmo on line ALG fosse k 0 -competitivo, per qualche valore k 0 < k,
allora avremmo
costALG (σ) ≤ k 0 × costOP T (σ) + a
(128)
per qualche costante a e per ogni sequenza di richieste σ. D’altra parte, abbiamo appena
mostrato che per una particolare sequenza di richieste σ di lunghezza n vale che
costALG (σ) = n − k
mentre per l’algoritmo LFD, in virtù della (127), per la stessa sequenza σ vale che
costLFD (σ) ≤
n
.
k
La diseguaglianza di sopra implica anche che
costOP T (σ) ≤ costLFD (σ) ≤
n
.
k
Pertanto, se fosse vera la (128) avremmo anche
n − k = costALG
≤ k 0 × costOP T (σ) + a
n
0
≤ k ×
+a
k
80
ASDII:Parte B — Lezione 9
il che implicherebbe la diseguaglianza
n−k ≤
k0 × n
+ a.
k
Ciò è ovviamente assurdo per valori di n sufficientemente grandi, visto che il rapporto k 0 /k
è strettamente minore di 1. Possiamo quindi rigettare la ipotesi assurda, e concludere che
non esistono algoritmi k 0 -competitivi per il problema del paging, qualunque sia il valore
k 0 < k.
81
ASDII:Parte B — Lezione 10
Algoritmi e Strutture Dati II: Parte B
Anno Accademico 2004-2005
Lezione 10
Docente: Ugo Vaccaro
In questa lezione continueremo a studiare algoritmi on-line. Considereremo il seguente
problema
Aggiornamento di Liste
Il problema dell’aggiornamento di liste consiste nel mantenere una struttura dati
dizionario mediante una lista non ordinata. Dato un insieme di elementi rappresentati
da una lista lineare, assumiamo di ricevere una sequenza σ = σ(1) . . . σ(m) di richieste,
dove ogni richiesta σ(i) è una delle tre possibili operazioni
1. accesso ad un elemento della lista,
2. inserzione di un nuovo elemento nella lista,
3. cancellazione di un elemento dalla lista.
Stimiamo i costi di ciascuna delle tre operazioni sopra descritte.
• Per accedere ad un elemento, un algoritmo generico inizia la ricerca dalla testa della
lista e procede sequenzialmente fin quando l’elemento desiderato non viene trovato.
Pertanto, se l’elemento da accedere si trova nella posizione i all’interno della lista,
possiamo assumere che il costo per servire una richiesta di accesso è pari a i.
• Per inserire un nuovo elemento nella lista, l’algoritmo prima scandisce l’intera lista
per verificare che l’elemento non è già presente, e quindi inserice l’elemento in questione alla fine della lista. Pertanto, se il numero di elementi nella lista prima
dell’inserzione è pari ad n, possiamo assumere che il costo per servire una richiesta di inserimento è pari a n + 1.
• Per cancellare un elemento dalla lista l’algoritmo prima effettua una ricerca per
trovare l’elemento, poi lo cancella. Pertanto, se l’elemento da cancellare si trova
nella posizione i all’interno della lista, possiamo assumere che il costo per servire
una richiesta di cancellazione è pari a i. 6
6
Tecnicamente dovremmo dire che il costo per cercare, inserire, e cancellare è O(i), O(n) e O(i), rispettivamente, in quanto ogni operazione richiede anche un certo numero costante di operazioni ausiliarie
(ad es., aggiornamento di puntatori della lista, etc.). Per semplicità , ignoreremo questi dettagli che non
modificano la reale natura del problema.
ASDII:Parte B — Lezione 10
82
Mentre l’algoritmo effettua le operazioni necessarie per soddisfare una richiesta, esso può
anche scambiare di posizione gli elementi all’interno della lista, al fine di ridurre il costo per
soddisfare eventuali richieste future. Ad esempio, immediatemente dopo aver eseguito una
operazione di accesso o di inserzione di un elemento, l’algoritmo potrebbe voler spostare
lo stesso elemento in una nuova posizione più vicina alla testa della lista. Assumiamo che
questa operazione non ci costi nulla, e chiameremo questo tipo di operazioni scambi gratis.
Usando scambi gratis l’algoritmo può in linea di principio ridurre il costo per soddisfare
successive richieste. Il motivo per cui assumiamo che gli scambi gratis non costino nulla
all’algoritmo risiede nel fatto che il “grosso” del lavoro è stato già effettuato con l’accesso
o l’inserimento dell’elemento, e quindi il suo eventuale spostamento verso la testa della
lista non rappresenta un onere ulteriore significativo. Un altro tipo di modifica all’ordine
degli elementi della lista può essere effettuato mediante scambi di due specifici elementi
adiacenti nella lista. Il costo di ciascuno di questi scambi è posto ad 1, e chiameremo
questo tipo di operazioni scambi pagati.
Il nostro obiettivo è quello di progettare un algoritmo che, in corrispondenza ad
una sequenza di richieste σ = σ(1) . . . σ(m), il cui costo individuale di servizio è stato
prima definito, riorganizzi la lista in modo da minimizzare il costo totale costo(σ) =
Pm
i=0 costo(σ(i)). L’idea intuitiva è di riorganizzare la lista spostando gli elementi più
frequentemente acceduti verso la testa della lista. Anche in questo caso una analisi
dell’algoritmo basata sul suo comportamento nel caso peggiore non ha senso, in quanto è
sempre possibile avere una sequenza di richieste σ costantemente all’ultimo elemento della
lista. Su tali sequenze, ogni algoritmo avrebbe costo pari a m volte la lunghezza della
lista. Di nuovo, quindi, ha senso effettuare l’analisi del comportamento di algoritmi per il
problema dell’aggiornamento delle lista mediante il concetto di competitività .
Tra gli algoritmi per l’aggiornamento di liste, probabilmente i più noti sono i seguenti
• Move-to-Front (MTF): Muovi l’elemento richiesto alla testa della lista.
• Transpose: Scambia l’elemento richiesto con quello immediatamente precedente
nella lista.
• Frequency-Count: Mantieni un contatore delle frequenza di accesso per ciascun
elemento della lista. Ogni qualvolta un elemento è acceduto incrementa il suo contatore di 1. Mantieni la lista in modo tale che gli elementi appaiono sempre nell’ordine
di frequenze di accesso non crescente.
Proveremo il seguente risultato.
Teorema 11 L’algoritmo MTF è 2-competitivo, ovvero per ogni sequenza di richieste σ
vale che
costoMTF (σ) ≤ 2 · costoOP T (σ),
dove costoOP T (σ) è il costo dell’algoritmo ottimo off-line che conosce tutta la sequenza σ
fin dall’inizio.
83
ASDII:Parte B — Lezione 10
Proof. Consideriamo una arbitraria sequenza di richieste di accesso σ = σ(1) . . . σ(m).
Confronteremo il comportamento di MTF e dell’algoritmo ottimo (chiamiamolo OP T )
off line su ciascuna richiesta σ(i) della sequanza σ usando una funzione ausiliaria Φ che
definiamo di sotto.
Diremo che una coppia di elementi x e y formano una inversione se x occorre prima
di y nella lista di MTF, mentre x occorre dopo di y nella lista di OP T . Assumeremo che
gli algoritmi MTF e OPT partano con la stessa lista.
Per ogni valore di t, 1 ≤ t ≤ m, sia costo MTF (t) il costo in cui incorre l’algoritmo
MTF nel servire la richiesta σ(t), e sia analogamente costo OP T (t) il costo in cui incorre
l’algoritmo OPT nel servire la richiesta σ(t). Inoltre, sia Φ(t) il numero di inversioni che si
hanno dopo che la richiesta σ(t) è stata servita. In virtù del fatto che gli algoritmi MTF
e OPT partono con la stessa identica lista, possiamo senz’altro assumere per convenzione
che Φ(0) = 0. Mostreremo innanzitutto che
∀t
costoMTF (t) + Φ(t) − Φ(t − 1) ≤ 2costoOP T (t) − 1.
(129)
Prima di provare la validità della formula (129), esaminiamone una sua importante conseguenza. Abbiamo
costoMTF (σ) =
≤
m
X
t=1
m
X
t=1
costoMTF (t)
(130)
(2costoOP T (t) − 1 − Φ(t) + Φ(t − 1))
= 2costoOP T (σ) − m − Φ(m) + Φ(0)
≤ 2costoOP T (σ),
(dalla (129)) (131)
(132)
(133)
dove l’ultima diseguaglianza la si ottiene in quanto Φ(0) = 0 e Φ(m) ≥ 0. Pertanto, visto
che la (129) implica l’enunciato del Teorema, concentriamoci sulla prova della (129).
Supponiamo per il momento che σ(i) sia una richiesta di accesso ad un elemento k i
della lista. Sia ti la posizione dell’elemento ki nella lista di MTF prima che la richiesta
σ(t) venga esaminata, ed analogamente sia s i la posizione dell’elemento ki nella lista di
OTP prima che la richiesta σ(t) venga esaminata. Sia inoltre P i il numero di scambi
pagati che l’algoritmo OPT decide di usare nell’eseguire la richiesta σ(i) (si ricordi che
l’algoritmo MTF non usa scambi pagati). Abbiamo quindi
costoMTF (i) = ti
costoOP T (i) = si + Pi .
(134)
Per provare la (129) distinguiamo due casi.
• I Caso: ti > si .
Supponiamo per il momento che l’algoritmo OPT non esegua scambi pagati, ovvero
Pi = 0. Quindi solo l’elemento acceduto k i può cambiare posizione, e pertanto solo il
84
ASDII:Parte B — Lezione 10
numero di inversioni che coinvolgono k i può cambiare. Prima che ki cambi posizione,
vi sono almeno ti −si inversioni del tipo (x, ki ), ovvero ci sono almeno ti −si elementi
che sono posizionati prima di ki nella lista di MTF, ma che vengono dopo di k i nella
lista di OPT, e quindi il numero di inversioni che coivolgono l’elemento k i è almeno
pari a ti − si . Sia X il numero di inversioni rimanenti, ovvero il numero di inversioni
che non coinvolgono l’elemento ki . Abbiamo quindi che
Φ(t − 1) ≥ ti − si + X.
Per definizione, l’algoritmo MTF porta k i alla testa della lista, mentre OPT porterà
ki in una qualunque delle posizioni 1, 2, . . . , s i . Pertanto, dopo la esecuzione della
richiesta σ(i) ci saranno al più si − 1 inversioni che coinvolgono l’elemento k i , cioè
ci saranno al più si − 1 elementi che sono posizionati prima di k i nella lista di OPT
ma che vengono dopo ki nella lista di MTF. Il numero X di inversioni nella lista che
non coinvolgevano ki è ovviamente rimasto immutato, visto che abbiamo spostato
di posizione solo l’elemento ki (si veda la figura di sotto).
si
OPT
ki
b
t−s
i i
MTF
a
c
b
a
c
ti
ki
Quindi
Φ(t) ≤ si − 1 + X.
In altri termini, abbiamo osservato che
Φ(t) − Φ(t − 1) ≤ (si − 1) − (ti − si ).
Dalla (134), e ricordando che Pi = 0, otteniamo
costoMTF (t) + Φ(t) − Φ(t − 1) = ti + Φ(t) − Φ(t − 1)
≤ ti + (si − 1) − (ti − si )
= 2si − 1
= 2costoOP T (t) − 1
85
ASDII:Parte B — Lezione 10
e quindi la (129) è provata nel caso in cui P i = 0. Se invece Pi > 0, osserviamo che
ognuno dei Pi scambi pagati può far aumentare la differenza Φ(t) − Φ(t − 1) al più 1,
in quanto uno scambio pagato può creare una sola inversione che prima non c’era.
Pertanto, abbiamo che in generale vale
Φ(t) − Φ(t − 1) ≤ (si − 1) − (ti − si ) + Pi .
Dalla (134) otteniamo
costoMTF (t) + Φ(t) − Φ(t − 1) = ti + Φ(t) − Φ(t − 1)
≤ ti + (si − 1) − (ti − si ) + Pi
= 2si − 1 + Pi
≤ 2si + 2Pi − 1
= 2costoOP T (t) − 1
e quindi la (129) è provata in generale.
• II Caso: ti ≤ si .
Ragionando come prima possiamo concludere che:
1) prima di eseguire la richiesta σ(i) ci sono almeno s i − ti inversioni del tipo (x, ki )
(cioè Φ(t − 1) ≥ si − ti + X, dove X ha lo stesso significato di prima, ovvero conta
il numero di inversioni che non coinvolgono k i ),
2) dopo che MTF ha portato ki alla testa della lista e che OPT ha spostato k i in
qualche posizione compresa tra 1, . . . , s i , ci saranno al più si − 1 inversioni della
forma (x, ki ) (cioè Φ(t) ≤ si − 1 + X),
3) dopo che OPT ha eseguito i suoi Pi scambi pagati, possiamo avere al più altre
Pi inversioni. Di conseguenza
costoMTF (t) + Φ(t) − Φ(t − 1) = ti + Φ(t) − Φ(t − 1)
≤ ti + (si − 1) − (si − ti ) + Pi
= 2ti − 1 + Pi
≤ 2si + Pi − 1
≤ 2si + 2Pi − 1
= 2costoOP T (t) − 1.
e quindi la (129) è provata anche in questo caso.
Ci resta da considerare il caso in cui la richiesta σ(i) possa essere una richiesta di
inserzione o di cancellazione. Nel caso in cui σ(i) è una richiesta di inserzione, allora
abbiamo
costoMTF (t) = n + 1 e costoOP T (t) = n + 1,
dove n è la lunghezza della lista prima della inserzione. Inoltre, è semplice osservare che
nessuna nuova inversione viene creata dalla inserzione, pertanto
Φ(t) − Φ(t − 1) = 0.
86
ASDII:Parte B — Lezione 10
Le due ultime uguaglianze provano la (129) anche nel caso in cui la richiesta σ(i) è una
richiesta di inserzione.
La prova della (129) nel caso in cui la richiesta σ(i) sia di una cancellazione è molto
simile alla corrispondente prova nel caso in cui a richiesta σ(i) sia di accesso. Basta solo
osservare che la cancellazione di un elemento non crea nuove inversioni, ed elimina tutte
quelle che coinvolgevano l’elemento cancellato. Pertanto, se ad esempio la richiesta σ(i)
di cancellazione fosse relativa ad un elemento k i che si trova nella posizione ti della lista
di MTF e nella posizione si della lista di OPT, allora per quanto osservato prima, ci
sono almeno ti − si inversioni del tipo (x, ki ) che saranno tutte eliminate a causa della
cancellazione di ki , se ti > si , (ce ne saranno invece almeno si − ti eliminate se ti ≤ si ).
Pertanto, sotto l’ipotesi che Pi = 0, avremo che
Φ(t) − Φ(t − 1) ≤ −(ti − si ),
se ti > si ,
Φ(t) − Φ(t − 1) ≤ −(si − ti ),
se ti ≤ si ,
mentre
e procedendo come prima proveremmo la (129). Se P i > 0, entrambe le espressioni si
sopra possono crescere al più di Pi , ma anche il costo costoOP T (t) crescerebbe della stessa
quantità , quindi come prima possiamo dedurre che la (129) vale.
Avendo quindi provato la (129) in tutti i casi possibili, possiamo concludere che MTF
è 2-competitivo.
Possiamo provare che il fattore di competitività 2 non è essenzialmente migliorabile.
Consideriamo un generico algoritmo on-line A, e costruiamo una sequenza σ di m richieste
di accesso, ciascuna richiedente l’elemento posizionato alla coda della lista mantenuta da
A. É chiaro che il costo di un tale algoritmo su di una tale sequenza σ soddisfa la relazione
costoA (σ) = nm,
dove n è il numero di elementi della lista. In altre parole, abbiamo osservato che per ogni
algoritmo on-line A esiste una particolare sequenza di m richieste che lo forza ad avere un
costo di nm.
Consideriamo ora l’algoritmo B off-line che, conoscendo σ per intera fin dall’inizio si
calcola la frequenza di accesso di ciascun elemento, ovvero per ciascun elemento della lista
si calcola il numero di volte in cui esso verrebbe richiesto in σ. Ciò fatto, usando gli scambi
pagati, può ordinare la lista nell’ordine dettato dalle frequenze decrescenti (cioè mette
nella testa l’elemento maggiormente acceduto, e via via fin quando non mette nell’ultima
posizione l’elemento che verrebbe richiesto il minor numero di volte). Questa operazione
costa a B al più n(n − 1)/2 (per esempio, l’algoritmo B potrebbe usare BubbleSort per
ordinare la lista in accordo alle frequenze decrescenti). Avendo adesso la lista ordinata,
gli m accessi richiesti da σ costeranno all’algoritmo B una quantità pari a
n
X
i=1
ifi ,
87
ASDII:Parte B — Lezione 10
dove fi è il numero di volte che l’elemento posizionato nella posizione i della lista di B
deve essere acceduto. Poichè B ha disposto i suoi elementi nella lista in modo tale che
f1 ≥ f2 ≥ . . . ≥ fn , si può dimostrare che
n
X
i=1
ifi ≤
n
mX
m(n + 1)
.
i=
n i=1
2
Da ciò otteniamo che per ogni sequenza di richieste σ, per l’algoritmo OPT off-line vale
che
m(n + 1) n(n − 1)
costoOP T (σ) ≤ costoB (σ) ≤
+
,
2
2
ovvero
m(n + 1)
n(n − 1)
≤
.
costoOP T (σ) −
2
2
Di conseguenza, in corrispondenza alla particolare sequenza di richieste σ sopra definita,
vale che
costoA (σ) = mn
2n
m(n + 1)
·
=
2
(n + 1)
2n
n(n − 1)
≥
· costoOP T (σ) −
(n + 1)
2
2
n
· n(n − 1)
=
2−
costoOP T (σ) −
n+1
n+1
2
≥
2−
costoOP T (σ) − n(n − 1).
n+1
I termini in n sono negligibili rispetto a costo A (σ) e costoOP T (σ) (in quanto possiamo
considerare una sequenza di richieste σ molto lunga), inoltre se la lista contiene un numero
di elementi n sufficientemente grande anche il termine 2/(n + 1) diventa non significativo.
Pertanto, possiamo concludere che il fattore di competitività 2 raggiunto dall’algoritmo
MTF è essenzialmente ottimo.
Ci resterebbe da provare che
n
X
i=1
ifi ≤
n
mX
i,
n i=1
sotto l’iptesi che f1 ≥ f2 ≥ . . . ≥ fn . Se f1 = f2 = . . . = fn , allora necessariamente vale
che fi = m/n per ogni i = 1, . . . , n e quindi la formula è provata.
Supponiamo quindi che non tutti gli f i sono uguali tra di loro. Ad esempio, esistono
indici k e j, con k < j tali che fk > fj . Definiamo nuovi fi0 nel seguente modo
fi0 =

 fk − 1
f +1
 j
fi
se i = k
se i = j
altrimenti.
88
ASDII:Parte B — Lezione 10
Calcoliamo
n
X
i=1
ifi −
n
X
i=1
ifi0 = kfk + jfj − kfk0 − jfj0
= kfk + jfj − k(fk − 1) − j(fj + 1)
= k−j
< 0.
Pertanto,
n
X
i=1
ifi <
n
X
ifi0 .
i=1
La morale della storia di sopra è la seguente: se esistono f k e fj tali che fk 6= fj , allora
P
possiamo definire nuovi fi0 in cui la differenza tra fk0 e fj0 è “diminuita”, e la somma ni=1 ifi0
è aumentata. Possiamo effettuare questo procedimento, e quindi aumentare sempre la
somma, fin quando non ci saranno più differenze tra gli f i , ovvero fi = m/n, per ogni i.
In corrispondenza a tali valori di f i avremo raggiunto il valore massimo della somma. Il
che equivale a dire che
n
n
X
mX
i.
ifi ≤
n i=1
i=1
89
ASDII:Parte B — Lezione 11
Algoritmi e Strutture Dati II: Parte B
Anno Accademico 2004-2005
Lezione 11
Docente: Ugo Vaccaro
In questa lezione vedremo alcune applicazioni della tecnica greedy al progetto di algoritmi on-line. Vediamo il primo esempio
SCHEDULING DI JOB
• Input:
– m macchine M1 , . . . , Mm
– una sequenza σ di job σ = J1 , . . . , Jn , di durata p(J1 ), . . . , p(Jn ), rispettivamente.
• Output: un’assegnazione di job a macchine in modo tale che sia minimizzato il
tempo entro il quale tutti i job vengono eseguiti.
Più precisamente, assumiamo che i job costituenti la sequenza σ si presentino in input
all’algoritmo A in successione. In corrispondenza ad ogni job J s , s = 1, . . . , n, l’algoritmo
dovrà immediatamente decidere a quale delle macchine M k , k = 1, . . . , m, assegnarlo. Sia
Ski l’insieme dei job assegnati dall’algoritmo A alla macchina M k , in corrispondenza alla
sottosequenza J1 , . . . , Ji dei job. Il carico della generica macchina M k dopo che l’algoritmo
A ha processato la sottosequenza J1 , . . . , Ji dei job sarà quindi
Lik (A) =
X
p(J),
J∈Ski
ed il makespan dell’algoritmo A sarà
L(A) = max Lnk (A).
k=1,...,m
Cerchiamo un algoritmo on-line A che abbia L(A) minimo.
La tecnica greedy ci suggerisce immediatamente il seguente algoritmo:
Assegna il job Js , s = 1, . . . , n, alla macchina Mk per cui
s−1
Ls−1
= min{Ls−1
1 , . . . , Lm },
k
ovvero alla macchina correntemente con il minor carico.
90
ASDII:Parte B — Lezione 11
Vogliamo provare che
∀σ
costogreedy (σ) ≤ 2costoOP T (σ),
(135)
dove costogreedy (σ) è il makespan dell’algoritmo greedy in corrispondenza di σ, e
costoOP T (σ) è il corrispondente costo dell’algoritmo ottimo off-line.
Osserviamo inannzitutto le seguenti due limitazioni inferiori a costo OP T (σ). Vale
∀σ
costoOP T (σ) ≥ max p(Js ),
(136)
∀σ
n
1 X
p(Js ).
costoOP T (σ) ≥
m s=1
(137)
s=1,...,n
e
P
La (136) è ovvia. La (137) la si ottiene una volta che si osservi che (1/m) ns=1 p(Js )
rappresenta il tempo necessario per eseguire tutti i job. Visto che abbiamo a disposizione
P
m macchine, almeno una di esse dovrà lavorare per un tempo pari a (1/m) ns=1 p(Js ).
Supponiamo ora che l’algoritmo greedy abbia effettuato una certa assegnazione di job
alle macchine, risultante in un dato carico L n1 , . . . , Lnm . Sia Mh la macchina con il carico
massimo, e sia Jt l’ultimo job ad essa assegnato dall’algoritmo greedy. In altri termini,
stiamo assumendo che
costogreedy (σ) = Lnh .
Sosteniamo che per il valore Lnh vale che
∀i
Lni ≥ Lnh − p(Jt ).
(138)
Infatti, al momento in cui viene assegnato il job J t alla macchina Mh , essa risulta essere la
meno carica (per come opera l’algoritmo greedy), da cui la (138). Dalla (138) otteniamo
W =
m
X
i=1
Lni ≥
m
X
i=1
(Lnh − p(Jt )) = m(Lnh − p(Jt ))
da cui
Lnh − p(Jt ) ≤
Osserviamo ora che
n
X
i=1
pertanto
p(Ji ) =
W
.
m
m
X
Lnk ,
k=1
costogreedy (σ) = Lnh
≤
=
W
+ p(Jt )
m
n
1 X
p(Js ) + p(Jt )
m s=1
≤ costoOP T (σ) + costoOP T (σ) (dalla (136) e (137))
(139)
(140)
91
ASDII:Parte B — Lezione 11
il che completa la dimostrazione di (135).
Esaminiamo ora il seguente problema, risolvibile anch’esso attraverso la tecnica greedy.
BIN PACKING ON-LINE
• Input:
– una sequenza σ = σ(1) . . . σ(m), dove ogni σ(i) rappresenta un oggetto di taglia
ai , con 0 < ai ≤ 1;
– un insieme di contenitori B1 , . . . , Bb , b ≥ m, ciascuno di capacità pari a 1.
• Output: un’assegnazione di tutti gli oggetti in σ a contenitori, con la condizione
che la somma delle taglie degli oggetti assegnati a ciascun contenitore non superi 1,
e che minimizzi il numero totale di contenitori usati.
Un possibile algoritmo on-line per questo problema può essere il seguente:
ALGORITMO NEXT-FIT
j←1
for i = 1, . . . , m do
aggiungi l’oggetto σ(i) in Bj , se ci và ,
else poni j ← j + 1 e inserisci σ(i) in B j .
Sia costoNEXT-FIT (σ) il numero di contenitori usati dall’algoritmo greedy NEXTFIT per impacchettare tutti gli oggetti in σ, e sia costo OP T (σ) il corrispondente numero
di contenitori usato dall’algoritmo ottimo off-line che conosce tutta la sequenza di richieste
fin dall’inizio. Vogliamo provare che
costoNEXT-FIT (σ) ≤ 2costoOP T (σ) + 1.
Osserviamo innazitutto che
costoOP T (σ) ≥
m
X
ai ,
i=1
in quanto occorre sicuramente usare un numero di contenitori superiore alla somma totale
delle dimensioni degli oggetti in σ. Consideriamo la soluzione prodotta dall’algoritmo
greedy, e supponiamo che esso abbia usato i contenitori
B1 , . . . , B s ,
92
ASDII:Parte B — Lezione 11
e quindi costoNEXT-FIT(σ) = s. L’osservazione chiave è che
X
∀j
ai +
X
σ(ai )∈Bj+1
σ(ai )∈Bj
ai ≥ 1,
∀j = 1, . . . s − 1.
(141)
Infatti, se ciò non fosse, non avremmo usato il nuovo contenitore B j+1 ma avremmo messo
tutti gli oggetti in Bj . Conseguenza della (141) è che
m
X
ak =
k=1
X
ai +
σ(ai )∈B1
X
ai + . . . +
σ(ai )∈B2
X
ai
σ(ai )∈Bs
≥ 1| + .{z
. . + 1} (raggruppando le somme a due a due consecutivamente)
(s−1)/2volte
=
s−1
.
2
Da cui
costoNEXT-FIT(σ) = s ≤ 2
n
X
k=1
ak + 1 ≤ 2costoOP T (σ) + 1.
L’approccio suggerito dall’analisi degli algoritmi on-line può essere utile anche in altri
contesti. Uno di questi riguarda classici problemi della Teoria delle Decisioni. Ci limitiamo,
in questa lezione, a presentare ed analizzare due semplici esempi.
Esempio 1: Affitto sci. Il problema può essere descritto come segue. Sia N > 1
un intero. Una persona deve usare un equipaggiamento (sci) per un qualche numero T di
giorni, 1 ≤ T ≤ N . Assumiamo che il valore N sia noto alla persona, ma che T non lo sia
(esso può infatti dipendere da ovvi fattori non dipendenti dalla volontà della persona) .
All’inizio di ogni giorno, è noto alla persona in questione se gli occorrono o meno gli sci
per la giornata. Pertanto, la persona deve effetuare una scelta tra le seguenti due opzioni:
1. Affittare gli sci per la giornata, ad un costo assumiamo pari ad 1;
2. Comprare gli sci, ad un costo pari ad un certo valore B 1.
Ovviamente, una volta che la persona abbia acquistato gli sci, non incorrerà in ulteriori
spese nei giorni successivi in cui deciderà ancora di sciare. Potrebbe, però , decidere di non
voler più sciare (tutto dipende dal valore incognito di T ). Il costo totale in cui la persona
incorre è ovviamante pari all’eventuale numero di giorni in cui ha affitato gli sci , più il
costo dell’acquisto degli stessi. Si pone, pertanto, il problema di stabilire una strategia
che minimizzi il costo, senza però conoscere il futuro (ovvero, il valore T dei giorni in cui
effettivamente si scierà ).
In questo caso, l’algoritmica on-line può essere di aiuto. Consideriamo un generico
algoritmo Ai che effettua la seguente decisione: stabilisce un intero i ≥ 0, affitta gli sci per
i giorni e, se ha ancora voglia di sciare dopo tale periodo, li acquista. Abbiamo ovviamente
costoAi =
T
i+B
se T ≤ i;
se T > i.
(142)
93
ASDII:Parte B — Lezione 11
La strategia ottima che conosce il futuro, ovvero T , deciderà ovviamente di comprare gli
sci se il periodo effettivo T in cui si scierà è > B, deciderà di affittarli giorno per giorno,
in caso contrario. Pertanto
costoOP T = min{B, T }.
(143)
Voggliamo provare che il miglior algoritmo consiste nello scegliere i = B − 1. In tal caso
abbiamo
T
se T ≤ B − 1;
costoAB−1 =
(144)
2B − 1 se T > B − 1.
Inoltre
T se T ≤ B − 1;
costoOP T =
(145)
B se T > B − 1.
Abbiamo pertanto che qualunque sia il valore incognito di T , vale che
costoAB−1 ≤
1
2B − 1
costoOP T = 2 −
B
B
costoOP T .
(146)
Proviamo ora che nessun altra scelta del valore di i può portare a coefficienti di competitività migliori di quello appena trovato. Distinguiamo due casi.
• Caso 1: i ≤ B − 1.
Nell’ipotesi che T = i + 1, abbiamo
costoAi
costoOP T
=
=
=
=
≥
=
i+B
min{T, B}
i+B
min{i + 1, B}
i+B
i+1
B−1
1+
i+1
B−1
1+
B
1
2− .
B
Quindi il coefficiente di competitività non è migliorato.
• Caso 2: i > B − 1.
Nell’ipotesi che T = i + 1, abbiamo
costoAi
costoOP T
=
=
=
i+B
min{T, B}
i+B
min{i + 1, B}
i+B
B
94
ASDII:Parte B — Lezione 11
= 1+
≥ 2.
i
B
Anche in questo caso il coefficiente di competitività non è migliorato.
Deduciamo quindi che la scelta ottima consiste nello scegliere i = B − 1.
Esempio 2: Compravendita di azioni. Supponiamo di avere un’azione, il cui
prezzo è noto poter variare tra due valori m e M , con 0 < m < M entrambi noti a
priori. All’inizio di ogni giornata i-esima i = 1, 2, . . . , veniamo a conoscenza del valore
vi ∈ [m, M ] giornaliero dell ’azione, e dobbiamo decidere se vendere l’azione, e quindi
realizzare un profitto vi , oppure mantenere il possesso dell’azione. Qual è la strategia
migliore, ovvero quella che ci massimizza il profitto? Anche in questo caso ci troviamo di
fronte ad un probleam suscettibile di analisi mediante l’algoritmica on-line.
Una generica strategia potrebbe essere la seguente: si stabilisca a priori un valore
p ∈ [m, M ], si venda l’azione il primo giorno in cui v i ≥ p, altrimenti si mantenga l’azione.
Come scegliere il valore p in modo da massimizzare il nostro profitto? Sia p max il valore massimo assunto da vi durante tutto il periodo in considerazione. Ovviamente, noi
non conosciamo tale valore pmax . Possono capitare due casi, a seconda della relazione
sussistente tra il valore p che abbiamo noi scelto ed il valore incognito p max .
• Caso 1: p ≤ pmax .
In tal caso, ci sarà un giorno in cui v i ≥ p, e pertanto realizzeremo un profitto p.
Potrebbe accadere, però , che il valore dell’azione continui a crescere ed arrivi al
valore massimo M . L’algoritmo ottimo off-line venderà l’azione esattamente quando
essa varrà M . Quindi, il rapporto tra il profitto dell’algoritmo on-line e dell’algoritmo
ottimo off-line è p/M .
• Caso 1: p > pmax .
In tal caso, non ci sarà mai un giorno in cui v i ≥ p, quindi non venderemmo mai
per tutta la durata del periodo in questione. Potrebbe accadere, pertanto, che
alla fine del periodo l’azione valga m, quindi questo è il profitto che realizzeremo.
L’algoritmo ottimo off-line, invece, venderà appena l’azione vale p max , realizzando
un profitto appunto pari a pmax . Il rapporto tra il profitto dell’algoritmo on-line e
dell’algoritmo ottimo off-line è in questo caso m/p max , che può ovviamente essere
arbitrariamente prossimo a m/p.
Il miglior algoritmo on-line è sicuramente quello che sceglierà p in modo tale che vi sia
bilanciamento tra le due possibili situazioni, ovvero per cui valga
p
m
= ,
M
p
ovvero sceglierà il valore p pari a
p=
√
mM .
ASDII:Parte B — Lezione 11
95
Detto in altri termini, l’algoritmo on-line ottimo
è quello che decide, giorno per giorno,
√
di vendere l’azione se il suo valore v i è ≥ mM , e che decide di mantenere il possesso
dell’azione in caso contrario. Il fattore di competitività di un tale algoritmo è pari a
p
M/m.
96
References
[1] V. Vazirani, Approximation Algorithms, Springer Verlag, 2001.
[2] D. Hochbaum, Approximation Algorithms for NP-Hard Problems, Wadsworth Publishing Company, 1996.
[3] A. Borodin, R. El-Yaniv, Online Computation and Competitive Analysis, Cambridge
University Press, 1998.