Algoritmi e Strutture Dati

Slides:



Advertisements
Presentazioni simili
Algoritmi e Strutture dati Mod B
Advertisements

Algoritmi e Strutture Dati
Algoritmi e Strutture Dati
Dipartimento di Ingegneria Idraulica e Ambientale - Universita di Pavia 1 Caduta non guidata di un corpo rettangolare in un serbatoio Velocità e rotazione.
Strutture dati per insiemi disgiunti
1 MeDeC - Centro Demoscopico Metropolitano Provincia di Bologna - per Valutazione su alcuni servizi erogati nel.
Il problema del minimo albero ricoprente in un grafo non cooperativo
Algoritmi e Strutture Dati
Cammini minimi con una sorgente
Alberi binari di ricerca
Implementazione dell algortimo di Viterbi attraverso la soluzione del problema di cammino mi- nimo tramite software specifico. Università degli studi di.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Ordinamenti ottimi.
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati Capitolo 2 Modelli di calcolo e metodologie di analisi.
Algoritmi e Strutture Dati
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Capitolo 12 Minimo albero ricoprente: Algoritmi di Prim e di Borůvka Algoritmi.
U V U V (a) |cfc|=2 prima e dopo (b) |cfc|=2 prima e |cfc|=1 dopo
Algoritmo di Ford-Fulkerson
Il problema del minimo albero ricoprente in un grafo con archi privati
Algoritmi e strutture Dati - Lezione 7
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Capitolo 12 Minimo albero ricoprente: Algoritmi di Prim e di Borůvka Algoritmi.
Algoritmi e Strutture Dati
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Capitolo 12 Minimo albero ricoprente: Algoritmi di Prim e di Boruvka Algoritmi.
Algoritmi e Strutture Dati
Capitolo 4 Ordinamento Algoritmi e Strutture Dati.
Capitolo 9 Il problema della gestione di insiemi disgiunti (Union-find) Algoritmi e Strutture Dati.
Capitolo 10 Tecniche algoritmiche Algoritmi e Strutture Dati.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Capitolo 12 Minimo albero ricoprente: Algoritmi di Prim e di Borůvka Algoritmi.
Algoritmi e Strutture Dati
Canale A. Prof.Ciapetti AA2003/04
Algoritmi e Strutture Dati (Mod. A)
Algoritmi e Strutture Dati (Mod. B)
Algoritmi e Strutture Dati (Mod. B)
Algoritmi e strutture dati
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati
1 © Alberto Montresor Algoritmi e Strutture Dati Capitolo 7 - Tabelle hash Alberto Montresor Università di Trento This work is licensed under the Creative.
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati
MP/RU 1 Dicembre 2011 ALLEGATO TECNICO Evoluzioni organizzative: organico a tendere - ricollocazioni - Orari TSC.
Heap binomiali Gli heap binomiali sono strutture dati su cui si possono eseguire efficientemente le operazioni: Make(H) : crea uno heap vuoto Insert(H,
Cos’è un problema?.
Gli italiani e il marketing di relazione: promozioni, direct marketing, digital marketing UNA RICERCA QUANTITATIVA SVOLTA DA ASTRA RICERCHE PER ASSOCOMUNICAZIONE.
CHARGE PUMP Principio di Funzionamento
Fibonacci Heaps e il loro utilizzo nell’algoritmo di Prim
Q UESTIONI ETICHE E BIOETICHE DELLA DIFESA DELLA VITA NELL AGIRE SANITARIO 1 Casa di Cura Villa San Giuseppe Ascoli Piceno 12 e 13 dicembre 2011.
1 Negozi Nuove idee realizzate per. 2 Negozi 3 4.
ORDINE DI CHIAMATA a 1minuto e 2 minuti PRINCIPALI TEMPI DELLA COMPETIZIONE ORDINE DI CHIAMATA a 1minuto e 2 minuti PRINCIPALI TEMPI DELLA COMPETIZIONE.
ISTITUTO COMPRENSIVO “G. BATTAGLINI” MARTINA FRANCA (TA)
Lezioni di Ricerca Operativa Corso di Laurea in Informatica
Passo 3: calcolo del costo minimo
Cerchiamo di rispondere alla seconda domanda 2)La soluzione trovata con lalgoritmo goloso è ottima o esistono anche soluzioni con più di quattro attività?
Algoritmi e Strutture Dati
Un trucchetto di Moltiplicazione per il calcolo mentale
Algoritmi e Strutture Dati
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Capitolo 12 Minimo albero ricoprente: Algoritmi di Prim e di Borůvka Algoritmi.
Esempi risolti mediante immagini (e con excel)
NO WASTE Progetto continuità scuola primaria scuola secondaria Salorno a.s. 2013_
Capitolo 10 Tecniche algoritmiche Algoritmi e Strutture Dati.
Capitolo 10 Tecniche algoritmiche Algoritmi e Strutture Dati.
Capitolo 12 Minimo albero ricoprente: Algoritmo di Kruskal Algoritmi e Strutture Dati.
Codici prefissi Un codice prefisso è un codice in cui nessuna parola codice è prefisso (parte iniziale) di un’altra Ogni codice a lunghezza fissa è ovviamente.
Capitolo 12 Minimo albero ricoprente: Algoritmo di Kruskal Algoritmi e Strutture Dati.
Transcript della presentazione:

