Grafi Definizioni/1 • Struttura dati per la rappresentazione di relazioni binarie • G=(V,E), |V|=n, |E|=m • V: insieme di Vertici o Nodi • E={{vi, vj}: vi, vj V} : insieme di Spigoli – {vi, vj} = {vj, vi} Grafo semplice o non orientato • E={(vi, vj): vi, vj V} : insieme di Archi – (vi, vj) (vj, vi) Grafo diretto o orientato • spesso i termini spigolo ed arco vengono usati come sinonimi giugno 2002 ASD - Grafi 2 Esempi • Relazioni di parentela – Alberi genealogici • • • • • Relazioni tra classi nei linguaggi OO Grafo del Web Assetti societari Reti di trasporto ................ giugno 2002 ASD - Grafi 3 Definizioni/2 • Multigrafo: E è un multinsieme • Pseudografo: E contiene anche coppie (vi, vi) cappi • Circuito in un grafo: v1, v2,….., vk: (vi, vi+1) E, v1= vk (senza archi ripetuti) • Ciclo in un grafo: Circuito con vi vj, per i j • Grafo pesato: valore reale wk associato ad ogni arco ek giugno 2002 ASD - Grafi 4 Definizioni/3 • Kn: Grafo semplice in cui sono presenti tutti gli archi. n 1 n (n 1) – numero di archi in Kn : (n i ) i 1 2 • G’=(V’,E’) sottografo di G=(V,E) se e solo se V’V ed E’ E • grado(v): # di archi incidenti in v • (vi, vj) E: vi adiacente a vj giugno 2002 ASD - Grafi 5 Esempi di grafi: (a-d) grafi semplici; (c) un grafo completo K4; (e) un multigrafo; (f) uno pseudografo; (g) un circuito in un grafo orientato; (h) un ciclo nel grafo orientato giugno 2002 ASD - Grafi 6 Rappresentazioni • Lista di adiacenza: ogni vertice è associato con la lista dei vertici adiacenti. • Lista di adiacenza può essere una tabella o una lista concatenata • Matrice di adiacenza: aih=1 se (vi, vh) E, aih=0 altrimenti • Matrice di Incidenza: aih=1 se vi eh, aih=0 altrimenti giugno 2002 ASD - Grafi 7 Rappresentazioni di grafi. Un grafo (a) rappresentato con una lista di adiacenze (b-c), giugno 2002 ASD - Grafi 8 Rappresentazioni di grafi. Un grafo (a) rappresentato come una matrice di adiacenze (d) e come una matrice d’incidenza (e) giugno 2002 ASD - Grafi 9 Vantaggi e Svantaggi • Lista di adiacenza: memoria O(m) Vantaggi: permette di scorrere i nodi adiacenti a v in O(grado(v)) Svantaggi: inserimenti e cancellazioni su liste concatenate in O(grado(v)) • Matrice di adiacenza: memoria O(n2) Vantaggi: Inserimenti e cancellazioni in O(1) Svantaggi: permette di scorrere i nodi adiacenti a v in O(n) • D.: matrice di incidenza ? giugno 2002 ASD - Grafi 10 Visita di un Grafo • Obiettivo: visitare una sola volta tutti i nodi del grafo. – • Es.: visitare un porzione del grafo del Web Difficoltà: – – Presenza di cicli: marcare i nodi visitati Presenza di nodi isolati: la visita termina quando sono state considerate tutte le componenti isolate del grafo giugno 2002 ASD - Grafi 11 Visita in profondità - DFS • La visita procede da tutti gli archi uscenti da un nodo. • Se tutti i nodi adiacenti sono stati visitati allora si torna al nodo “predecessore”. • Una volta tornati al nodo di partenza si prosegue da un nodo qualsiasi non visitato. • I nodi vengono rinumerati secondo l’ordine di visita. giugno 2002 ASD - Grafi 12 Esempio di applicazione dell’algoritmo depthFirstSearch ad un grafo giugno 2002 ASD - Grafi 13 L’algoritmo depthFirstSearch applicato ad un grafo orientato giugno 2002 ASD - Grafi 14 Implementazione della DFS/1 • I nodi sono inizialmente marcati con 0, i=1. • Assumi la visita sia arrivata ad un nodo v. • La visita prosegue da un nodo u adiacente a v se marcato 0. • Se nessun nodo adiacente marcato 0 è disponibile torna al nodo da cui si è raggiunto v oppure termina se v è il nodo iniziale. • Ogni volta che un nodo mai visitato è raggiunto, questo viene marcato con i++ • Viene marcato sia l’nizio che la fine della visita di un nodo v, risp. num(v) e fin(v) giugno 2002 ASD - Grafi 15 Implementazione della DFS/2 depthFirstSearch() { for (tutti i vertici v) num(v)=fin(v)=0; // Vedi slide succ. edges = null; i=j=1; // per aggiornare num(v) e fin(v) while (<esiste v tale che num(v)==0>)//main loop DFS(v); <visualizza edges> } giugno 2002 ASD - Grafi 16 Implementazione della DFS/3 DFS(v) { num(v)=i++; //prima visita di v for (<tutti i vertici u adiacenti a v>) if (num(u) == 0) { <inserisci lato (v,u) in edges> DFS(u); } fin(v)=j++; // ultima visita di v } giugno 2002 ASD - Grafi 17 implementazione della DFS/4 • l’implementazione iterativa della DFS utilizza una pila per memorizzare gli archi uscenti da un nodo visitato • ad ogni passo si estrae l’arco sulla cima della pila • la visita prosegue dal nodo adiacente se marcato 0 giugno 2002 ASD - Grafi 18 Proprietà della DFS • l’algoritmo DFS visita l’intera componente del grafo raggiungibile dal nodo di partenza • se collezioniamo gli archi (edges) che portano alla scoperta di nuovi nodi, otteniamo una collezione di alberi che coprono l’intero grafo – dipende dal fatto che un arco viene seguito solo se il nodo adiacente non è mai stato raggiunto. • gli archi seguiti connettono un nodo con marca inferiore ad un nodo con marca superiore (forward edges) • gli archi che non vengono seguiti al contrario connettono nodi con marca superiore a nodi con marca inferiore (back edges) giugno 2002 ASD - Grafi 19 Complessità della DFS • O(n) per inizializzare marcatura dei nodi • Test degli archi uscenti da un nodo v: – O(grado(v)) nella rappresentazione con lista di adiacenza. – O(n) nella rappresentazione con matrice di adiacenza. • Ogni arco viene testato al più due volte, una volta per ogni estremo • Complessivamente O(n + m), O(n2) (grafo denso) giugno 2002 ASD - Grafi 20 Visita in ampiezza - BFS • La visita in ampiezza fa uso di una coda per memorizzare tutti gli archi incidenti nel nodo v visitato che portano ad un nodo marcato 0 • I nodi raggiungibili marcati 0 vengono quindi marcati • La visita procede dall’arco (v,u) in testa alla coda giugno 2002 ASD - Grafi 21 implementazione della BFS breadthFirstSearch() { for (tutti i vertici v) num(v)=0; edges = null; i = 1; while (<esiste v tale che num(v)==0>) { num(v) = i++; enqueue(v); while (<la coda non è vuota>) { v = dequeue(); giugno 2002 ASD - Grafi 22 implementazione della BFS/2 for(<tutti i vertici u adiacenti a v>) if (num(u) == 0) { num(u) = i++; enqueue(u); <inserisci arco (v,u) in edges> } } } <visualizza edges> } giugno 2002 ASD - Grafi 23 Un esempio di applicazione dell’algoritmo breadthFirstSearch ad un grafo giugno 2002 ASD - Grafi 24 Applicazione dell’algoritmo breadthFirstSearch ad un grafo orientato giugno 2002 ASD - Grafi 25 Ordinamento Topologico/1 • Grafi diretti aciclici (DAG) possono rappresentare ordinamenti parziali. • Ordinamento parziale: i,j,k, se i j e j k allora i k • Ogni elemento i è rappresentato da un nodo vi • i<j sse esiste un cammino diretto da vi a vj. • Se esiste un ciclo non è possibile rappresentare un ordine parziale. Perché? giugno 2002 ASD - Grafi 26 Ordinamento parziale • Possono esistere coppie tra le quali non è definito alcun ordine – Es.: classi sorelle in linuaggi OO • Modellano molte situazioni di interesse pratico – Ereditarietà tra classi Java – Vincoli di precedenza in progetti complessi giugno 2002 ASD - Grafi 27 Ordinamento Topologico/2 • Determinare un ordinamento dei vertici vp(1), vp(2),….., vp(n) tale che se esiste un cammino da vp(i) a vp(j) allora p(i)>p(j). • L’ordinamento topologico secondo la relazione < è ottenuto considerando la sequenza in ordine inverso • Per determinare l’ordinamento topologico occorre che ogni nodo nell’ordine sia seguito da tutti i suoi predecessori. • Un vertice pozzo è un vertice che non ha archi uscenti. In un DAG esiste sempre giugno 2002 vertice. Perché? ASD - Grafi tale 28 Ordinamento Topologico/3 TopologicalSort() { for i = 1 to n { <trova un vertice pozzo v> num(v)=i; <elimina dal DAG tutti gli archi incidenti in v> } } giugno 2002 ASD - Grafi 29 Ordinamento topologico: g,e,b,f,d,c,a giugno 2002 ASD - Grafi 30 Ordinamento Topologico/4 • In pratica un ordinamento topologico si ottiene se nella sequenza ogni nodo è seguito dai suoi predecessori. • Si esegue una DFS e si ordinano i vertici secondo il valore fin(v). • Il valore fin(v) è inferiore a quello dei suoi predecessori. • L’ordinamento topologico si ottiene dalla sequenza ordinata secondo fin(v) scandita in ordine inverso. Come si dimostra? giugno 2002 ASD - Grafi 31 Ordinamento Topologico/5 TS(v) num(v)=i++; per tutti i vertici u adiacenti a v if (num(u) == 0) TS(u); else if (fin(u)==0) errore; // identificato un ciclo /* dopo avere esaminato tutti i predecessori, assegna a v un numero maggiore di quelli assegnati a qualsiasi predecessore */ fin(v) = j++; giugno 2002 ASD - Grafi 32 Ordinamento Topologico/6 topologicalSorting(digraph) per tutti i vertici v num(v)= fin(v) = 0; i = j = 1; while (esiste v tale che num(v) == 0) TS (v); Visualizza vertici in ordine inverso secondo fin(v); giugno 2002 ASD - Grafi 33 Connettività in Grafi diretti • Due nodi u,v sono connessi in un grafo orientato se esiste un cammino diretto che collega u a v. • Un grafo diretto è fortemente connesso se per ogni coppia u,v, esiste un cammino da u a v e da v ad u. • Un grafo è debolmente connesso se ogni coppia di nodi è connessa da un cammino quando gli archi orientati si sostituiscono con gli archi non orientati. giugno 2002 ASD - Grafi 34 Componenti fortemente connesse SCC/1 • Un grafo diretto può essere decomposto in componenti fortemente connesse, V1 , V2 ,… , Vk, tale che – V V1 V2 .... Vk – u, v V j u connesso a v e v connesso ad u – Vj è un insieme massimale • GT=(V, ET) giugno 2002 ET : se (u ,v ) E (v ,u ) E T ASD - Grafi 35 SCC / 2 Strongly ConnectedComponent(G) Esegui DFS(G) per calcolare fin(v) per ogni vertice v; Calcola GT ; Calcola DFS(GT) considerando i nodi nel Main Loop in ordine decrescente di fin(v) ; Output ogni albero di DFS(GT ) come una componente fortemente connessa separata • Complessità: O(m+nlog n). DFS più ordinamento in ordine decrescente rispetto a fin. giugno 2002 ASD - Grafi 36 G GT a b c d a b c d 8/6 6/8 1/5 5/4 2 1 4 5 h e f 4/2 3 7 e 7/7 f g 3/1 2/3 num/fin g h 6 8 num Radici Alberi DFS: b, c, g, h SCC: {a,b,e} {c,d} {f,g} {h} Esempio di esecuzione dell’algoritmo per SCC giugno 2002 ASD - Grafi 37 Il Problema dei Cammini Minimi • G=(V,E) è un grafo pesato sugli archi • d(u,v), (u,v) E: peso sull’arco (u,v) • Cammino dal nodo s al nodo t: v1, v2,….., vk: (vi, vi+1) E, v1= s, vk=t k 1 • Lunghezza del cammino: d (vi , vi 1 ) i 1 • Il cammino di lunghezza minima non contiene cicli ……se le distanze sugli archi sono positive. Come si dimostra? giugno 2002 ASD - Grafi 38 Il problema dei Cammini Minimi/2 • Determinare il cammino di lunghezza minima – dal nodo s al nodo t – dal nodo s a tutti gli altri nodi V (SSSP) – tra tutte le coppie di nodi del grafo (APSP) • Numerose applicazioni: reti stradali, reti di comunicazione, scheduling di progetti, progetto di circuiti,…. giugno 2002 ASD - Grafi 39 Single Source Shortest Paths/1 • Consideriamo un grafo pesato con pesi non negativi. • Determinare il cammino minimo da un nodo s a tutti i nodi V del grafo • Ogni sottocammino di un cammino minimo è esso stesso un cammino minimo. • Ex: s,…,i,…j,…,v: cammino minimo da s a v i,…,j è un cammino minimo da i a j. Come si dimostra? giugno 2002 ASD - Grafi 40 Single Source Shortest Paths/2 • La collezione dei cammini minimi da s a tutti i nodi V può essere sempre posta nella forma di un albero. Come si dimostra? • Algoritmi per SSSP mantengono ad ogni istante delle etichette sui nodi. • Etichette rappresentano delle approssimazioni delle distanze dalla sorgente. • Vi sono algoritmi che ad ogni passo fissano alcune etichette ai loro valori finali, ex Dijkstra. • Altri algoritmi, ex: Bellmann & Ford, possono modificare tutte le etichette lungo l’intera esecuzione dell’algoritmo. giugno 2002 ASD - Grafi 41 Dijkstra/1 1. Due insiemi di nodi Q ed R. 2. Inizialmente Q= {}, R={1,..,n} 3. v R, v s, dist (v) , dist ( s ) 0 4. Ad ogni passo estrai il nodo v in R con min dist(v) ed inserisci v in Q 5. Per ogni u adiacente a v aggiorna la distanza da s ad u attraverso nodi in Q: if dist (u ) dist (v ) d (v, u ) dist (u ) dist (v ) d (v, u ) pred(u) v giugno 2002 ASD - Grafi 42 Un’esecuzione di DijkstraAlgorithm giugno 2002 ASD - Grafi 43 Dijkstra/2 • Ad ogni passo si determina la distanza minima di un nodo v in R. Il nodo viene inserito in Q. • Dijkstra termina in n passi. • Ad ogni passo occorre determinare il nodo v in R con minimo valore dist(v), O(log n) usando un heap per la coda di priorità. • Occorre poi eseguire il rilassamento per ogni adiacente u di v, O(grado(u)) vertici, ed eventualmente aggiornare la priorità. Complessivamente O(m log n) • Complessità di Dijkstra O((n + m )log n). giugno 2002 ASD - Grafi 44 Dijkstra/3 • Correttezza: Dimostrare che dist(v) è la distanza minima d(v) da s a v quando v è incluso in Q. • Per assurdo, considera il primo nodo v inserito in Q per cui dist(v) > d(v) • Esiste un cammino da s a v alternativo più breve che contiene almeno un nodo in R. • Sia v’ il primo nodo in R sul cammino da s a v. • v’ è connesso ad s con un cammino formato di soli nodi in Q, con dist(v’) < dist(v). • Una contraddizione poiché v’ sarebbe stato selezionato in luogo di v. giugno 2002 ASD - Grafi 45 Dijkstra/4 DijkstraAlg(grafo digraph, vertice source) for tutti i vertici v dist(v)= ; dist(source)=0; R = tutti i vertici; while (R!=0) v = vertice in R con minimo dist(v); for tutti i vertici u in R adiacenti a v if dist(u) > dist(v) + d(v,u) dist(u) = dist(v) + d(v,u); pred(u) = v; giugno 2002 ASD - Grafi 46 Dijkstra/5 • La collezione dei pred(u) forma l’albero dei cammini minimi con sorgente s. • Si può risolvere il problema APSP eseguento n volte Dijkstra a partire da n sorgenti. Complessità:O(n (m +n) log n). giugno 2002 ASD - Grafi 47 Minimo Albero Ricoprente – MST • Si desidera selezionare un sottografo di un grafo che mantenga la connettività tra tutti i nodi al minore costo possibile. • Ex: selezionare un sottoinsieme di tratte aeree che permettono di raggiungere tutte le destinazioni con costo minimo. • Assumiamo un grafo semplice e pesi non negativi d(u,v) sugli archi. • La rete ottima è un albero. Perché? giugno 2002 ASD - Grafi 48 8 b 11 8 h 4 i 7 d 9 2 4 a c 7 14 e 6 1 g 2 f 10 A Graph and its MST giugno 2002 ASD - Grafi 49 MST / 2 • Strategie Greedy: procedi attraverso una sequenza di scelte ottime locali. – in generale portano alla soluzione ottima solo in casi particolari. • Per il MST, consideriamo algoritmi che mantengono la seguente proprietà: P1. Ad ogni passo l’insieme degli archi selezionati è un sottoinsieme del MST finale. • Ad ogni passo un nuovo arco viene aggiunto alla soluzione mantenendo P1 giugno 2002 ASD - Grafi 50 MST / 3 • Definiamo un arco “safe” se può essere aggiunto ad un MST mantenendo P1 • Il generico algoritmo Greedy: Algorithm_MST(G,d) A={} while A non è uno Spanning Tree trova un arco (u,v) safe per A; Inserisci (u,v) in A; return A • Diversi algoritmi differiscono per la strategia di ricerca di un arco safe. • Questo algoritmo ha n-1 iterazioni giugno 2002 ASD - Grafi 51 archi “safe” • un taglio (S, V\S) è una partizione dei vertici negli insiemi S e V\S • un arco (u,v) attraversa il taglio se uS e v V\S (o viceversa) • un taglio rispetta un insieme di archi A se nessun arco di A attraversa il taglio • l’arco (u,v) di peso minimo che attraversa il taglio è safe per A – è ok un qualsiasi taglio che rispetti A • dimostrazione? giugno 2002 ASD - Grafi 52 Algoritmo di Boruvka • L’insieme A forma un insieme di componenti connesse • Safe: Determina l’arco di costo minimo che connette due componenti connesse in A. • I pesi degli archi vengono memorizzati in una coda di priorità. • Ad ogni passo si estrae il minimo e si eliminano anche tutti gli archi tra due componenti che vengono unite. • Complessità? giugno 2002 ASD - Grafi 53 8 b 11 8 6 1 h b a 4 i 7 7 c h giugno 2002 6 g 2 Esecuzione dell’Algoritmo di Boruvka e 10 f d 9 5 i 1 14 2 g 3 4 d 9 2 4 a 7 c e La numerazione indica l’ordine di selezione degli archi del MST f ASD - Grafi 54 Algoritmo di Kruskal • Ordina gli archi secondo peso crescente • Safe: Determina l’arco di peso minimo che non determina cicli in A. • Complessità: – Ordinamento degli archi in O(m log m). – Verifica m volte se si ha un ciclo. Determinare l’esistenza di un ciclo può essere svolto in O(log n) – In totale O(m log m) = O(m log n) • L’esecuzione è identica all’algoritmo di Boruvska giugno 2002 ASD - Grafi 55 Algoritmo di Prim / 1 • L’insieme A forma ad ogni passo una singola componente connessa • Inizialmente A contiene {u,v} tale che (u,v) è l’arco di costo minimo. • Ad ogni passo si inserisce in A l’arco di costo minimo che attraversa il taglio indotto da A. giugno 2002 ASD - Grafi 56 Algoritmo di Prim /2 • L’implementazione di Prim è simile a Dijkstra con Q=A. Un Heap R memorizza il peso minimo di un arco che connette un nodo di R ad un nodo di A. • Ad ogni passo un nodo v di minima priorità è inserito in A (e rimosso dall’Heap R) • Per tutti i nodi u in R adiacenti a v si aggiorna la priorità di u se d(v,u) è minore della priorità corrente di u. • Complessità: O(m log n) per l’aggiornamento della priorità che può essere svolto m volte. giugno 2002 ASD - Grafi 57 Algoritmo di Prim / 3 PrimAlg(grafo graph, vertice s) A = {s}; R = tutti i vertici/s; for tutti i vertici v rank(v)=min{d(s,v), }; while R!=0 estrai vertice v in R con minimo rank(v)=d(r,v) r A ; A = A v; pred(u) = v; for tutti i vertici u in R adiacenti ad v if rank(u)>d(v,u) rank(u) = d(v,u); giugno 2002 ASD - Grafi 58 8 b a 11 h b 6 1 g 6 c 4 7 h giugno 2002 2 14 f 5 g 2 Esecuzione dell’Algoritmo di Prim e 10 d 8 3 i 1 d 4 i 7 7 9 2 4 8 a c e La numerazione indica l’ordine di selezione degli archi del MST f ASD - Grafi 59