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.