Algoritmi e Strutture Dati Capitolo 14 - Greedy Alberto Montresor Università di Trento This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.5/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA. © Alberto Montresor

Gli algoritmi per problemi di ottimizzazione Introduzione Gli algoritmi per problemi di ottimizzazione Eseguono una sequenza di decisioni La programmazione dinamica In maniera bottom-up, valuta tutte le decisioni possibili Evitando però di ripetere sotto-problemi (decisioni) già percorse Un algoritmo greedy (ingordo, goloso) Seleziona una sola delle possibili decisioni... ... quella che sembra ottima (ovvero, è localmente ottima) Sperabilmente, si ottiene così un ottimo globale © Alberto Montresor

Quando applicare la tecnica greedy? Introduzione Quando applicare la tecnica greedy? Quando è possibile dimostrare che esiste una scelta ingorda “Fra le molte scelte possibili, ne può essere facilmente individuata una che porta sicuramente alla soluzione ottima” Quando il problema ha sottostruttura ottima “Fatta tale scelta, resta un sottoproblema con la stessa struttura del problema principale” Non tutti i problemi hanno una scelta ingorda Nota: in alcuni casi, soluzioni non ottime possono essere comunque interessanti © Alberto Montresor

Insieme indipendente di intervalli i ai bi Problema: Siano dati n intervalli distinti [ai, bi[ della retta reale trovare un insieme indipendente massimo un sottoinsieme di massima cardinalità formato da intervalli tutti disgiunti tra loro Definizioni: S={ 1, 2,…, n } un insieme di intervalli Ad ogni intervallo i in S sono associati: ai → tempo di inizio bi → tempo di fine Che relazione c’è con “Insieme indipendente di intervalli pesati”? 1 1 4 2 3 5 3 0 6 4 5 7 5 3 8 6 5 9 7 6 10 8 8 11 9 8 12 10 12 14 © Alberto Montresor

Attività 1 2 3 4 5 6 7 8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

Attività 1 2 3 4 5 6 7 8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

Come affrontare il problema Cerchiamo innanzitutto di risolverlo tramite programmazione dinamica (PD): Individuiamo una sottostruttura ottima Scriviamo una definizione ricorsiva per la dimensione della soluzione ottima Scriviamo una versione iterativa bottom-up dell'algoritmo Passiamo poi alla tecnica greedy Cerchiamo una possibile scelta ingorda Dimostriamo che la scelta ingorda porta alla soluzione ottima Scriviamo un algoritmo ricorsivo o iterativo che effettua sempre la scelta ingorda © Alberto Montresor

Sottostruttura ottima Si assuma che le attività siano ordinate per tempo di fine: b1 ≤ b2 ≤ … ≤ bn Sottoproblema S[i,j] S[i,j] = { k | bi ≤ ak < bk ≤ aj } Il sottoinsieme di attività che iniziano dopo che i ha finito finiscono prima che j abbia iniziato Aggiungiamo due attività “fittizie”: 0 → b0 = 0 n+1 → an+1 = +∞ Così S[0, n+1] corrisponde al problema completo S Domanda Dimostrare che se i ≥ j, allora S[i,j] è vuoto bi ak bk aj © Alberto Montresor

Sottostruttura ottima: caratterizzazione Se A[i,j] è una soluzione ottimale di S[i,j], e l'attività k è inclusa in A[i,j], allora Il problema S[i,j] viene suddiviso in due sottoproblemi: S[i,k]: le attività di S[i,j] che finiscono prima di k S[k,j]: le attività di S[i,j] che iniziano dopo di k A[i,j] contiene le soluzioni ottimali di S[i,k] e S[k,j] A[i,j] ∩ S[i,k] è la soluzione ottimale di S[i,k] A[i,j] ∩ S[k,j] è la soluzione ottimale di S[k,j] Dimostrazione: Utilizzando il metodo cut-and-paste © Alberto Montresor

Definizione ricorsiva Descrizione ricorsiva della soluzione: A[i,j] = A[i,k] ∪ { k } ∪ A[k, j] Come determinare k ? Analizzando tutte le possibilità... Definizione: D[i, j] La dimensione del più grande sottoinsieme A[i,j] ⊆ S[i,j] di attività mutualmente compatibili Come si calcola? © Alberto Montresor

Verso una soluzione “greedy” La definizione precedente: Ci permette di scrivere un algoritmo basato su programmazione dinamica o su memoization Complessità: θ(n3) Devo risolvere tutti i sottoproblemi con i ≠ j, nel caso peggiore tempo O(n) per un sottoproblema Posso fare di meglio? Avevamo visto una soluzione O(n log n) Richiedeva pre-elaborazione Questa soluzione è peggiore, ma... Siamo sicuri che sia necessario analizzare tutti i possibili valori per k? © Alberto Montresor

Teorema: “Greedy choice” Sia S[i,j] un sottoproblema non vuoto, e m l'attività di S[i,j] con il minor tempo di fine; allora m è compresa in qualche soluzione ottima di S[i,j] Il sottoproblema S[i,m] è vuoto Dimostrazione Conseguenze: Non è più necessario analizzare tutti i possibili valori di k Faccio una scelta “ingorda”, ma sicura: seleziono l'attività m con il minor tempo di fine Non è più necessario analizzare due sottoproblemi: Elimino tutte le attività che non sono compatibili con la scelta ingorda Mi resta solo un sottoproblema da risolvere: S[m,j] © Alberto Montresor

Realizzazione iterativa Complessità Inserisce la prima attività: è sempre compatibile Se l'input è già ordinato: θ(n) Altrimenti, è necessario un passo di ordinamento: θ(n log n) © Alberto Montresor

i=0 1 2 3 4 5 6 7 Attività 8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

i=1 2 3 4 5 6 7 Attività 8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

i=1 2 3 4 5 6 7 Attività 8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

i=1 2 3 4 5 6 7 Attività 8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

1 2 3 i=4 5 6 7 Attività 8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

1 2 3 i=4 5 6 7 Attività 8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

1 2 3 i=4 5 6 7 Attività 8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

1 2 3 i=4 5 6 7 Attività 8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

1 2 3 4 5 6 7 Attività i=8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

1 2 3 4 5 6 7 Attività i=8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

1 2 3 4 5 6 7 Attività i=8 9 10 11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

1 2 3 4 5 6 7 Attività 8 9 10 i=11 Tempo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 © Alberto Montresor

Tecnica greedy – approccio a partire da PD Abbiamo cercato di risolvere il problema della selezione delle attività tramite programmazione dinamica: Abbiamo individuato una sottostruttura ottima Abbiamo scritto una definizione ricorsiva per la dimensione della soluzione ottima Abbiamo dimostrato la proprietà della scelta greedy: Per ogni sottoproblema, esiste almeno una soluzione ottima che inizia con la scelta greedy Abbiamo scritto un algoritmo iterativo che effettua sempre la scelta ingorda © Alberto Montresor

Un numero intero positivo n Output Problema del resto Input Un numero intero positivo n Output Il più piccolo numero intero di pezzi per dare un resto di n centesimi utilizzando monete da 50c, 10c, 5c e 1c. Esempi n = 78, 7 pezzi: 50+10+10+5+1+1+1 n = 18, 5 pezzi: 10+5+1+1+1 Seguiamo la stessa procedura precedente: Programmazione dinamica → approccio greedy © Alberto Montresor

Descrivere un alg. goloso per dare il resto con le monete specificate Problema del resto Domanda Descrivere un alg. goloso per dare il resto con le monete specificate Sottostruttura ottima Scelta ingorda Provare che tale algoritmo fornisce sempre una soluzione ottima Supponete di avere un insieme di monete i cui valori siano potenze di c: c0, c1, c2, ..., ck L'algoritmo visto in precedenza funziona ancora? Trovare un insieme di “pezzature” per cui la scelta golosa non è applicabile © Alberto Montresor

Tecnica greedy – approccio senza passare per PD Evidenziare i “passi di decisione” Trasformare il problema di ottimizzazione in un problema di “scelte” successive Evidenziare una possibile scelta ingorda Dimostrare che tale scelta rispetto il “principio della scelta ingorda” Evidenziare la sottostruttura ottima Dimostrare che la soluzione ottima del problema “residuo” dopo la scelta ingorda può essere unito a tale scelta Scrittura codice: top-down, anche in maniera iterativa Nota: può essere necessario pre-processare l'input © Alberto Montresor

Algoritmo di scheduling Definizione: 1 processore, n job p1, p2, ..., pn Ogni job pi ha un tempo di esecuzione t[i] Minimizzare il tempo di completamento medio Domanda: Risolvere il problema tramite la tecnica greedy 1 4 3 6 1 4 3 6 8 14 5 11 (4+5+11+14)/4 = 34/4 (1+4+8+14)/4 = 27/4 Shortest job first © Alberto Montresor

Un intero positivo C - la capacità dello zaino Input Un intero positivo C - la capacità dello zaino n oggetti, tali che l’oggetto i-esimo è caratterizzato da: un profitto pi e un volume vi , entrambi interi positivi Zaino (o Zaino 0/1) trovare un sottoinsieme S di {1, . . . , n} di oggetti tale che il volume totale non superi la capacità massima e il profitto totale sia massimo Zaino reale (o Zaino frazionario) E' possibile prendere frazioni di oggetti © Alberto Montresor

Scegli per primo l’oggetto che ha profitto più alto v[] 10 €60 25 55 €125 30 €120 C Scegli per primo l’oggetto che ha profitto più alto 30 25 €125 + €120 = €245 © Alberto Montresor

Scegli per primo l’oggetto che ha profitto più alto v[] 10 €60 25 55 €125 30 €120 C Scegli per primo l’oggetto che ha profitto più alto 30 25 €125 + €120 = €245 30 Scegli per primo l’oggetto che ha volume più alto 25 €120 + €125 = €245 © Alberto Montresor

Scegli per primo l’oggetto che ha profitto più alto v[] 10 €60 €6 per litro 25 55 €125 30 €5 per litro €120 €4 per litro C Scegli per primo l’oggetto che ha profitto più alto 30 25 €125 + €120 = €245 30 Scegli per primo l’oggetto che ha volume più alto 25 €120 + €125 = €245 Scegli per primo l’oggetto che ha il più alto “profitto specifico” 25 20/30 10 €60 + €120 + €80 = €260 © Alberto Montresor

Dimostrare che il problema dello Zaino Reale gode della scelta greedy Domanda: Dimostrare che il problema dello Zaino Reale gode della scelta greedy Dimostrare che il problema dello Zaino 0-1 non gode di scelta greedy In altre parole: Risolvere il problema dello Zaino Reale con programmazione dinamica è inutile e costoso Risolvere il problema dello Zaino 0-1 con tecnica greedy è scorretto © Alberto Montresor

O(n log n) per l’ordinamento O(n) per la scelta dei valori Zaino reale Complessità O(n log n) per l’ordinamento O(n) per la scelta dei valori © Alberto Montresor

Problema della compressione Rappresentare i dati in modo efficiente Impiegare il numero minore di bit per la rappresentazione Goal: risparmio spazio su disco e tempo di trasferimento Una possibile tecnica di compressione: codifica di caratteri Tramite funzione di codifica f: f(c) = x c è un possibile carattere preso da un alfabeto Σ x è una rappresentazione binaria “c è rappresentato da x” © Alberto Montresor

Supponiamo di avere un file di n caratteri Codici di Huffman Supponiamo di avere un file di n caratteri Composto dai caratteri: a b c d e f Con le seguenti frequenze: 45% 13% 12% 16% 9% 5% Codifica tramite ASCII (8 bit per carattere) Dimensione totale: 8n bit Codifica basata sull'alfabeto (3 bit per carattere) Codifica: 000 001 010 011 100 101 Dimensione totale: 3n bit Possiamo fare di meglio? © Alberto Montresor

Considerate questo codice: Codici di Huffman Codifica a lunghezza variabile Caratteri: a b c d e f Codifica: 0 101 100 111 1101 1100 Costo totale: (0.45⋅1+0.13⋅3+0.12⋅3+0.16⋅3+0.09⋅4+0.05⋅4)⋅n=2.24n Codice “a prefisso” (meglio, “senza prefissi”): Nessun codice è prefisso di un altro codice Condizione necessaria per permettere la decodifica Esempio: ADDAABCA 0·111·111·0·0·101·100·0 Considerate questo codice: f(a) = 1, f(b)=10, f(c)=11 © Alberto Montresor

Esistono testi “difficili” per una rappresentazione di questo tipo? Codici di Huffman Alcune domande E' possibile che il testo codificato sia più lungo della rappresentazione con 3 bit? Esistono testi “difficili” per una rappresentazione di questo tipo? Come organizzare un algoritmo per la decodifica? Come organizzare un algoritmo per la codifica è il tema dei lucidi seguenti Cenni storici David Huffman, 1952 Algoritmo ottimo per costruire codici prefissi Utilizzato in PKZIP © Alberto Montresor

Rappresentazione ad albero per la decodifica Algoritmo di decodifica: 1. parti dalla radice 2. leggi un bit alla volta percorrendo l'albero: 0: sinistra 1: destra 3. stampa il carattere della foglia 4. torna a 1 Rappresentazione come alberi binari Figlio sinistro: 0 Figlio destro: 1 Caratteri dell'alfabeto sulle foglie b a c e 1 d a: 00 b: 010 c: 011 d: 100 e: 101 © Alberto Montresor

Rappresentazione ad albero per la decodifica Non c'è motivo di avere un nodo interno con un solo figlio Il “figlio unico” può essere sostituito al proprio genitore Sia che sia una foglia che un nodo interno 1 a: 00 b: 010 c: 011 d: 100 e: 101 Spazio sprecato a b c d e © Alberto Montresor

Rappresentazione ad albero per la decodifica Non c'è motivo di avere un nodo interno con un solo figlio Il “figlio unico” può essere sostituito al proprio genitore Sia che sia una foglia che un nodo interno 1 1 1 a d e 1 In questo albero ogni nodo interno ha due figli b c © Alberto Montresor

Definizione formale del problema Definizione: codice ottimo Dato un file F, un codice C è ottimo per F se non esiste un altro codice tramite il quale F possa essere compresso impiegando un numero inferiore di bit. Nota: Il codice ottimo dipende dal particolare file Possono esistere più soluzioni ottime I codici a prefisso ottimi sono rappresentati da un albero in cui tutti i nodi interni hanno due figli © Alberto Montresor

Definizione formale del problema Supponiamo di avere: un file F composto da caratteri nell’alfabeto Σ un albero T che rappresenta la codifica Quanti bit sono richiesti per codificare il file? Per ogni c ∈ Σ, sia dT(c) la profondità della foglia che rappresenta c Il codice per c richiederà allora dT(c) bit Se f [c] è il numero di occorrenze di c in F, allora la dimensione della codifica è © Alberto Montresor

Principio del codice di Huffman Algoritmo di Huffman Principio del codice di Huffman Minimizzare la lunghezza dei caratteri che compaiono più frequentemente Assegnare ai caratteri con la frequenza minore i codici corrispondenti ai percorsi più lunghi all’interno dell’albero Un codice è progettato per un file specifico Si ottiene la frequenza di tutti i caratteri Si costruisce il codice Si rappresenta il file tramite il codice Si aggiunge al file una rappresentazione del codice © Alberto Montresor

Costruzione del codice Passo 1: Costruire una lista ordinata di nodi foglia per ogni carattere, etichettato con la propria frequenza f : 5 e : 9 c : 12 b : 13 d : 16 a : 45 © Alberto Montresor

Costruzione del codice Passo 2: Rimuovere i due nodi “più piccoli” (con frequenze minori) Passo 3: Collegarli ad un nodo padre etichettato con la frequenza combinata (sommata) c : 12 b : 13 d : 16 a : 45 14 f : 5 e : 9 © Alberto Montresor

Costruzione del codice Passo 4: Aggiungere il nodo combinato alla lista. c : 12 b : 13 14 d : 16 a : 45 f : 5 e : 9 © Alberto Montresor

Costruzione del codice Ripetere i passi 2-4 fino a quando non resta un solo nodo nella lista 14 d : 16 25 a : 45 f : 5 e : 9 c : 12 b : 13 © Alberto Montresor

Costruzione del codice Ripetere i passi 2-4 fino a quando non resta un solo nodo nella lista 25 30 a : 45 c : 12 b : 13 14 d : 16 f : 5 e : 9 © Alberto Montresor

Costruzione del codice Ripetere i passi 2-4 fino a quando non resta un solo nodo nella lista a : 45 55 25 30 c : 12 b : 13 14 d : 16 f : 5 e : 9 © Alberto Montresor

Costruzione del codice Al termine, si etichettano gli archi dell'albero con bit 0-1 100 1 a 55 1 25 30 1 1 c b 14 d 1 f e © Alberto Montresor

Algoritmo in pseudo-codice Tree: f // frequenza (key) c // carattere left // figlio sinistro right // figlio destro Complessità θ(n log n) © Alberto Montresor

Dimostrazione di correttezza Teorema: L'output dell'algoritmo Huffman per un dato file è un codice a prefisso ottimo Schema della dimostrazione: Sottostruttura ottima Dato un problema sull'alfabeto Σ, è possibile costruire un sottoproblema con un alfabeto più piccolo Proprietà della scelta greedy Scegliere i due elementi con la frequenza più bassa conduce sempre ad una soluzione ottimale © Alberto Montresor

Σ un alfabeto, f un array di frequenze Scelta greedy Sia Σ un alfabeto, f un array di frequenze x, y i due caratteri che hanno frequenza più bassa Allora Esiste un codice prefisso ottimo per Σ in cui x,y hanno la stessa profondità massima e i loro codici differiscono solo per l'ultimo bit Dimostrazione Al solito, basata sulla trasformazione di una soluzione ottima Supponiamo che esistano due caratteri a,b con profondità massima e questi siano diversi da x,y © Alberto Montresor

Assumiamo (senza perdere in generalità): f[x] ≤ f[y] f[a] ≤ f[b] Scelta greedy Assumiamo (senza perdere in generalità): f[x] ≤ f[y] f[a] ≤ f[b] Poiché le frequenze di x e y sono minime: f[x] ≤ f[a] f[y] ≤ f[b] Scambiamo x con a: otteniamo T' Scambiamo y con b: otteniamo T" T T' T" x a a ... ... ... y y b a b x b x y © Alberto Montresor

Ma poiché T è ottimo, sappiamo anche che: C(f,T) ≤ C(f,T '') Scelta greedy Dimostriamo che: C(f,T '') ≤ C(f, T ') ≤ C(f,T) Ma poiché T è ottimo, sappiamo anche che: C(f,T) ≤ C(f,T '') Quindi T'' è anch'esso ottimo © Alberto Montresor

Sottostruttura ottima Sia Σ un alfabeto, f un array di frequenze x, y i due caratteri che hanno frequenza più bassa Σ' = Σ - { x, y } ∪ { z } f[z] = f[x] + f[y] O un albero che rappresenta un codice a prefisso ottimo per Σ' Allora: L'albero T (ottenuto da O sostituendo il nodo foglia z con un nodo interno con due figli x, y) rappresenta un codice a prefisso ottimo per Σ © Alberto Montresor

Sottostruttura ottima Esprimiamo la relazione fra il costo di T e O Per ogni c ∈ Σ - { x, y } → f[c]·dT(c) = f[c]·dO(c) Quindi tutte queste componenti sono uguali dT(x) = dT(y) = dO(z)+1 da cui concludiamo f[x]·dT(x) + f[y]·dT(y) = (f[x] + f[y])( dO(z)+1 ) = f[z]·dO(z) + f[x] + f[y] C(f, T) = C(f, O) + f[x] + f[y] C(f, O) = C(f, T) - f[x] - f[y] © Alberto Montresor

Sottostruttura ottima Per assurdo: supponiamo T non sia ottimo Allora esiste un albero T' tale che C(f, T') < C(f, T) Senza perdere in generalità, sappiamo che T' ha x e y come foglie sorelle Sia T'' l'albero ottenuto da T' sostituendo il padre di x, y con un nodo z tale che f[z] = f[x] + f[y] Allora C(f, T'') = C(f, T') – f[x] – f[y] < C(f, T) – f[x] – f[y] = C(f, O) Il che è assurdo, visto che O è ottimo © Alberto Montresor

Algoritmo di scheduling - Programmi in ritardo Dati n programmi p1, ..., pn, tali che ciascun pi richiede ti unità di tempo di esecuzione e deve essere eseguito entro una certa scadenza di, trovare un ordine S in cui eseguirli tutti in modo da minimizzare il numero di programmi per i quali la scadenza non è rispettata Algoritmo di Moore (1968) Sequenza iniziale S - programmi ordinati per scadenze crescenti Si cerca il primo programma p in ritardo Si elimina il più lungo programma p’ nella sottosequenza che precede p, ottenendo la sequenza S’ Si itera il procedimento sulla sottosequenza S’, fino ad ottenere una sottosequenza S* senza programmi in ritardo Si eseguono quindi i programmi in S*, seguiti dai programmi in ritardo © Alberto Montresor

Algoritmo di scheduling - Programmi in ritardo Dimostrazione Basata sul principio di sostituzione: Trasformiamo una qualunque soluzione ottima nella sequenza generata dall’algoritmo greedy, senza inficiarne l’ottimalità © Alberto Montresor

Albero di copertura di peso minimo Un problema di notevole importanza: determinare come interconnettere diversi elementi fra loro minimizzando certi vincoli sulle connessioni Esempio classico: progettazione dei circuiti elettronici dove si vuole minimizzare la quantità di filo elettrico per collegare fra loro i diversi componenti Questo problema prende il nome di: albero di copertura (di peso) minimo albero di connessione (di peso) minimo minimum spanning tree © Alberto Montresor

Definizione del problema Input: G=(V,E) un grafo non orientato e connesso w: V×V → R una funzione di peso (costo di connessione) se [u,v] ∈ E, allora w(u,v) è il peso dell'arco [u,v] se [u,v] ∉ E, allora w(u,v) = ∞ Poiché G non è orientato, w(u,v) = w(v,u) © Alberto Montresor

Definizione del problema Albero di copertura (spanning tree) Dato un grafo G=(V,E) non orientato e connesso, un albero di copertura di G è un sottografo T=(V, ET) tale che T è un albero ET ⊆ E T contiene tutti i vertici di G 8 7 b c d 9 4 2 a 11 i 14 e 7 6 4 8 10 h g f 1 2 © Alberto Montresor

Definizione del problema Output: albero di copertura minimo (minimum spanning tree) L'albero di copertura il cui peso totale sia minimo Nota: L’albero di copertura di peso minimo non è unico 8 7 8 7 b c d b c d 9 9 4 2 4 2 a 11 i e a 11 i 14 14 e 7 6 7 6 4 4 8 8 10 10 h g f h g f 1 2 1 2 © Alberto Montresor

Differenze con cammini minimi Calcolare un albero dei cammini minimi da singola sorgente a un albero di copertura minima coincidono? 2 a b 2 1 c d 1 © Alberto Montresor

Un algoritmo di tipo “goloso” generico Algoritmo generico Vediamo Un algoritmo di tipo “goloso” generico Due “istanze” di questo algoritmo: Kruskal e Prim L'idea è di accrescere un sottoinsieme A di archi in modo tale che venga rispettata la seguente condizione: A è un sottoinsieme di qualche albero di connessione minimo Un arco [u,v] è detto sicuro per A se A ∪ {[u,v]} è ancora un sottoinsieme di qualche albero di connessione minimo. © Alberto Montresor

Algoritmo generico © Alberto Montresor

Un arco [u,v] attraversa il taglio se u ∈S e v ∈V-S Definizioni Per caratterizzare gli archi sicuri dobbiamo introdurre alcune definizioni: Un taglio (S,V-S) di un grafo non orientato G=(V,E) è una partizione di V in due sottoinsiemi disgiunti Un arco [u,v] attraversa il taglio se u ∈S e v ∈V-S Un taglio rispetta un insieme di archi A se nessun arco di A attraversa il taglio Un arco che attraversa un taglio è leggero nel taglio se il suo peso è minimo fra i pesi degli archi che attraversano un taglio © Alberto Montresor

Insieme A: archi in grigio Esempio Arco leggero che attraversa il taglio 8 7 b c d 9 4 2 a 11 i 14 e 7 6 4 8 V 10 h g f 1 2 Taglio V-S Insieme A: archi in grigio Il taglio rispetta A © Alberto Montresor

La regola per riconoscere gli archi sicuri è data dal seguente Teorema: Sia G=(V,E) un grafo non orientato e connesso Sia w una funzione peso a valori reali definita su E Sia A ⊆ E contenuto in un qualche albero di copertura minimo per G Sia (S,V-S) un qualunque taglio che rispetta A Sia [u,v] un arco leggero che attraversa il taglio. Allora l’arco [u,v] è sicuro per A © Alberto Montresor

Esempio: arco non sicuro perché il taglio non rispetta A 8 7 b c d 9 4 2 a 11 i 14 e 7 6 4 8 10 h g f 8 7 1 2 b c d 9 4 2 Arco sicuro a 11 i 14 e 7 6 4 8 7 b c d 8 10 9 h g f 4 2 1 2 a 11 i 14 e 7 6 4 8 10 h g f 1 2 Arco non sicuro © Alberto Montresor

Esempio: arco non sicuro perché non leggero 8 7 b c d 9 4 2 a 11 i 14 e 7 6 4 8 10 h g f 8 7 1 2 b c d 9 4 2 Arco sicuro a 11 i 14 e 7 6 8 7 4 b c d 8 9 10 4 2 h g f 1 2 a 11 i 14 e 7 6 4 8 10 h g f 1 2 Arco non sicuro © Alberto Montresor

Sia G=(V,E) un grafo non orientato e connesso Archi sicuri Corollario: Sia G=(V,E) un grafo non orientato e connesso Sia w una funzione peso a valori reali definita su E Sia A ⊆ E contenuto in un qualche albero di copertura minimo per G Sia C una componente connessa (un albero) nella foresta GA=(V,A) Sia [u,v] un arco leggero che connette C a qualche altra componente in GA Allora l’arco [u,v] è sicuro per A © Alberto Montresor

Utilizziamo una struttura dati Merge-Find Algoritmo di Kruskal Idea Ingrandire sottoinsiemi disgiunti di un albero di copertura minimo connettendoli fra di loro fino ad avere l’albero complessivo Si individua un arco sicuro scegliendo un arco [u,v] di peso minimo tra tutti gli archi che connettono due distinti alberi (componenti connesse) della foresta L’algoritmo è greedy perché ad ogni passo si aggiunge alla foresta un arco con il peso minore Implementazione Utilizziamo una struttura dati Merge-Find © Alberto Montresor

Algoritmo di Kruskal © Alberto Montresor

Visualizzazione 8 7 8 7 b c d b c d 9 9 4 2 4 2 a 11 i 14 e a 11 i 14 6 7 6 4 4 8 8 10 10 h g f h g f 1 2 1 2 8 7 8 7 b c d b c d 9 9 4 2 4 2 a 11 i 14 e a 11 i 14 e 7 6 7 6 4 4 8 8 10 10 h g f h g f 1 2 1 2 © Alberto Montresor

Visualizzazione 8 7 8 7 b c d b c d 9 9 4 2 4 2 a 11 i 14 e a 11 i 14 6 7 6 4 4 8 8 10 10 h g f h g f 1 2 1 2 8 7 8 7 b c d b c d 9 9 4 2 4 2 a 11 i 14 e a 11 i 14 e 7 6 7 6 4 4 8 8 10 10 h g f h g f 1 2 1 2 © Alberto Montresor

Visualizzazione 8 7 8 7 b c d b c d 9 9 4 2 4 2 a 11 i 14 e a 11 i 14 6 7 6 4 4 8 8 10 h g f 10 h g f 1 2 1 2 8 7 8 7 b c d b c d 9 4 2 9 4 2 a 11 i 14 e 7 6 a 11 i 4 14 e 7 6 4 8 8 h g f 10 10 h g f 1 2 1 2 © Alberto Montresor

Utilizziamo la versione con euristica sul rango + compressione Analisi Il tempo di esecuzione per l’algoritmo di Kruskal dipende dalla realizzazione della struttura dati per insiemi disgiunti Utilizziamo la versione con euristica sul rango + compressione L’inizializzazione richiede O(n) L'ordinamento richiede O(m log m) = O(m log n2) = O(m log n) Vengono eseguite O(m) operazioni sulla foresta di insiemi disgiunti, il che richiede un tempo O(m) Totale: O(n+m log n + m) = O(m log n) © Alberto Montresor

L’algoritmo di Prim procede mantenendo in A un singolo albero L’albero parte da un vertice arbitrario r (la radice) e cresce fino a quando non ricopre tutti i vertici Ad ogni passo viene aggiunto un arco leggero che collega un vertice in VA con un vertice in V-VA dove VA è l'insieme di nodi raggiunti da archi in A Correttezza (VA,V-VA) è un taglio che rispetta A (per definizione) Per il corollario, gli archi leggeri che attraversano il taglio sono sicuri © Alberto Montresor

Una struttura dati per i nodi non ancora nell'albero Implementazione Una struttura dati per i nodi non ancora nell'albero Durante l'esecuzione, i vertici non ancora nell'albero si trovano in una coda con priorità Q ordinata in base alla seguente definizione di priorità: “La priorità del nodo v è il peso minimo di un arco che collega v ad un vertice nell'albero, o +∞ se tale arco non esiste” Come mantenere l'albero Ogni nodo v mantiene un puntatore al padre p[v] A è mantenuto implicitamente: A = { [v, v.p] | v ∈V-Q-{r} } Terminazione: Quando la coda Q è vuota Tutti i nodi tranne la radice conoscono il proprio padre © Alberto Montresor

Algoritmo di Prim © Alberto Montresor

Algoritmo di Prim: Esempio 4 8 7 8 8 7 b c d b c d 9 9 4 2 4 2 a 11 a 11 i 14 e i 14 e 7 6 7 6 4 4 8 8 10 10 h g f h g f 1 2 1 2 8 8 7 7 8 7 8 7 b c d b c d 9 9 4 2 4 2 2 a 11 i 14 e a 11 i 14 e 7 6 7 6 4 4 8 8 10 10 h g f h g f 1 2 1 6 2 4 7 4 8 © Alberto Montresor

Algoritmo di Prim: Esempio 8 7 8 7 7 7 b c d b c d 9 9 4 2 4 2 10 a 11 a 11 i e i 14 14 e 7 6 7 6 4 10 4 8 8 10 10 h g f h g f 1 2 1 2 7 1 2 7 8 7 8 7 b c d 9 b c d 4 2 9 4 2 10 a 11 i 14 e 7 6 a 11 4 i 14 e 7 6 4 9 8 h 10 g f 8 h 10 g 6 f 1 2 1 2 © Alberto Montresor

Algoritmo di Prim: Esempio 8 7 b c d 9 4 2 a 11 i 14 e 7 6 4 8 h 10 g f 1 2 © Alberto Montresor

Algoritmo di Prim: Analisi L’efficienza dell’algoritmo di Prim dipende dalla coda Q Se Q viene realizzata tramite uno heap binario: Inizializzazione: O(n log n) Il ciclo principale viene eseguito n volte ed ogni operazione extractMin() è O(log n) Il ciclo interno viene eseguito O(m) volte L'operazione decreaseKey() sullo heap che costa O(log n) Tempo totale: O(n+n log n + m log n)=O(m log n) asintoticamente uguale a quello di Kruskal Cosa succede se la coda con priorità è implementata tramite vettore non ordinato? © Alberto Montresor

L’arco con peso minimo è sicuro Esercizi Vero o falso L’arco con peso minimo è sicuro L’arco con il secondo peso minimo è sicuro L’arco con il terzo peso minimo è sicuro Albero di copertura minima in un piano Dati n punti nel piano La distanza euclidea fra due punti è il peso di una connessione fra di essi Trovare un insieme di connessioni di peso minimo © Alberto Montresor

Primo algoritmo: Boruvka 1926 Kruskal 1956 Conclusioni Prospettiva storica Primo algoritmo: Boruvka 1926 Kruskal 1956 Prim 1957 (ma anche Jarnik nel 1930) Utilizzando gli heap di Fibonacci Prim viene eseguito in tempo O(m + n log n) Sviluppi recenti Karger, Klein, Tarjan (algoritmo randomizzato): O(n+m) Fredman, Willard (algoritmo non basato su confronti): O(n+m) © Alberto Montresor

Semplici da programmare Molto efficienti Algoritmi greedy Vantaggi Semplici da programmare Molto efficienti Quando è possibile dimostrare la proprietà di scelta ingorda, danno la soluzione ottima La soluzione sub-ottima può essere accettabile Svantaggi Non sempre applicabili se si vuole la soluzione ottima © Alberto Montresor