Dati e Algoritmi 1: A. Pietracaprina Grafi (II parte) 1 Breath-First Search (algoritmo iterativo) • Si assume una rappresentazione tramite liste di adiacenza. L’ordine con cui si visitano i vicini di un nodo è determinato dall’ordine nella lista di adiacenza. • vertice di partenza s (livello L0 ) • ∀i ≥ 0: visita i vertici del livello Li e genera il livello Li+1 : • Li+1 = { vicini non ancora visitati del livello Li } • DISCOVERY EDGE (u,v ): u al livello Li , v al livello Li+1 scoperto a partire da u • CROSS EDGE: gli altri archi 2 Algoritmo Iterativo BFS(G , s) Input: grafo G = (V , E ) non diretto, vertice s ∈ V Output: visita di tutti i vertici e tutti gli archi nella componente connessa di s marcando gli archi ancora non marcati come DISCOVERY/CROSS EDGE /* Considera ogni vertice/arco NON VISITATO/NON MARCATO */ 3 Algoritmo Iterativo BFS(G , s) (continua) visita s e marcalo VISITATO; crea una collezione L0 contenente s; i ← 0; while (!Li .isEmpty()) do crea una collezione di vertici Li+1 vuota; forall v ∈ Li do forall e ∈ G .incidentEdges(v ) do if (e = NON MARCATO) then w ← G .opposite(v ,e); if (w è NON VISITATO) then marca e come DISCOVERY EDGE; visita w e marcalo VISITATO; inserisci w in Li+1 ; else marca e come CROSS EDGE; i ← i + 1; return; 4 Esempio s"=" 1" 1" 2" Liste"di"adiacenza" "(ordine"crescente)" 3" 4" 5" 6" 7" 1:" 2" 3" 2:" 1" 4" 3:" 1" 5" 4:" 2" 5" 6" 5:" 3" 4" 6" 6:" 4" 5" D"="DISCOVERY"EDGE" C"="CROSS"EDGE"" L0! L1! D" 1" 2" D" 3" D" 7" L2! 4" D" 7:" 5" L3! 6" D" C" C" 5" D" 7" 5 Analisi di BFS • G = (V , E ) grafo non diretto, s ∈ V • Cs ⊆ G : componente connessa di G contenente s. Proposizione (≈ Prop. 14.16 [GTG14]) Dopo l’esecuzione di BFS(G , s) si ha: (a) tutti i vertici di Cs sono visitati; (b) i DISCOVERY EDGE formano uno spanning tree T di Cs , chiamato BFS tree (c) ∀v ∈ Li il cammino in T da s a v ha i archi e qualsiasi altro . cammino in G da s a v ha ≥ i archi (⇒ i = distanza(s, v )) (d) se (u, v ) ∈ E e (u, v ) 6∈ T (⇒ (u, v ) è un CROSS EDGE) gli indici dei livelli a cui u e v appartengono differiscono al più di 1 6 Dimostrazione (Esercizi C-14.46, C-14.47 [GTG14]) (a) come per la DFS (b) come per la DFS (c) Si consideri il cammino P : s = u0 − u1 − · · · − ui = v , dove uj ∈ Lj viene “scoperto” da uj−1 , ∀j : 1 ≤ j ≤ i. Quindi (uj−1 , uj ) è un DISCOVERY EDGE, e, di conseguenza, P è un cammino in T . Per assurdo, se esistesse in G un cammino P 0 : s = z0 − z1 · · · − zt = v , con t < i, si avrebbe 7 Dimostrazione (continua) s = z0 ∈ L0 z1 ∈ L1 z2 ∈ .. . L1 o L2 zt = v ∈ L1 o . . . o Lt ⇒ v 6∈ Li : assurdo (d) Per assurdo, se u ∈ Li e v ∈ Li+k con k > 1 v sarebbe scoperto a partire da u e quindi si avrebbe v ∈ Li+1 e non v ∈ Li+k (assurdo). 8 Complessità di BFS Hp: rappresentazione di G tramite liste di adiacenza ns = num. vertici in Cs ms = num. archi in Cs Li : rappresentati tramite liste • ∀v vertice in Cs viene eseguita esattamente 1 iterazione del primo ciclo forall ed esattamente degree(v ) iterazioni del secondo ciclo forall • ciascuna iterazione del secondo ciclo forall richiede tempo Θ (1) • tutti gli accessi alle Li richiedono tempo Θ (1) ⇒ complessità di BFS(G ,s) ∈ Θ (ms ) Corollario Se G = (V , E ) è connesso, la complessità di BFS(G ,s) è Θ (|E |) ∀s ∈ V . 9 Proposizione Dato G = (V , E ) con |V | = n e |E | = m i seguenti problemi possono essere risolti in tempo O (m + n) tramite BFS: 1 testare se G è connesso 2 identificare le componenti connesse di G 3 identificare uno spanning tree di G , se G è connesso 4 identificare un cammino minimo tra due vertici s e t, se esiste 5 identificare un ciclo in G , se esiste, o affermare che G non ha cicli 10 Dimostrazione 1 Simile al caso della DFS 2 Simile al caso della DFS 3 Simile al caso della DFS Cammino minimo da s a t. 4 • Modifichiamo BFS(G ,s) in modo che quando marca un arco (v , w ) come DISCOVERY EDGE imposti v come padre di w • Eseguiamo BFS(G ,s). Alla fine, se t non è stato mai marcato come visitato si dice in output che non esiste un cammino da s a t, altrimenti, partendo da t e risalendo di padre in padre si costruisce il cammino, che risulta minimo in virtù della proposizione precedente. Complessità: O (n + m), dato che si invoca la BFS una sola volta. Esercizio: scrittura di uno pseudocodice più dettagliato 11 Dimostrazione di (5) Osservazione ∃ un ciclo se e solo se ∃ una componente connessa sulla quale BFS marca almeno un arco come CROSS EDGE. Supponiamo che (v , w ) sia marcato come CROSS EDGE da BFS(G ,s): • Esistono due cammini da v a s e da w a s fatti solo di DISCOVERY EDGE • i livelli di v e w differiscono al più di 1 12 Dimostrazione di (5) (continua) A ogni nodo i ∈ V associamo i campi: LV [i].ID, LV [i].level e LV [i].parent. Inizialmente, LV [i].ID è impostato a 0, mentre LV [i].level e LV [i].parent sono impostati a null. Si definisce BFS(G ,s,k) come modifica di BFS(G ,s) che: • Assegna il valore k al campo ID di ciascun vertice visitato (quindi appartenente alla componente connessa di s) • Se w viene “scoperto” da v , marcando l’arco (v , w ) come DISCOVERY EDGE, assegna v al campo LV [w ].parent (ovvero, v diventa “padre” di w . • Imposta il campo LV [v ].level di ciascun vertice v visitato all’indice del livello di v . 13 Dimostrazione di (5) (continua) k ← 1; for i ← 1 to n do if (LV [i].ID= 0) then BFS(G ,i,k); k ← k + 1; forall e ∈ E do if (e = (i, j) è marcato come CROSS EDGE) then C ← collezione di archi vuota; if (LV [i].level = LV [j].level+1) then aggiungi (i, LV [i].parent) a C ; i ← LV [i].parent; • Complessità: O (n + m) • Oss.: le BFS possono essere interrotte appena si marca un arco come CROSS EDGE if (LV [j].level = LV [j].level+1) then aggiungi (j, LV [j].parent) a C ; j ← LV [j].parent; while (i 6= j) do aggiungi (i, LV [i].parent) (j, LV [j].parent) a C ; i ← LV [i].parent; j ← LV [j].parent; return C ; return NO CYCLE; 14 Caso di Studio: 6 gradi di separazione • 1929: Nel racconto Catene Krigyes Karinthy ipotizza con due persone arbitrarie sono collegate da una catena di conoscenze che passa per al più 5 intermediari (da qui la teoria dei 6 gradi di separazione) • 1967: Stanley Milgram decise di verificare sperimentalmente la teoria chiedendo a un gruppo di persone del Midwest (USA) di mandare un pacco a uno sconosciuto in Massachusetts facendolo arrivare tramite conoscenti, conoscenti di conoscenti, ecc. Effettivamente, in media servirono tra i 5 e i 7 intermediari • Verifiche successive: nel 2006 sulla rete di MSN Messenger (6.6 gradi di separazione media), e nel 2011 su Facebook (4.74 gradi di separazione media). Su Faceook risultò che il 92% delle coppie di utenti è separato da al più 4 gradi! 15 Caso di Studio: 6 gradi di separazione (continua) • Grafo di Facebook: n ' 1.8 ∗ 109 vertici (utenti), e m ' 95 ∗ n archi (amicizie). • Calcolo delle distanze esatte per tutte le coppie di utenti: complessità Θ (n · m) ⇒ non eseguibile in pratica. Si usano allora stime approssimate 16 Osservazione DFS(G ,s) e BFS(G ,s) marcano tutti gli archi di Cs (componente comune che contiene s). Infatti sappiamo che visitano tutti i vertici di Cs e (by inspection) per ogni vertice considerano tutti gli archi incidenti, marcandoli se non sono già marcati. 17 Esercizio C-14.37 [GTG14] Sia T lo spanning tree generato dai discovery edge di una Depth-First Search (DFS) di un grafo G non diretto e connesso. T è radicato nel vertice di partenza della DFS. Dimostrare che un arco di G marcato come back edge collega un vertice con un suo antenato in T . (Si osservi che questo giustifica la locuzione “back edge”.) Esercizio C-14.49 [GTG14] Un grafo G = (V , E ) si dice bipartito se l’insieme di vertici V può essere partizionato in due sottoinsiemi X e Y tali che ogni arco di E incide su un vertice di X e uno di Y . Progettare e analizzare un algoritmo efficiente che determini se un grafo non diretto G è bipartito. 18 Esercizio Sia G = (V , E ) un grafo non diretto con k > 1 componenti connesse. Progettare un algoritmo che aggiunga k − 1 archi a G per renderlo connesso e analizzarne la complessità. Si assuma di poter aggiungere a E un arco (i, j) 6∈ E in tempo costante invocando il metodo G.addArc(i,j). Esercizio Sia G un grafo non diretto e connesso in cui ciascun vertice ha grado esattamente c, con c > 2 costante intera. Si consideri l’esecuzione di BFS(G , s) a partire da un arbitrario vertice s ∈ V . Dimostrare per induzione su i che il livello Li generato da BFS(G , s) contiene ≤ c i vertici, per ogni i ≥ 0. 19 Esempio domande prima parte • Definire rigorosamente le componenti connesse di un grafo non diretto G • Dimostrare che in un free tree G con n vertici ed m archi, si ha che m = n − 1. • Sia G un grafo non diretto con n vertici ed m archi. Indicare tre problemi computazionali che, dato in input G possono essere risolti in tempo O (n + m). • Sia G = (V , E ) un grafo non diretto e connesso. Dati due vertici s, t ∈ V dire brevemente come trovare il cammino più breve da s a t, e quanto tempo è richiesto per trovarlo • Sia G = (V , E ) un grafo non diretto e connesso. Dimostrare che eseguendo DFS(G , s), per un qualche s ∈ V si visitano tutti i vertici. 20 Riepilogo • Definizioni e terminologia sui grafi. • Equivalenza tra rooted tree e free tree • Relazione tra la somma dei degree e il numero di archi in un grafo • Relazioni tra numero di vertici e numero di archi in alberi, foreste e grafi connessi • Depth-First Search • Algoritmo • Analisi e proprietà • Problemi computazionali risolvibili tramite DFS • Breadth-First Search • Algoritmo • Analisi e proprietà • Problemi computazionali risolvibili tramite DFS 21