Algoritmi greedy Gli algoritmi greedy in genere non
- Slides: 17
Algoritmi greedy Gli algoritmi greedy in genere non sono esatti, cioè determinano soluzioni non necessariamente ottime Per il problema dell’albero ricoprente sono esatti • Algoritmo di Prim – conserva connessione e aciclicità – tende alla ricopertura • Algoritmo di Kruskal – conserva aciclicità e ricopertura – tende verso la connessione
Algoritmo di Prim(G, c) U = {v 1}; X = While U V do e* = (u, v) = arg minu U, v VU cuv X = X {e*} U = U {v} • Parte con un solo vertice (arbitrario) e nessun arco (sottografo connesso e aciclico, ma non ricoprente) • Aggiunge ogni volta l’arco minimo del taglio individuato dall’albero corrente conserva connessione e aciclicità
Esempio di esecuzione dell’algoritmo di Prim 1 v 1 4 2 2 6 3 2 v 5 4 U = {v 1} X =Ø 4 2 6 4 4 4 2 5 1 v 1 3 v 3 U = {v 1, v 2} X = {[v 1, v 2]} v 3 5 4 2 6 v 5 3 2 v 2 2 2 v 5 v 4 1 v 2 2 6 v 3 5 v 4 v 2 1 v 1 4 v 2 2 2 v 5 5 3 v 3 U = {v 1, v 2, v 5} X = {[v 1, v 2], [v 1, v 5]} v 4
Esempio di esecuzione dell’algoritmo di Prim 1 v 1 4 2 6 4 v 5 5 v 2 2 3 v 3 U = {v 1, v 2 , v 3, v 5} X = {[v 1, v 2], [v 1, v 5], [v 3, v 5]} 4 v 4 4 2 6 2 1 v 2 2 2 v 5 3 v 3 5 U = {v 1, v 2 , v 3 , v 4, v 5} = V X = {[v 1, v 2], [v 1, v 5], [v 3, v 5], [v 4, v 5]} costo: 9
Esempio di esecuzione dell’algoritmo di Prim Altre soluzioni ammissibili (di costo non inferiore) 1 v 1 2 6 4 v 4 4 v 2 2 v 3 4 5 1 v 1 2 6 4 v 4 4 v 2 2 2 v 5 5 v 4 4 2 6 2 v 5 1 v 1 costo: 16 3 costo: 11 v 2 2 2 v 5 3 v 3 costo: 9 Altra soluzione ottima, ottenibile cambiando lato al passo 2
Complessità dell’algoritmo di Prim La complessità dipende da come si determina e* Idea 1: scandire tutti i lati: O(mn) Ogni ciclo While richiede O(m) operazioni elementari (se T(U, X) è una lista di lati più un vettore di incidenza 4 m + 4 operazioni) • 2 m per l’appartenenza al taglio [v, w] (S), [v, w] A • 4 per aggiornare X e U (incremento contatore, aggiunta estremi nella lista, cambio di bit nel vettore) • 2 m per confrontare il costo minimo con cuv ed eventualmente aggiornarlo
Una versione O(n 2) N. B. : Ad ogni passo nel taglio cambiano solo gli archi incidenti nell’ultimo vertice aggiunto a U Idea 2: per ogni vertice v fuori da U conservare il lato minimo incidente in v del taglio indotto da U (e il suo costo) Pred[v] = arg min u U cuv Cmin[v] = min u U cuv (cuv = + se [i, j] E) 4 v 1 U v 3 v 5 v U 2 6 Pred[v] = v 3 Cmin[v] = 2
Una versione O(n 2) Prim 2(G, c) For v = 1 to n do Uinc[v] = 0 Cmin[v] = + Pred[v] = 0 { U = {v 1}; X = } Uinc[1] = 1 Cmin[1] = 0 For v Adj(1) do Cmin[v] = c 1 v Pred[v] = 1 m=0 … … While m < n-1 do {U V} v. Min = 0 min = + For v = 2 to n do If (Uinc[v] = 0) and (Cmin[v] < min) min = Cmin[v] v. Min = v Uinc[v. Min] = 1 {U = U {v}} For v Adj(v. Min) do If (Uinc[v] = 0) and (cvv. Min<Cmin[v]) Cmin[v] = cvv. Min Pred[v] = v. Min
Esecuzione della versione O(n 2) Prim 2(G, c) For v = 1 to n do Uinc[v] = 0 Cmin[v] = + Pred[v] = 0 { U = {v 1}; X = } Uinc[1] = 1 Cmin[1] = 0 For v Adj(1) do Cmin[v] = c 1 v Pred[v] = 1 1 v 1 4 2 2 6 4 U = {v 1} X= Ø Uinc = [1 0 0] Cmin = [0 1 4 6 2] Pred = [1 1 1] 3 2 v 5 v 3 5 v 4 1 v 1 6 m=0 v 4 4 2 4 … v 2 2 2 v 5 5 3 v 3
Esecuzione della versione O(n 2) UU=={v{v 1}2} 1, v X =X{[v = 1Ø, v 2]} … While m < n-1 do {U V} v. Min = 0 min = + For v = 2 to n do If (Uinc[v] = 0) and (Cmin[v] < min) min = Cmin[v] v. Min = v Uinc[v. Min] = 1 {U = U {v}} For v Adj(v. Min) do If (Uinc[v] = 0) and (cuv < Cmin[v]) Cmin[v] = cuv Pred[v] = v. Min 1 v 1 2 6 4 v 4 4 v 2 2 2 v 5 3 v 3 5 Uinc = [1 01 0 0 0] Cmin = [0 1 3 6 2] Pred = [1 1 2 1 1]
Esecuzione della versione O(n 2) … While m < n-1 do {U V} v. Min = 0 min = + For v = 2 to n do If (Uinc[v] = 0) and (Cmin[v] < min) min = Cmin[v] v. Min = v Uinc[v. Min] = 1 {U = U {v}} For v Adj(v. Min) do If (Uinc[v] = 0) and (cuv < Cmin[v]) Cmin[v] = cuv Pred[v] = v. Min U = {v 1, v 2, v 5} X = {[v 1, v 2], [v 1, v 5]} 1 v 1 2 6 4 v 4 4 v 2 2 2 v 5 3 v 3 5 Uinc = [1 1 0 0 1] Cmin = [0 1 2 4 2] Pred = [1 1 5 5 1]
Esecuzione della versione O(n 2) … While m < n-1 do {U V} v. Min = 0 min = + For v = 2 to n do If (Uinc[v] = 0) and (Cmin[v] < min) min = Cmin[v] v. Min = v Uinc[v. Min] = 1 {U = U {v}} For v Adj(v. Min) do If (Uinc[v] = 0) and (cuv < Cmin[v]) Cmin[v] = cuv Pred[v] = v. Min U = {v 1, v 2 , v 3, v 5} X = {[v 1, v 2], [v 1, v 5], [v 3, v 5]} 1 v 1 4 2 6 4 v 5 v 2 2 2 3 v 3 5 Uinc = [1 1 1 0 1] Cmin = [0 1 2 4 2] Pred = [1 1 5 5 1]
Esecuzione della versione O(n 2) … While m < n-1 do {U V} v. Min = 0 min = + For v = 2 to n do If (Uinc[v] = 0) and (Cmin[v] < min) min = Cmin[v] v. Min = v Uinc[v. Min] = 1 {U = U {v}} For v Adj(v. Min) do If (Uinc[v] = 0) and (cuv < Cmin[v]) Cmin[v] = cuv Pred[v] = v. Min U = {v 1, v 2 , v 3 , v 4, v 5} X = {[v 1, v 2], [v 1, v 5], [v 3, v 5], [v 4, v 5]} 1 v 1 2 6 4 v 4 4 v 2 2 2 v 5 3 v 3 5 Uinc = [1 1 1] Cmin = [0 1 2 4 2] Pred = [1 1 5 5 1]
Complessità – Prim versione O(n 2) 1 4 2 3 For v = 1 to n do … For v Adj(1) do … 1 L’inizializzazione richiede O(n) 2 Il primo ciclo For interno richiede O(n) While m < n-1 do … For v = 2 to n do … For v Adj(v. Min) do … 3 Il secondo ciclo For interno scorre la stella uscente da v. Min L’analisi aggregata dà O(m) per l’insieme delle sue esecuzioni 4 I due cicli For interni sono eseguiti n - 1 volte O(n 2)
Una versione O(m log n) L’operazione più costosa è la ricerca del lato di costo minimo Idea 3: conserviamo gli O(n) lati candidati in uno heap • creiamo lo heap dei lati candidati (Build. Heap): O(n) in più • leggiamo direttamente l’elemento minimo: O(n) anziché O(n 2) • Aggiorniamo lo heap quando l’albero cresce di un vertice v; ogni lato incidente in v potrebbe entrare nello heap che andrebbe quindi aggiornato (Heapify): O(log n) anziché O(1) Complessivamente, esaminiamo O(m) archi
Correttezza dell’algoritmo di Prim Dato un albero ricoprente T, lato di diminuzione è un lato e T che, aggiunto a T, vi induce un ciclo C T {e} contenente un lato f C {e} di costo superiore (ce < cf ) f ce < cf C e T c(T {e}{f}) < c(T) Se esiste un lato e di diminuzione rispetto a un albero T ricoprente, si può ridurre il costo di T scambiando il lato e con almeno un lato f di C
Correttezza dell’algoritmo di Prim Per assurdo: l’albero T(V, X) restituito dall’algoritmo di Prim non sia ottimo, e lo sia invece T*(V, X*) Seguiamo l’esecuzione di Prim, arrestandoci al primo arco e X, ma X* Aggiungendo e a X* ciclo C U Sia f (U) C v h Sicuramente ce cf e f T* Se ce = cf allora T* {e} / {f} è ottimo perché costa come T* Se ce < cf, e è un lato di diminuzione quindi T* non è ottimo!
- Mai fare agli altri ciò che non vorresti fosse fatto te
- Non fidarti di chi non chiude gli occhi quando ti bacia
- Veux-tu briser du péché le pouvoir parole
- Poema epico classico
- Proporzione leve
- Leva di primo genere
- Genere narrativo spagnolo del xvi secolo
- Genere transitivo
- I promessi sposi generi
- What type of genre is stranger things
- Il moto armonico di una molla
- Che cos'è l'epica classica
- Leve di terzo genere canna da pesca
- Seesaw
- Leva di primo genere
- Trattatistica genere letterario
- Farenheit 451 genre
- Genero epico