Scaricare la presentazione
La presentazione è in caricamento. Aspetta per favore
PubblicatoFino Poggi Modificato 11 anni fa
1
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
2
Algoritmo di Prim Prim(G,c) U = {v1}; X = While U V do e* = (u,v) = arg minu U, v V\U 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à
3
Esempio di esecuzione dell’algoritmo di Prim
U = {v1} X =Ø 1 v1 v5 v2 v3 v4 2 6 5 3 4 1 v1 v5 v2 v3 v4 2 6 5 3 4 v4 1 v1 v5 v2 v3 2 6 5 3 4 U = {v1, v2, v5} X = {[v1,v2], [v1,v5]} U = {v1, v2} X = {[v1,v2]} 1 v1 v5 v2 v3 v4 2 6 5 3 4
4
Esempio di esecuzione dell’algoritmo di Prim
1 v1 v5 v2 v3 v4 2 6 5 3 4 U = {v1, v2 , v3, v5} X = {[v1,v2], [v1,v5], [v3,v5]} costo: 9 1 v1 v5 v2 v3 v4 2 6 5 3 4 U = {v1, v2 , v3 , v4, v5} = V X = {[v1,v2], [v1,v5], [v3,v5], [v4,v5]}
5
Esempio di esecuzione dell’algoritmo di Prim
Altre soluzioni ammissibili (di costo non inferiore) costo: 11 1 v1 v2 1 v1 v5 v2 v3 v4 2 6 5 3 4 4 costo: 16 2 3 2 6 2 v5 v3 4 5 v4 1 v1 v5 v2 v3 v4 2 6 5 3 4 Altra soluzione ottima, ottenibile cambiando lato al passo 2 costo: 9
6
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 4m + 4 operazioni) 4 per aggiornare X e U (incremento contatore, aggiunta estremi nella lista, cambio di bit nel vettore) 2m per l’appartenenza al taglio [v,w] (S), [v,w] A 2m per confrontare il costo minimo con cuv ed eventualmente aggiornarlo
7
Una versione O(n2) Pred[v] = arg min uU cuv
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 uU cuv Cmin[v] = min uU cuv (cuv = + se [i,j] E) 4 vU v1 2 v3 Pred[v] = v3 6 Cmin[v] = 2 v5 U
8
Una versione O(n2) Prim2(G,c) For v = 1 to n do Uinc[v] = Cmin[v] = + Pred[v] = 0 { U = {v1}; X = } Uinc[1] = 1 Cmin[1] = 0 For v Adj(1) do Cmin[v] = c1v Pred[v] = 1 m = 0 … … While m < n-1 do {U V} vMin = min = + For v = 2 to n do If (Uinc[v] = 0) and (Cmin[v] < min) min = Cmin[v] vMin = v Uinc[vMin] = 1 {U = U {v}} For v Adj(vMin) do If (Uinc[v] = 0) and (cvvMin<Cmin[v]) Cmin[v] = cvvMin Pred[v] = vMin
9
Esecuzione della versione O(n2)
1 v1 v5 v2 v3 v4 2 6 5 3 4 Prim2(G,c) For v = 1 to n do Uinc[v] = Cmin[v] = + Pred[v] = 0 { U = {v1}; X = } Uinc[1] = 1 Cmin[1] = 0 For v Adj(1) do Cmin[v] = c1v Pred[v] = 1 m = 0 … U = {v1} X = Ø 1 1 v1 v2 Uinc = [ ] Cmin = [ ] Pred = [ ] 4 2 3 2 6 v5 2 v3 4 5 v4
10
Esecuzione della versione O(n2)
1 v2 U = {v1,v2} X = {[v1,v2]} U = {v1} … While m < n-1 do {U V} vMin = min = + For v = 2 to n do If (Uinc[v] = 0) and (Cmin[v] < min) min = Cmin[v] vMin = v Uinc[vMin] = 1 {U = U {v}} For v Adj(vMin) do If (Uinc[v] = 0) and (cuv < Cmin[v]) Cmin[v] = cuv Pred[v] = vMin X = Ø v3 1 v1 v5 v2 v4 2 6 5 3 4 Uinc = [ ] Cmin = [ ] Pred = [ ]
11
Esecuzione della versione O(n2)
U = {v1, v2, v5} … While m < n-1 do {U V} vMin = min = + For v = 2 to n do If (Uinc[v] = 0) and (Cmin[v] < min) min = Cmin[v] vMin = v Uinc[vMin] = 1 {U = U {v}} For v Adj(vMin) do If (Uinc[v] = 0) and (cuv < Cmin[v]) Cmin[v] = cuv Pred[v] = vMin X = {[v1,v2], [v1,v5]} v4 1 v1 v5 v2 v3 2 6 5 3 4 Uinc = [ ] Cmin = [ ] Pred = [ ]
12
Esecuzione della versione O(n2)
U = {v1, v2 , v3, v5} X = {[v1,v2], [v1,v5], [v3,v5]} … While m < n-1 do {U V} vMin = min = + For v = 2 to n do If (Uinc[v] = 0) and (Cmin[v] < min) min = Cmin[v] vMin = v Uinc[vMin] = 1 {U = U {v}} For v Adj(vMin) do If (Uinc[v] = 0) and (cuv < Cmin[v]) Cmin[v] = cuv Pred[v] = vMin 1 v1 v2 4 2 3 2 6 v5 2 v3 4 v4 5 Uinc = [ ] Cmin = [ ] Pred = [ ]
13
Esecuzione della versione O(n2)
U = {v1, v2 , v3 , v4, v5} … While m < n-1 do {U V} vMin = min = + For v = 2 to n do If (Uinc[v] = 0) and (Cmin[v] < min) min = Cmin[v] vMin = v Uinc[vMin] = 1 {U = U {v}} For v Adj(vMin) do If (Uinc[v] = 0) and (cuv < Cmin[v]) Cmin[v] = cuv Pred[v] = vMin X = {[v1,v2], [v1,v5], [v3,v5], [v4,v5]} 1 v1 v2 4 2 3 2 6 2 v5 v3 4 5 v4 Uinc = [ ] Cmin = [ ] Pred = [ ]
14
Complessità – Prim versione O(n2)
For v = 1 to n do … For v Adj(1) do … While m < n-1 do … For v = 2 to n do … For v Adj(vMin) do … 1 L’inizializzazione richiede O(n) 1 Il primo ciclo For interno richiede O(n) 2 3 Il secondo ciclo For interno scorre la stella uscente da vMin L’analisi aggregata dà O(m) per l’insieme delle sue esecuzioni 4 2 3 4 I due cicli For interni sono eseguiti n - 1 volte O(n2)
15
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 (BuildHeap): O(n) in più leggiamo direttamente l’elemento minimo: O(n) anziché O(n2) 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
16
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 T e C 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
17
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 Sicuramente ce cf T* v h e Se ce = cf allora T* {e} / {f} è ottimo perché costa come T* f Se ce < cf, e è un lato di diminuzione quindi T* non è ottimo!
Presentazioni simili
© 2024 SlidePlayer.it Inc.
All rights reserved.