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
1
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
2
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
3
Insieme indipendente di intervalli
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:
✦
i
ai bi
1 1 4
2 3 5
3 0 6
4 5 7
5 3 8
S={ 1, 2,…, n } un insieme di intervalli
6 5 9
Ad ogni intervallo i in S sono associati:
7 6 10
✦
✦
ai → tempo di inizio
8 8 11
bi → tempo di fine
9 8 12
✦
✦
Che relazione c’è con “Insieme indipendente di intervalli pesati”?
✦
© Alberto Montresor
10 12 14
4
Attività
1
2
3
4
5
6
7
8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
5
Attività
1
2
3
4
5
6
7
8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
6
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
7
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
bi ak
bk aj
✦n+1
→ an+1 = +∞
✦
Così S[0, n+1] corrisponde al problema completo S
✦
© Alberto Montresor
8
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
9
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
10
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
11
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
12
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
13
i=0
1
2
3
4
5
6
7 Attività
8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
14
i=1
2
3
4
5
6
7 Attività
8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
15
i=1
2
3
4
5
6
7 Attività
8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
16
i=1
2
3
4
5
6
7 Attività
8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
17
1
2
3
i=4
5
6
7 Attività
8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
18
1
2
3
i=4
5
6
7 Attività
8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
19
1
2
3
i=4
5
6
7 Attività
8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
20
1
2
3
i=4
5
6
7 Attività
8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
21
1
2
3
4
5
6
7 Attività
i=8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
22
1
2
3
4
5
6
7 Attività
i=8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
23
1
2
3
4
5
6
7 Attività
i=8
9
10
11
Tempo
0
© Alberto Montresor
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
24
1
2
3
4
5
6
7 Attività
8
9
10
i=11
0
© Alberto Montresor
Tempo
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15
25
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
26
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
27
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
✦
Domanda
✦
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?
✦
Domanda
✦
Trovare un insieme di “pezzature” per cui la scelta golosa non è applicabile
✦
© Alberto Montresor
28
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
29
Algoritmo di scheduling
Definizione:
4
✦
1 processore, n job p1, p2, ..., pn
✦
1
Ogni job pi ha un tempo di esecuzione t[i]
✦
6
Minimizzare il tempo di completamento medio
✦
3
Domanda: Risolvere il problema tramite la tecnica greedy
✦
Shortest job first
4
1
0
4
1
0 1
© Alberto Montresor
3
6
5
11
4
4
(4+5+11+14)/4 = 34/4
3
14
(1+4+8+14)/4 = 27/4
6
8
14
30
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
31
p[]
€60
v[]
10
€125
25
€120
C
30
55
Scegli per primo l’oggetto che ha profitto più alto
25
© Alberto Montresor
30
€125 + €120 = €245
32
p[]
€60
v[]
10
€125
25
€120
C
30
55
Scegli per primo l’oggetto che ha profitto più alto
25
30
€125 + €120 = €245
Scegli per primo l’oggetto che ha volume più alto
30
© Alberto Montresor
25
€120 + €125 = €245
33
p[]
v[]
€60
€125
€5 per litro
25
€120
C
€6 per litro
10
€4 per litro
30
55
Scegli per primo l’oggetto che ha profitto più alto
25
€125 + €120 = €245
30
Scegli per primo l’oggetto che ha volume più alto
30
25
€120 + €125 = €245
Scegli per primo l’oggetto che ha il più alto “profitto specifico”
10
25
20/30
€60 + €120 + €80 = €260
© Alberto Montresor
34
Problema dello zaino
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
35
Zaino reale
Complessità
✦
O(n log n) per l’ordinamento
✦
O(n) per la scelta dei valori
✦
© Alberto Montresor
36
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
37
Codici di Huffman
Supponiamo di avere un file di n caratteri
✦
Composto dai caratteri:
d
e
a
f
Con le seguenti frequenze: 45%
13%
✦
✦
b
12%
16%
c
9%
5%
Codifica tramite ASCII (8 bit per carattere)
✦
Dimensione totale: 8n bit
✦
Codifica basata sull'alfabeto (3 bit per carattere)
✦
Codifica:
011
100
000
✦
001
010
101
Dimensione totale: 3n bit
✦
Possiamo fare di meglio?
✦
© Alberto Montresor
38
Codici di Huffman
Codifica a lunghezza variabile
✦
Caratteri: a
f
b
✦
c
0
Costo totale:
(0.45⋅1+0.13⋅3+0.12⋅3+0.16⋅3+0.09⋅4+0.05⋅4)⋅n=2.24n
✦
100
e
Codifica:
1101 1100
✦
101
d
111
Codice “a prefisso” (meglio, “senza prefissi”):
✦
Nessun codice è prefisso di un altro codice
✦
Condizione necessaria per permettere la decodifica Considerate questo codice:
f(a) = 1, f(b)=10, f(c)=11
✦Esempio: ADDAABCA
✦
0·111·111·0·0·101·100·0
✦
© Alberto Montresor
39
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
40
Rappresentazione ad albero per la decodifica
Algoritmo di decodifica:
Rappresentazione come alberi binari
✦
Figlio sinistro: 0
✦
Figlio destro: 1
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
Caratteri dell'alfabeto sulle foglie
✦
1
0
0
1
0
a
© Alberto Montresor
0
1
0
1
b
c
d
e
a: 00
b: 010
c: 011
d: 100
e: 101
41
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
0
0
1
0
Spazio sprecato
a
© Alberto Montresor
0
1
0
1
b
c
d
e
a: 00
b: 010
c: 011
d: 100
e: 101
42
Rappresentazione ad albero per la decodifica
a: 00
b: 010
c: 011
d: 100
e: 101
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
0
0
1
a
© Alberto Montresor
0
1
b
c
0
1
d
e
In questo albero
interno ha due figli
ogni
nodo
a: 00
b: 010
c: 011
d: 10
e: 11
43
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
✦
Nota:
✦
I codici a prefisso ottimi sono rappresentati da un albero in cui tutti i nodi interni
hanno due figli
✦
© Alberto Montresor
44
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
45
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
46
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
47
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
48
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
49
Costruzione del codice
Ripetere i passi 2-4 fino a quando non resta un solo nodo
nella lista
14
f: 5 e: 9
© Alberto Montresor
d : 16
25
a : 45
c : 12 b : 13
50
Costruzione del codice
Ripetere i passi 2-4 fino a quando non resta un solo nodo
nella lista
25
c : 12
30
b : 13
14
a : 45
d : 16
f: 5 e: 9
© Alberto Montresor
51
Costruzione del codice
Ripetere i passi 2-4 fino a quando non resta un solo nodo
nella lista
55
a : 45
30
25
c : 12
b : 13
14
d : 16
f: 5 e: 9
© Alberto Montresor
52
Costruzione del codice
Al termine, si etichettano gli archi dell'albero con bit 0-1
0
100
a
1
0
0
c
25
55
1
0
b
14
0
f
© Alberto Montresor
1
30
1
1
d
e
53
Algoritmo in pseudo-codice
Tree:
f
c
left
right
// frequenza (key)
// carattere
// figlio sinistro
// figlio destro
Complessità
θ(n log n)
© Alberto Montresor
54
Dimostrazione di correttezza
Teorema: L'output dell'algoritmo Huffman per un dato file è un codice a prefisso
ottimo
✦
Schema della dimostrazione:
✦
Proprietà della scelta greedy
✦Scegliere i due elementi con la frequenza più bassa conduce sempre ad una
soluzione ottimale
✦
Sottostruttura ottima
✦
Dato un problema sull'alfabeto Σ, è possibile costruire un sottoproblema con un
alfabeto più piccolo
✦
© Alberto Montresor
55
Scelta greedy
Sia
✦
Σ un alfabeto, f un vettore 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 (sono foglie sorelle)
✦
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
56
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
x
T'
a
a
...
...
...
y
a
© Alberto Montresor
b
T"
y
x
b
b
x
y
57
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
58
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
59
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]
✦
da cui concludiamo
✦
C(f, T) = C(f, O) + f[x] + f[y]
C(f, O) = C(f, T) - f[x] - f[y]
✦
© Alberto Montresor
60
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
(proprietà della scelta greedy)
✦
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
61
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
62
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
63
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
T
✦E ⊆ E
✦T contiene tutti i vertici di G
✦
8
7
b
c
4
2
a 11 7
i
8
h
9
14
4
g
1
© Alberto Montresor
6
d
f
e
10
2
64
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
b
c
4
d
i
7
6
8
h
g
1
© Alberto Montresor
f
2
10
8
d
9
2
11
a
7
c
4
e
14
4
b
9
2
11
a
8
i
7
h
6
g
1
14
4
f
e
10
2
65
Differenze con cammini minimi
Calcolare
✦
un albero dei cammini minimi da singola sorgente a
✦
un albero di copertura minima
✦
coincidono?
✦
a
2
b
2
1
d
c
1
© Alberto Montresor
66
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
67
Algoritmo generico
© Alberto Montresor
68
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
69
Esempio
Arco leggero che
attraversa il taglio
8
7
b
c
4
9
2
11
a
d
i
7
6
14
4
8
h
g
1
f
2
10
e
V
Taglio
V-S
Insieme A: archi in grigio
Il taglio rispetta A
© Alberto Montresor
70
Archi sicuri
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
71
Esempio: arco non sicuro perché il taglio non rispetta A
4
8
4
b
11
a
8
8
2
i
7
h
1
7
c
6
g
d
14
4
2
9
f
10
b
11
a
8
2
i
7
h
6
d
9
e
14
4
f
2
10
Arco sicuro
e
4
8
b
11
a
8
h
2
7
i
1
Arco non sicuro
© Alberto Montresor
c
g
1
7
7
c
6
g
d
14
4
2
9
f
e
10
72
Esempio: arco non sicuro perché non leggero
8
b
c
4
8
8
b
c
4
8
i
7
h
6
d
g
1
6
10
9
e
14
4
g
f
10
2
Arco sicuro
e
8
b
f
2
h
9
14
4
i
7
1
2
11
a
7
d
2
11
a
7
c
4
8
d
9
2
11
a
7
i
7
h
6
4
g
1
14
f
e
10
2
Arco non sicuro
© Alberto Montresor
73
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
74
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
75
Algoritmo di Kruskal
© Alberto Montresor
76
Visualizzazione
8
7
b
c
4
8
d
i
7
h
6
4
g
1
f
b
8
i
h
h
g
f
e
14
f
10
8
d
9
2
11
a
10
7
c
4
e
2
b
9
9
14
4
8
4
2
6
1
d
g
1
© Alberto Montresor
6
i
7
7
c
7
8
10
2
11
a
11
a
d
2
2
8
4
c
4
e
14
7
b
9
2
11
a
8
i
7
h
6
g
1
e
14
4
f
10
2
77
Visualizzazione
8
7
b
c
4
8
d
i
7
h
6
4
g
1
f
b
8
i
7
h
f
10
8
10
f
7
c
d
9
2
11
a
e
2
4
e
14
4
2
4
g
b
9
9
14
8
d
g
1
© Alberto Montresor
6
6
h
7
c
i
7
1
2
11
a
11
a
8
10
d
2
2
8
4
c
4
e
14
7
b
9
2
11
a
8
i
7
h
6
g
1
e
14
4
f
10
2
78
Visualizzazione
8
b
c
4
2
a 11 7
i
8
h
6
d
g
f
b
4
2
a 11 7
i
8
10
2
a 11 7
i
h
6
g
1
f
b
e
14
4
2
f
10
2
a 11 7
i
8
h
6
d
9
e
14
4
g
1
10
7
c
4
e
2
8
9
9
14
4
1
d
g
d
6
h
7
c
4
© Alberto Montresor
e
c
2
8
7
b
9
14
4
1
8
8
7
f
10
2
79
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
80
Algoritmo di Prim
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
81
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
82
Algoritmo di Prim
© Alberto Montresor
83
Algoritmo di Prim: Esempio
4
b
8
c
4
8
d
i
7
h
8
6
g
1
f
b
8
7
h
8
© Alberto Montresor
i
6
7
d
h
8
2
f
4
9
6
4
f
1
2
8
7
c
8
i
7
h
7
6
7
d
1
9
2
e
10
f
6
10
14
4
g
e
14
2
11
a
d
g
4
e
10
i
7
b
9
14
4
g
1
8
10
2
11
a
7
c
2
11
a
7
2
2
8
4
4
e
14
4
b
9
2
11
a
8
c
8
7
4
84
Algoritmo di Prim: Esempio
8
7
b
c
4
2
a 11 7
i
8
h
7
d
6
1
7
h
7
d
i
f
2
h
6
e
10
2
a 11 7
i
h
1
10
f
7
c
4
8
10
e
2
b
9
9
14
4
g
1
7
d
8
14
4
g
1
© Alberto Montresor
6
a 11 7
8
10
i
2
1
2
a 11 7
8
10
7
c
4
2
c
4
e
10
f
8
b
b
9
14
4
g
2
8
7
6
g6
d
9
e
14
4
f
9
10
2
85
Algoritmo di Prim: Esempio
8
b
c
4
2
a 11 7
i
8
h
6
d
9
e
14
4
g
1
© Alberto Montresor
7
f
10
2
86
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
87
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
88
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
89
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
90