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