Algoritmi greedy
Un algoritmo greedy è un algoritmo iterativo che effettua ad ogni passo la miglior scelta locale per arrivare alla miglior soluzione. Per esempio, gli algoritmi greedy già visti hanno come scelta migliore:
- Kruskal, gli archi più leggeri
- Prim, il nodo con peso entrante più piccolo
- Dijkstra, i nodi con distanza minore
Questi algoritmi seguono una struttura generale che può essere modellata come:
greedy(X)
sort(X) // Ordinamento dei dati secondo un criterio greedy
A = {} // Inizializzazione di strutture dati
for each x in X // Estrazione ordinata degli elementi
if ok(union(A, {x})) // Scelta locale
A = union(A, {x})
return A
Problema della selezione delle attività
Un altro esempio di algoritmo greedy è quello per cui si vuole il massimo numero di attività che sono tra loro compatibili ovvero, data l'ora di inizio e l'ora di fine:
In questo caso il criterio greedy consiste nell'ordinare in base al tempo :
greedy_activity_selector(s, f)
n = s.length = f.length
sort(s, f) // Ordina i due array in base ad f
A = {1} // Contiene per prime quelle che finiscono prima
j = 1 // Ultima attività inserita
for i = 2 to n
if s[i] >= f[j] // Controlla che i sia compatibile con l'ultima attività j
A = union(A, {i})
j = i
return A
che ha complessità per il sort
.
Altri criteri potevano non funzionare, per esempio se l'ordinamento fosse stato per:
- durata, un'attività corta scelta prima che ne coinvolge due più grandi può renderle incompatibili
- tempo d'inizio, un'attività che comincia prima potrebbe finire dopo le altre rendendole incompatibili
Problema della clique massima
Un esempio in cui l'approccio greedy fallisce è quello per trovare la clique massima di un grafo :
greedy_clique(G)
sort(G.V) // Ordinamento decrescente dei nodi in base al loro grado
A = {}
for each u in G.V
if is_a_clique(G, A, u)
A = union(A, {u})
return A
is_a_clique(G, A, u)
for each v in A
if not contains(G.E, (u, v))
return false
return true
che avrebbe complessità dato che is_a_clique
fa al più iterazioni.
Un esempio su cui fallisce è il grafo
infatti restituirebbe {1, 5}
invece che {5, 6, 7}
, perchè dopo 1
e 5
non potrebbe aggiungere altri nodi.
Anche se i nodi fossero stati ordinati in base ai triangoli adiacenti, esiste un grafo in cui fallisce:
perchè anche in questo caso restituirebbe {5, 1, 2}
invece che {5, 6, 7, 8}
.