• AVVERTENZA: Di seguito trovate alcuni appunti, poco ordinati e poco formali, che uso come traccia durante le lezioni. Non sono assolutamente da considerarsi sostitutivi del materiale didattico. 1 Di cosa tratta la ricerca operativa • La Ricerca Operativa è la disciplina che tratta l’applicazione di metodi analitici avanzati per la soluzione di problemi di decisione (o per prendere decisioni “migliori”). Utilizzando tecniche di diverse area della matematica, come la matematica discreta e la teoria dei grafi, la modellazione matematica, l’analisi statistica, l’algebra lineare e l’ottimizzazione, la Ricerca Operativa consente di arrivare alla soluzione ottima o sub-ottima di complessi problemi decisionali, in tempi ragionevoli. Data la sua natura fortemente applicativa, la Ricerca Operativa ha intersezioni con molte altre discipline, in particolare con molte aree dell’economia e dell’informatica. È alla base della teoria dei giochi. • Nelle prime settimane del corso ci occupiamo di Teoria dei Grafi. 2 Il problema dei ponti Königsberg [1], [2] • La geometria delle “posizioni”. Descrizione del problema dei ponti: scegliere un qualsiasi punto di partenza (sulla terra o su un’isola) e tornarvi, dopo aver attraversato cisacun ponte una e una sola volta. Esiste una soluzione? Un aiuto intermedio: passaggio alla rappresentazione su grafo. Osservare come il grafo coglie gli elementi cruciali del problema, ovvero quali e quanti collegamenti ci sono tra i vari luoghi, mentre elimina quelli inessenziali (lunghezza e forma dei ponti, forma delle isole etc.) • Se non esiste soluzione, perché non esiste? Ovvero, come è possibile certificare la non esistenza di una soluzione? Banalmente: basta considerare le 5040 = 7! possibili permutazioni dei ponti. E se il numero di ponti aumenta? 8! =40320; 20! numero di 19 cifre; 100! numero di 158 cifre) Si può fare di meglio? • Il problema di Königsberg non ha soluzione perché la seguente condizione necessaria non è soddisfatta: condizione necessaria per l’esistenza di una soluzione è che su ciascun vertice del corrispondente grafo incida un numero pari di spigoli (i.e. ponti). La soluzione è anche sufficiente? Risponderemo più avanti quando torneremo sul problema dei ponti di Königsberg, presenteremo un semplice algoritmo per individuare una soluzione (quando esiste) e affronteremo un problema di ottimizzazione che in qualche modo lo generalizza: il Problema del Postino Cinese [9]: 1 3 Definizioni di base di teoria dei grafi [5] • Definizione di grafo (non orientato, semplice): una coppia di insiemi G(V, E) dove V è l’insieme di vertici e E l’insieme degli spigoli, ovvero un insieme di coppie non ordinate di vertici distinti (per indicare questo, spesso useremo la notazione E ⊆ V2 ). • Definizione di multigrafo: una coppia di insiemi G(V, E) dove V è l’insieme di vertici e E il multiinsieme degli spigoli, ovvero un multiinsieme di coppie non ordinate di vertici distinti. • Definizione di grafo orientato (semplice): una coppia di insiemi D(N, A) dove N è l’insieme dei nodi e A l’insieme degli archi, ovvero un insieme di coppie ordinate di nodi distinti. Nel seguito quando parliamo semplicemente di grafo, ci riferiamo a un grafo non orientato; per riferirci ai grafi orientati useremo appunto, esplicitamente, l’aggettivo “orientato” • Osservare come la definizione di un grafo non sia grafica. Osservare come un grafo con vertici V = {a, b, c, d} e E = {ab, ac, ad, bc, bd, cd} può essere disegnato in modi profondamente diversi! La rappresentazione grafica introduce aleatorietà che invece non sono presenti nella definizione come coppia di insiemi. • Gli estremi di uno spigolo e ∈ E sono i vertici u, v della coppia individuata dallo spigolo, ovvero i vertici tali che e ≡ uv. Se tra due vertici di un grafo esiste uno spigolo essi sono adiacenti. Uno spigolo e è incidente in un vertice v se v è un estremo di e. Due spigoli sono incidenti se hanno un estremo in comune. • Sia G(V, E) un grafo e v ∈ V . Grado deg(v): il numero di vertici cui v è adiacente. N(v): l’insieme dei vertici cui v è adiacente. δ(v): l’insieme degli spigoli incidenti su v. • Handshaking Lemma [8]: per un qualunque grafo G(V, E) vale: ∑v∈V deg(v) = 2|E|. Osservare come questo risultato banalmente si estende a multigrafi. Corollario: per un qualunque grafo G(V, E), il numero di vertici di grado dispari è pari. • Sia G(V, E) un grafo. Un vertice isolato di G è un vertice di grado 0. Il numero di spigoli |E| è la dimensione di G. Il numero di vertici |V | è l’ordine di G. • Sia G(V, E) un grafo. Il grafo complemento di G è il grafo H(W, F) tale che W = V W e F = 2 \ E, ovvero il grafo complemento prende gli stessi vertici di G, mentre una coppia di vertici è adiacente nel grafo complemento se e solo se non è adiacente in G. Normalmente, lo indichiamo semplicemente come G. Osservare come il complemento del complemento di G è proprio G. • Sia G(V, E) un grafo. Un sottografo H ⊆ G è un grafo H(W, F) tale che W ⊆ V e F ⊆ W E ∩ 2 , ovvero W è un sottoinsieme di V , mentre F è un sottoinsieme di spigoli di E i cui estremi sono in W . 2 • Sia G(V, E) un grafo e W ⊆ V un sottoinsieme di vertici. Il sottografo indotto da W su G W è il grafo H(W, F) con vertici W e spigoli F = E ∩ 2 , ovvero prende tutti gli spigoli di E i cui estremi sono in W . Normalmente, lo indichiamo semplicemente come G[W ]. • Sia G(V, E) un grafo. Un sottografo ricoprente H ⊆ G è un sottografo H(W, F) tale che W = V. 3.1 Digressione: relazioni di conoscenza • Osservare come i grafi consentono di rappresentare anche relazioni di conoscenza. È vero che in un inseme qualsiasi di 5 persone esistono sempre 3 persone che si conoscono reciprocamente o viceversa esistono sempre 3 persone che non si conoscono reciprocamente? È possibile riformulare il problema in modo equivalente su grafo: è vero che per un qualunque grafo G con 5 vertici vale la seguente affermazione: in G e/o nel complemento di G esiste un triangolo (ovvero tre vertici mutuamente adiacenti)? Falso: un controesempio è dato da un ciclo di lunghezza 5. • Considerare il problema precedente con 6 persone. Sia G(V, E) un grafo con 6 persone e sia v ∈ V . Osserviamo che: dG (v) + dG (v) = 5, quindi o degG (v) ≥ 3 oppure dG (v) ≥ 3. Supponiamo che degG (v) ≥ 3 e siano x, y, z tre vertici in N(v): se esiste uno spigolo tra due vertici, e.g. x, y, allora v, x, y è un triangolo; se non esiste alcuno spigolo tra x, y, z, allora x, y, z è un triangolo nel complemento. Il caso dG (v) ≥ 3 segue banalmente perché G è comunque un grafo con 6 vertici e quindi basta applicare il ragionamento precedente. (Dove fallisce questa dimostrazione quando n = 5?) • Osservare che il risultato precedente si estende a un qualunque grafo G con più di 6 vertici: basta considerare il sottografo indotto su G da 6 vertici qualsiasi. 4 Cenni sulle strutture dati per la rappresentazione dei grafi Illustriamo in breve le principali strutture dati più usate per memorizzare i grafi. Questi argomenti sono trattati con maggiore dettaglio nel corso di Ingegneria degli algoritmi [10]. • Matrice adiacenza: matrice binaria n × n A, dove n = |V (G)|, tale che ai j = 1 se e solo se i j ∈ E(G). La matrice è simmetrica. Inoltre: – Per sapere se un esiste uno spigolo tra un vertice i e un vertice j dobbiamo leggere il valore dell’elemento ai j (oppure a ji ) della matrice. Questa è un operazione elementare, quindi ha complessità O(1). 3 – Per contare il numero di vertici adiacenti a un certo vertice i dobbiamo scorrere tutta la riga di i (o la colonna di i), quindi questa operazione ha complessità O(n). – Per contare il numero di spigoli del grafo dobbiamo scandire tutta la matrice, quindi la complessità del conteggio del numero di spigoli è O(n2 ). – L’occupazione di memoria di questa struttura dati è O(n2 ). – Se abbiamo bisogno di memorizzare ulteriori informazioni, e.g. la lunghezza, la capacità di uno spigolo, il generico elemento ai j della matrice non è più semplicemente un numero binario, ma un record composto di diversi campi: il primo campo, a valore 0-1 ci dice se quello spigolo fa parte del grafo, i successivi campi riportano appunto lunghezza, capacità etc. – Se vogliamo memorizzare con matrice di adiacenza un grafo orientato, l’unica cosa che cambia è che la matrice non è più simmetrica, in quanto ovviamente può capitare che (i, j) ∈ E ma ( j, i) ∈ / E. Si osservi come, per un grafo non orientato, potremmo utilizzare la simmetria della matrice, e il fatto che per un grafo semplice la diagonale superiore si compone di tutti 0, e memorizzare solo la parte triangolare superiore della matrice di adiacenza. Tuttavia il vantaggio di questa rappresentazione “compatta” sarebbe trascurabile, perché e.g. l’occupazione di memoria è sempre O(n2 ) (la diagonale superiore di una matrice n × n ha n(n−1) elementi). 2 Il vantaggio principale della matrice di adiacenza è quello di poter verificare in tempo costante se un certo spigolo appartiene al grafo. Il suo svantaggio principale è quello di essere una struttura statica che non è influenzata in alcun modo dal numero di spigoli presenti nel grafo: per esempio, il grafo completo con n vertici e il grafo vuoto con n vertici occupano la stessa quantità di memoria! Questo svantaggio è superato dalla prossima rappresentazione basata su liste, che vediamo a breve. Prima però introduciamo un altro modello di rappresentazione su matrice che sarà molto utile per formulazione di problemi di flusso e altri problemi che studieremo più avanti. • Matrice incidenza: matrice binaria n × m A, dove n = |V (G)| e m = |E(G)|. Ogni colonna è associata univocamente a uno spigolo, e aih = 1 se e solo se i è estremo dello spigolo eh (quindi ogni colonna ha esattamente due valori diversi da zero). Osserviamo che: – Sapere se un esiste uno spigolo tra un vertice i e un vertice j dobbiamo scorrere tutte le colonne di A, quindi l’operazione ha complessità O(m). – Per contare il numero di vertici adiacenti a un certo vertice i dobbiamo scorrere tutta la riga di i, quindi questa operazione ha complessità O(n). – Per contare il numero di spigoli del grafo dobbiamo solo contare tutte le colonne, quindi la complessità del conteggio del numero di spigoli è O(m). – L’occupazione di memoria di questa struttura dati è O(n × m). 4 – Se abbiamo bisogno di memorizzare ulteriori informazioni, e.g. la lunghezza, la capacità di uno spigolo, possiamo semplicemente utilizzare un vettore addizionale di dimensione m opportunamente collegato alla matrice A. – Se vogliamo memorizzare con matrice di adiacenza un grafo orientato, l’unica cosa che cambia è che su ogni colonna h gli unici valori non zero saranno un 1 e un -1: 1 per la coda i dell’arco e -1 per la testa j dell’arco (i, j) = eh . • Liste adiacenza. (Per una breve introduzione alle liste si veda [7].) Utilizziamo per ogni vertice i una lista ad(i) in cui riportiamo tutti gli elementi cui i è adiacente; ogni elemento della lista ad(i) ha due campi: l’etichetta del vertice cui i è adiacente e il puntatore alla posizione del prossimo elemento della lista (o, in alternativa, il valore convenzionale “NULL” se la lista è finita). La struttura è completata da un vettore di puntatori, in cui è riportato per ogni vertice i del grafo, il puntatore al primo elemento della sua lista di adiacenza ad(i) (o, in alternativa, “NULL” se i è un vertice isolato). – Per sapere se esiste uno spigolo tra un vertice i e un vertice j dobbiamo scandire la lista degli adiacenti di i (oppure di j). Questa operazione ha complessità O(deg(i)) (risp. O(deg( j))). – Per contare il numero di vertici adiacenti a un certo vertice i dobbiamo scorrere tutta la lista di i, quindi la complessità è O(deg(i)). – Per contare il numero di spigoli del grafo dobbiamo scandire tutte le liste di adiacenza, quindi la complessità del conteggio del numero di spigoli è ∑i∈V O(deg(i)) = O(m). – L’occupazione di memoria è O(m) + O(n). Il termine O(n) proviene dal vettore di puntatori. Il termine O(m) proviene invece dalle liste di adiacenze vere e proprie (seguendo gli stessi argomenti utilizzati per il conteggio degli spigoli). – Se abbiamo bisogno di memorizzare ulteriori informazioni, e.g. la lunghezza, la capacità di uno spigolo, il generico elemento della lista di adiacenza di i, oltre a riportare l’etichetta del vertice cui i è adiacente e il puntatore al prossimo (eventuale) elemento nella lista, riporta anche la lunghezza, capacità etc. dello spigolo corrispondente. – Se vogliamo memorizzare con liste di adiacenza un grafo orientato, l’unica cosa che cambia è che per ciascun vertice i avremo due insiemi di liste: una per i successori di i e una per i predecessori (e, ovviamente, il vettore dei puntatori deve ospitare due puntatori per ciascun vertice). Si noti che tutte le informazioni in una lista, e.g. quella dei predecessori, possono essere dedotte guardando tutte le liste dei successori, quindi c’e’ una ridondanza nel memorizzare due diversi insiemi di liste; tuttavia, in genere, si preferisce questa più pratica soluzione. 5 5 Strumenti: le dimostrazioni per induzione [5] • Primo principio di induzione. Per dimostrare che una certa proposizione P(n) vale per un qualunque numero naturale n posso procedere come segue. 1) Passo base: dimostro che P(1) è vera, ovvero che la proposizione è vera per n = 1. 2) Passo induttivo: dimostro che se P(n) è vera, allora anche P(n + 1) è vera. Illustrare il primo principio di induzione con il passaparola e il domino. • Dimostriamo con il primo principio che la seguente proposizione P(n): la somma dei 2 primi n numeri dispari, ∑n−1 h=0 (2h + 1), è pari a n . Passo base: l’affermazione è vera per n = 1. Passo induttivo: supponiamo che l’affermazione sia vera per n, i.e. ∑n−1 h=0 (2h+1) = 2 n , e facciamo vedere che essa è vera per n + 1; infatti: n n−1 ∑ 2h + 1 = ( ∑ 2h + 1) + 2n + 1 = n2 + 2n + 1 = (n + 1)2 h=0 h=0 . • Secondo principio di induzione (apparentemente più forte del primo, ma in realtà equivalente). 1) Passo base: dimostro che P(1) è vera, ovvero che la proposizione è vera per n = 1. 2) Passo induttivo: dimostro che se P(i) è vera per qualunque i ≤ n, allora anche P(n + 1) è vera. • Dimostrare con il secondo principio che ogni numero naturale può essere espresso come prodotto di numeri primi. Passo base: l’affermazione è vera per n = 1. Passo induttivo: supponiamo che l’affermazione sia vera per ogni numero minore o uguale a n, i.e. per ogni 1 ≤ h ≤ n, i.e. vale h = a1 (h) · a2 (h) · . . . · ak(h) (h), con a1 (h), a2 (h), . . . , ak(h) (h) primi, e facciamo vedere che essa è vera per n + 1. Se n + 1 è primo, allora l’affermazione è vera poiché n + 1 = (n + 1) · 1; se n + 1 non è primo, allora n + 1 può essere fattorizzato come il prodotto di numeri tra 2 e n, cioè n + 1 = b1 · b2 . Ognuno di questi due fattori è minore di n + 1, vale quindi l’ipotesi induttiva e allora possiamo sostituire a ciascun fattore la sua scomposizione in numeri primi, quindi alla fine esprimiamo n + 1 come prodotto di numeri primi. • Attenzione nell’uso del principio di induzione! Dimostriamo che “un qualunque insieme di n cavalli è formato da cavalli dello stesso colore” (vedi [6]). Prendiamo un insieme di n cavalli. Se n = 1, l’affermazione è banale. Supponiamo ora che l’affermazione valga per insiemi con n cavalli e consideriamo un qualunque insieme X di n + 1 cavalli, che indichiamo come X = {1, 2, 3, . . . , n + 1}. Consideriamo i due sottoinsiemi con n elementi X1 = {1, 2, 3, . . . , n} e X2 = {2, 3, . . . , n + 1}: per ipotesi induttiva, ciascuno di questi due insiemi è formato da cavalli dello stesso colore. Osserviamo anche che questi due insiemi si intersecano, e quindi esiste un cavallo che appartiene sia a X1 che a X2 . Concludiamo quindi che X è formato da cavalli dello stesso colore. O no? 6 6 Grafi connessi, aciclici e alberi [3] • Sia G(V, E) un grafo. Un walk su G è una successione x1 e1 x2 e2 . . . , x p−1 , e p−1 x p , p ≥ 1, dove xi ∈ V e ei ≡ xi xi+1 ∈ E. I vertici x1 e x p sono detti estremi del walk e il walk è chiuso se x1 = x p . • Un trail su G è un walk in cui gli spigoli non si ripetono (non passiamo più volte per lo stesso spigolo, ma possiamo passare più volte per lo stesso vertice). In particolare, un trail i cui estremi coincidono è detto circuito. • Un path su G è un trail in cui i vertici non si ripetono (non passiamo più volte per lo stesso vertice) con la sola eccezione, possibilmente, dei vertici estremi. In particolare, un path i cui estremi coincidono è detto ciclo. La lunghezza di un path (in caso, di un ciclo) è pari al numero degli spigoli del path. • È facile verificare che se tra due vertici di un grafo esiste un walk che li connette, allora esiste un path che li connette. Similmente, un grafo ha un circuito se e solo se ha un ciclo. • Sia G(V, E) un grafo. Due vertici u e v sono connessi se esiste un path di G con estremi u e v. • Un grafo G(V, E) è detto connesso se per ogni coppia di vertici distinti u, v ∈ V , u e v sono connessi. Come è fatto un grafo che non è connesso? • Una famiglia di sottoinsiemi Q1 , Q2 , . . . , Qk di un insieme Q è una partizione di Q se ogni elemento di Q appartiene a un e un solo sottoinsieme Qi . Un modo per definire una partizione di un insieme è quello di definire sull’insieme una una relazione di equivalenza (vedi [7]), ovvero una relazione tra coppie di elementi dell’insieme che sia: riflessiva, simmetrica e transitiva. Una relazione di equivalenza definita su un insieme lo partiziona in classi di equivalenza: due elementi appartengono alla stessa classe se e solo se sono equivalenti. • Osservare come la connessione definisca una relazione di equivalenza sui vertici di un grafo. Infatti, la connessione è: riflessiva, ogni vertice è connesso a se stesso); simmetrica, se u è connesso a v, allora v è connesso a u; transitiva, se u è connesso a v e se v è connesso a z, allora u è connesso a z. La connessione determina quindi una partizione di V in classi di equivalenza V1 ,V2 , . . .Vk e, in modo analogo una “partizione” di G nei sottografi indotti G[V1 ], G[V2 ], . . . , G[Vp ], che sono detti componenti connesse di G. In altre parole le componenti connesse di G, sono i più grandi pezzi connessi di G. • Sia G(V, E) un grafo e siano e ∈ E e v ∈ V rispettivamente uno spigolo e un vertice di G. Il grafo G − e è il grafo con insieme dei vertici V e insieme degli spigoli E \ {e}, ovvero il grafo ottenuto da G rimuovendo lo spigolo e. Il grafo G − v è il grafo con insieme dei vertici V \ {v} e insieme degli spigoli E \ δ(v), ovvero il grafo ottenuto da G rimuovendo il vertice v e tutto gli spigoli ad esso adiacenti. 7 • Un grafo è detto aciclico se esso è privo di cicli. Lemma 1 Sia G(V, E) un grafo connesso e e ∈ E un suo spigolo. Valgono: (i) Il grafo G − e ha al più due componenti connesse. (ii) Se G è aciclico, il grafo G − e ha esattamente due componenti connesse. Dimostrazione Siano u, v ∈ V tali che e ≡ uv (i) Ci sono due casi possibili. 1) Se u e v sono ancora connessi in G − e da un path P∗ , allora G − e è ancora connesso: infatti a un qualsiasi path P di G con estremi x, y corrisponde un walk P0 di G − e con estremi x, y, ottenuto sostituendo, eventualmente, allo spigolo e il path P∗ . 2) Se u e v non sono connessi in G − e, allora G − e ha due sole componenti connesse: quella che contiene u e quella che contiene v. Infatti supponiamo per assurdo che esista un terza componente connessa G3 , che contenga un vertice z e non contenga u e v. Poiché G è connesso, in G esiste un path da z a u che non usa uv oppure un path da z a v che non usa uv: questo path è anche un path di G − e e questo contraddice il fatto che in G − e z non è connesso a u e v. (ii) Dobbiamo semplicemente dimostrare che u e v appartengono a diverse componenti connessi di G − e. In caso contrario, esisterebbe un path di G − e che li connette, e questo path, insieme ad e, formerebbe un ciclo in G, una contraddizione. Corollario 2 Sia G(V, E) un grafo connesso e v ∈ V un vertice. Il grafo G − v ha al più deg(v) componenti connesse. Dimostrazione Basta iterare il Lemma 1 su tutti gli spigoli incidenti su v. Teorema 3 Condizione necessaria perché un grafo sia connesso è che il numero degli spigoli sia almeno pari al numero dei vertici -1, i.e. |E(G)| ≥ |V (G)| − 1. La dimostrazione è per induzione sul numero dei vertici. In particolare, utilizziamo il secondo principio di induzione. L’affermazione è banalmente verificata se |V (G)| = 1. Supponiamo ora che un qualunque grafo connesso con al più n ≥ 1 vertici abbia almeno n − 1 spigoli, e dimostriamo che allora un (qualunque) grafo connesso G con n + 1 vertici ha almeno n spigoli. Consideriamo un vertice v ∈ V (G) e il grafo G − v. Siano G1 , G2 , . . . , Gk le componenti connesse di G − v, con k ≤ deg(v). Ogni componente connessa Gi è tale che |V (Gi )| ≤ n e quindi, per ipotesi induttiva, per ciascuna componente connessa, vale |E(Gi )| ≥ |V (Gi )| − 1. Osserviamo anche che possiamo partizionare E(G) in k + 1 classi: δ(v), E(G1 ), E(G2 ) . . . , E(Gk ). Segue: k k |E(G)| = |δ(v)|+ ∑ |E(Gi )| ≥ deg(v)+ ∑ (|V (Gi )|−1) = deg(v)+n−k ≥ n = |V (G)|−1. i=1 i=1 L’ultima disequazione segue dal Corollario 2, mentre per l’ultima equazione ricordiamo che V (G) = V (G − v) ∪ {v}. 8 • Poniamo attenzione al fatto che |E(G)| ≥ |V (G)| − 1 è una condizione solo necessaria perché G sia connesso. Considerate un grafo con 10 vertici {a1 , a2 , . . . , a5 , b1 , b2 , . . . , b5 } e tale che tutti i vertici ai siano a coppie adiacenti, tutti i vertici bi siano a coppie adiacenti e tale che non vi siano altri adiacenze. Malgrado il grafo abbia 20 >> 9 spigoli, esso non è connesso. • Lemma 4 Sia G(V, E) un grafo aciclico e e ∈ E uno spigolo. Il grafo G − e è ancora aciclico e il suo numero di componenti connesse è pari al numero di componenti connesse di G +1. Infatti, siano u e v gli estremi dello spigolo e e siano G1 , G2 , . . . , Gk le componenti connesse di G. Supponiamo senza perdita di genaralità, che u, v ∈ Gk . È immediato verificare che G1 , G2 , . . . , Gk−1 sono ancora componenti connesse di G − e. Infine segue dal Lemma 1 che Gk − e consta di due componenti connesse. • Teorema 5 Condizione necessaria perché un grafo sia aciclico è che il numero degli spigoli sia al più pari al numero dei vertici -1, i.e. |E(G)| ≤ |V (G)| − 1. La dimostrazione è per induzione sul numero degli spigoli. In particolare, utilizziamo il secondo principio di induzione. L’affermazione è banalmente verificata se |E(G)| = 1. Supponiamo ora che un qualunque grafo aciclico con al più m ≥ 1 spigoli abbia almeno m + 1 vertici e dimostriamo che allora un (qualunque) grafo aciclico G con m + 1 spigoli ha almeno m + 2 vertici. Consideriamo uno spigolo e ∈ E(G) e il grafo G − e. Siano G1 , G2 , . . . , Gk le componenti connesse di G−e, con k ≥ 2. Ogni componente connessa Gi è tale che |E(Gi )| ≤ m. Quindi, per ipotesi induttiva, per ciascuna componente connessa, vale |E(Gi )| ≤ |V (Gi )| − 1. Osserviamo anche che possiamo partizionare E(G) in k + 1 classi: e, E(G1 ), E(G2 ) . . . , E(Gk ). Segue: k k |E(G)| = 1 + ∑ |E(Gi )| ≤ 1 + ∑ (|V (Gi )| − 1) = 1 + |V (G)| − k ≤ |V (G)| − 1 i=1 i=1 . • Poniamo attenzione al fatto che |E(G)| ≤ |V (G)| − 1 è una condizione solo necessaria perché G sia aciclico. Considerate un grafo con 10 vertici {a1 , a2 , . . . , a10 } e con spigoli {a1 a2 , a2 a3 , a3 a1 }. Malgrado il grafo abbia 3 << 9 spigoli, esso non è aciclico. • Un grafo aciclico e connesso è detto albero. Segue dai Teorema 3 + Teorema 5 che un albero con n vertici ha n − 1 spigoli. Si osservi che un albero è un grafo “minimalmente connesso”, in quanto la rimozione di un qualsiasi spigolo pregiudica la connettività e “massimalmente aciclico”, in quanto l’aggiunta di un qualsiasi spigolo pregiudica la aciclicità. Si osservi, in particolare, che per ogni coppia di vertici di un albero, esiste un e un solo cammino con estremi i due vertici. • Un grafo ad albero è quindi un grafo che viene utilizzato in tutti quei casi, per esempio il progetto di una rete irrigua, in cui l’esigenza primaria è quella della connessione, mentre la robustezza non è un’istanza cruciale. 9 • Lemma 6 Un qualunque albero T (V, E) con almeno due vertici ha almeno due vertici di grado uno, che sono detti foglie. Si osservi che T non ha vertici di grado zero poiché stiamo assumendo che T abbia almeno due vertici (ed è connesso). Partizioniamo quindi V in due classi: la classe V1 dei vertici che hanno grado 1, e la classe V2 dei vertici che hanno grado > 1. Per l’handshaking lemma, 2|E| = ∑v∈V deg(v) = ∑v∈V1 deg(v)+ ∑v∈V2 deg(v) = |V1 |+ ∑v∈V2 deg(v) ≥ |V1 | + 2|V2 |. Poiché 2|E| = 2(|V | − 1) = 2|V1 | + 2|V2 | − 2 segue: 2|V1 | + 2|V2 | − 2 ≥ |V1 | + 2|V2 |, quindi |V1 | ≥ 2. 7 Circuiti Euleriani ([3, 11]) • Dato G(V, E) un circuito euleriano è un circuito (i.e. un trial chiuso) che passa per ciascuno spigolo di G una e una sola volta. Per esempio, se visitiamo una mostra in cui le opere sono disposte nei corridoi, un circuito euleriano ci permetterebbe di visitare tutta la mostra senza passare più volte per lo stesso corridoio. Quando un grafo ammette un circuito euleriano? Teorema 7 Sia G(V, E) un grafo connesso. Allora le 3 affermazioni seguenti sono equivalenti: 1. G ammette un circuito euleriano. 2. Ogni vertice di G ha grado pari. 3. L’insieme degli spigoli di G può essere partizionato in cicli disgiunti sugli archi, i.e. / E(G) = E(C1 ) ∪ E(C2 ) ∪ . . . ∪ E(Cq ), dove ogni Ci è un ciclo e E(Ci ) ∩ E(C j ) = 0, se i 6= j. (1) → (2) – Banale, lo abbiamo già osservato per il problema dei ponti di Königsberg. (2) → (3) – L’affermazione è banale se G ha tutti vertici di grado 0. Supponiamo quindi che G abbia almeno un vertice v con grado > 0 e consideriamo la componente connessa G0 di G che contiene v. G0 è connessa, quindi se fosse anche aciclica sarebbe un albero. Ma in questo caso avrebbe due vertici di grado 1, cosa che possiamo escludere, poiché per ipotesi ogni vertice di G ha grado pari. Quindi G0 , e quindi anche G, ha un ciclo C0 : lo rimuoviamo e consideriamo il grafo G1 = G − C0 , ovvero il grafo che ha gli stessi vertici di G, ma insieme degli spigoli pari a E \ E(C0 ). Osserviamo che tutti i vertici di questo grafo hanno ancora grado pari o nullo. Se G1 ha tutti vertici di grado 0, allora l’affermazione è dimostrata con E = E(C0 ). Altrimenti G1 ha degli spigoli e quindi almeno una componente connessa: ragionando come prima possiamo dedurre che in questa componente connessa esiste un ciclo C1 . Rimuoviamo C1 e consideriamo il grafo G2 = G1 − C1 . Se questo grafo ha tutti vertici di grado 0, allora l’affermazione è dimostrata con E = E(C0 ) ∪ E(C1 ), altrimenti, poiché è ancora vero che ogni vertice di 10 G2 ha grado pari o nullo, possiamo ripetere il ragionamento precedente. Poiché il numero di spigoli di G è finito, allora esisteranno un numero p e successivi cicli C2 ,C3 , . . . ,C p , per costruzione disgiunti sugli archi, tali che E(G) = E(C0 ) ∪ E(C1 ) ∪ E(C2 ) ∪ . . . ∪ E(C p ). (3) → (1) – Consideriamo il ciclo C1 . L’affermazione è banale se q = 1. Se q > 1, poiché / È posil grafo è connesso, esiste un altro ciclo C j , j 6= 1, tale che V (C1 ) ∩ V (C j ) 6= 0. sibile concatenare C1 e C j in un circuito Q1 come segue: 1) percorriamo C1 a partire da un vertice arbitrario; 2) appena incontriamo un vertice v che appartiene a V (C1 ) ∩V (C j ) abbandoniamo C1 per percorrere interamente C j ; 3) quando abbiamo terminato di percorrere C j (quindi siamo di nuovo su v), terminiamo di percorrere C1 . L’affermazione è dimostrata se q = 2. Se q > 2, sempre per l’ipotesi di connessione, possiamo concatenare / Naturalmente possiQ1 con un altro ciclo Ch , h ∈ / {1, j}, tale che V (Q1 ) ∩ V (Ch ) 6= 0. amo iterare questo procedimento fino a concatenare tutti i cicli C1 , . . . ,Cq in unico ciclo, euleriano per G. • È immediato verificare che la dimostrazione precedente, e quindi il teorema, si estende a grafi con spigoli paralleli. • Quando un grafo connesso G ammette un trail euleriano aperto, ovvero tale che il vertice iniziale del trail è diverso dal vertice finale del trail? Se e solo se i vertici di grado dispari di G sono esattamente due. Dimostrarlo attraverso il seguente lemma: G connesso ammette percorso euleriano aperto se e solo se esistono due vertici u, v tali che G + uv ammette circuito euleriano (si noti che in generale G + uv potrebbe non essere un grafo semplice). • Nelle pieghe della precedente dimostrazione intravediamo un algoritmo. In effetti il procedimento che ci siamo dati per concatenare i circuiti è già algoritmico. Per avere un algoritmo vero e proprio, dobbiamo fornire un procedimento per individuare un circuito1 / in cui tutti i vertici hanno grado pari. (Osserviamo che in un grafo G, con E(G) 6= 0, l’affermazione (2) → (3) non richiede l’ipotesi di connessione). Questo è semplice. In/ fatti, per trovare un circuito in un grafo in cui tutti i vertici hanno grado pari e E(G) 6= 0, è sufficiente scegliere un vertice v con grado > 0 e percorrere a partire da v un cammino qualsiasi, che però eviti di percorrere più volte uno stesso spigolo: è facile convincersi, che poiché ogni vertice ha grado pari e il numero di spigoli è finito, questo cammino deve necessariamente tornare a v ed essere quindi un circuito. • Possiamo quindi enunciare il seguente algoritmo per trovare un circuito Euleriano in un grafo connesso (vedremo più avanti un algoritmo per riconoscere se un grafo è connesso). L’algoritmo è detto di Hierholzer. Si noti che l’implementazione che segue differisce da quanto discusso prima per i seguenti dettagli: a ogni passo cerchiamo di costruire circuiti invece che cicli, in particolare circuiti più lunghi possibile (osservare che se tutti i nodi hanno grado pari ogni cammino ran1 È semplice vedere che il Teorema 7 continua a valere se in (3) sostituiamo la parola “circuiti” alla parola “cicli”. Nel seguito ci riferiamo a questa riformulazione 11 dom che parte da un nodo v e evita di percorrere più volte lo stesso arco è destinato a riportarci in v. Nostra convenzione: risolviamo le situazioni di parità a favore del nodo primo in ordine alfabetico o numerico; la concatenazione la facciamo man mano, i.e. concateniamo il nuovo circuito a quello corrente. Algoritmo di Hierholzer (Input G connesso e con almeno due vertici) 1. Se G contiene un vertice di grado dispari, STOP: G non ha un circuito euleriano. Se G non ha spigoli, STOP: caso banale. 2. Scegli un vertice v0 di G. 3. **Costruzione di un circuito a partire da v0 in G**. Scegli uno spigolo e1 = v0 v1 di G. A partire da e1 , costruisci un circuito C0 = {e1 , . . . , ek } come segue: finché è possibile, scegli uno spigolo ei+1 di G tale che ei e ei+1 sono entrambi incidenti in vi e ei+1 6∈ {e1 , . . . , ei }. 4. Poni i = 0, G0 = G e Z0 = C0 . 5. Rimuovi gli spigoli di Zi dal grafo Gi e sia Gi+1 = Gi \ Ci . Se ogni vertice di Gi+1 è isolato, allora il circuito Ci è un trail euleriano per Gi : STOP. 6. Scegli un vertice wi del circuito Ci incidente con qualche spigolo del grafo Gi+1 (wi non è un vertice isolato per Gi+1 ). Costruisci un circuito Zi+1 , con inizio (e termine) wi nella componente connessa di wi in Gi+1 , seguendo il procedimento indicato al passo 3 (ma con v0 = wi e G = Gi+1 ). 7. Forma un circuito Ci+1 concatenando Ci e Zi+1 (entrambi i circuiti contengono il vertice wi ). 8. Poni i = i + 1 e vai a 5. • È facile verificare che utilizzando liste d’adiacenza l’algoritmo di Hierholzer può essere implementato con complessità O(|E|). 8 Alberi [3] • Il seguente Lemma 8 mostra che, in modo alternativo, possiamo definire un albero come un grafo connesso con n vertici e n − 1 spigoli, oppure come un grafo aciclico con n vertici e n − 1 spigoli. Lemma 8 Sia G un grafo con n vertici. Allora due qualunque delle seguenti affermazioni implicano la terza: 1. G è connesso. 2. G è aciclico. 12 3. G ha n − 1 spigoli. (1) + (2) → (3) – segue dal Teorema 3 + Teorema 5. (2) + (3) → (1) – supponiamo viceversa che G è aciclico, con n-1 spigoli, ma non è connesso. Allora G è fatto di componenti connesse G1 , G2 , . . . , Gk con k ≥ 2. Siano u e v rispettivamente un vertice di G1 e un vertice di G2 ; naturalmente uv ∈ / E(G). Si osservi che il grafo G + uv, ovvero il grafo ottenuto aggiungendo a G lo spigolo uv, è ancora aciclico: infatti, poiché G è aciclico, un qualunque ciclo di G + uv è necessariamente composto dallo spigolo uv e da un path con estremi u e v che già doveva appartenere a G, contraddicendo il fatto che in G u e v appartengano a diverse componenti connesse. Quindi G + uv è un grafo aciclico con n vertici e n spigoli, una contraddizione al Teorema 5. (3) + (1) → (2) – supponiamo viceversa che G è connesso, con n-1 spigoli, ma non è aciclico. Sia quindi C un ciclo di G e e uno spigolo del ciclo C. Si osservi che il grafo G − e è ancora connesso: infatti a un qualsiasi path P di G con estremi x, y corrisponde un walk P0 di G − e con estremi x, y, ottenuto sostituendo, eventualmente, allo spigolo e il path P∗ (composto dagli archi di C − e) . Quindi G − e è un grafo connesso con n vertici e n − 2 spigoli, una contraddizione al Teorema 3. • Sottolineiamo due punti salienti della dimostrazione del Lemma 8. Se G è un grafo, G1 e G2 sono due componenti connesse di G (n.b. G potrebbe avere anche anche altre componenti connesse) e u e v sono rispettivamente un vertice di G1 e un vertice di G2 , allora i cicli del grafo G + uv sono gli stessi di G. In altre parole, se aggiungiamo uno spigolo tra 2 componenti connesse, non creiamo nuovi cicli. Se G è un grafo, C è un ciclo di G e e è uno spigolo di C, allora le componenti connesse di G − e sono le stesse componenti connesse di G. In altre parole, se rimuoviamo uno spigolo di un ciclo, non creiamo nuove componenti connesse. • Un grafo aciclico e connesso è dunque un albero. Un grafo solo aciclico è formato da più componenti connesse, ognuna delle quali è un albero. Un grafo aciclico è quindi anche detto foresta. Si osservi quindi che un grafo aciclico con n vertici e k componenti connesse ha esattamente n − k spigoli. • Spesso per riferirci a un grafo che è un albero useremo la notazione T (V, E). • Illustriamo un’ulteriore definizione di albero, questa volta di tipo algoritmico. Consideriamo la seguente procedura, detta Growing Tree Procedure (144-145 [4]): 1. Partiamo dal grafo G con un solo vertice. 2. Ripetiamo un qualunque numero di volte i seguenti due passi: 2.1) sia G0 il grafo ottenuto aggiungendo a G un nuovo vertice adiacente a un solo vertice di G; 2.2) sia G := G0 . 13 Lemma 9 Ogni grafo costruito dalla Growing Tree Procedure è un albero e ogni albero può essere costruito dalla procedura Growing Tree Procedure. La dimostrazione del Lemma 9 può ottenersi per induzione sul numero dei vertici sfruttando le seguenti oservazioni: – Sia T (V, E) un albero. Consideriamo il grafo T 0 (V ∪ {v}, E ∪ {uv}), ottenuto aggiungendo a T un nuovo vertice v adiacente a esattamente un vertice u ∈ V . È facile vedere che T 0 è anch’esso un albero. Cominciamo infatti dal mostrare che T 0 è connesso: due vertici (x, y), con x e y 6= v, sono connessi in T 0 cosı̀ com’erano connessi in T ; inoltre v è connesso a qualunque vertice z ∈ V , poiché v è connesso a u e u è connesso a z. Mostriamo quindi che T 0 è aciclico: un qualunque ciclo dovrebbe includere il vertice v, che però ha grado 1, e quindi non può appartenere ad alcun ciclo. T 0 è aciclico e connesso e quindi T 0 è un albero. – Sia T (V, E) un albero con almeno due vertici e v ∈ V una foglia (v esiste, per via del Lemma 6). Consideriamo il grafo T 0 (V − {v}, E \ δ(v)), ottenuto rimuovendo da T la foglia v. È facile vedere che T 0 è anch’esso un albero. T 0 è aciclico: infatti T 0 proviene da un grafo aciclico, cui rimuoviamo uno spigolo e un vertice. Inoltre T 0 è connesso: infatti, due qualunque vertici x, y, con x e y ∈ V (T 0 ), sono connessi in T con un path P che non include v e/o uv, quindi P è anche un path di T 0 . 8.1 Prüfer Code ([12]) • Consideriamo un insieme V = {0, 1, . . . , n − 2, n − 1} di n vertici. Siamo interessati a contare quanti sono i diversi alberi con insieme dei vertici V . Naturalmente, due alberi T (V, E1 ) e T (V, E2 ) sono diversi se e solo esiste uno spigolo i j, con 0 ≤ i < j ≤ n − 1, tale che i j ∈ E1 e i j ∈ / E2 . • Conteremo i diversi alberi in modo “indiretto”. Mostreremo infatti come ad ogni albero T (V, E) possiamo associare in modo univoco un vettore con n − 2 componenti, ognuna delle quali a valore intero tra 0 e n − 1. Mostreremo anche come, viceversa, a ogni vettore con n − 2 componenti, ognuna delle quali a valore intero tra 0 e n − 1, possiamo associare in modo univoco un albero con insieme dei vertici {0, 1, . . . , n − 2, n − 1}. Segue quindi che contare i diversi alberi con insieme dei vertici V è la stessa cosa che contare i diversi vettori, con n − 2 componenti e a valori interi tra 0 e n − 1. Abbiamo quindi il seguente risultato, noto come: Teorema di Cayley I diversi alberi con insieme dei vertici V = {0, 1, . . . , n − 2, n − 1} sono n(n−2) . • Per arrivare a questo risultato, analizzeremo diverse possibili rappresentazioni per gli alberi riferendoci, per esempio, all’albero T̃ = (V, Ẽ) con Ẽ = {01, 04, 13, 16, 17, 24, 45}. 14 • Un primo semplice modo di memorizzare un albero è attraverso la matrice di adiacenza. La matrice di adiacenza di un albero T (V, E) è una matrice binaria A di dimensione |V | × |V | (quindi nel nostro caso n × n) tale che ai j = 1 se e solo se i j ∈ E(T ). Si osservi che A è simmetrica e con la diagonale principale di tutti 0. Naturalmente, ad ogni albero possiamo associare un’unica matrice di adiacenza, e poiché il numero di matrici n × n, simmetriche, n(n−1) binarie e con la diagonale principale di tutti 0 è pari a 2 2 (perché ?), questo ci permette n(n−1) dire che il numero di diversi alberi con insieme dei vertici V è minore o uguale a 2 2 . . . ma di fatto strettamente minore perché ci sono matrici n × n, simmetriche, binarie e con la diagonale principale di tutti 0 che non corrispondono ad alcun albero: per esempio la matrice n × n con tutti 1! Riportiamo infine la matrice di adiacenza dell’albero T̃ : 0 1 0 0 1 0 0 0 1 0 0 1 0 0 1 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 • Un secondo semplice modo di memorizzare un albero è attraverso la lista degli spigoli. Per ottenere questa rappresentazione, per prima cosa ordiniamo la lista degli spigoli in un qualunque modo: sia e1 , . . . , en−1 i nostro ordinamento. Dopo di che memorizziamo il nostro albero attraverso 2 vettori a e b, entrambi di dimensione n − 1, in questo modo: per 1 ≤ h ≤ n − 1, a[h] = i e b[h] = j se e solo se eh = i j. Si osservi che cambiando l’ordinamento degli spigoli lo stesso albero può dare luogo a diverse liste degli spigoli, quindi la rappresentazione non è univoca. Per renderla tale introduciamo un ordinamento lessicografico degli spigoli dell’albero e degli estremi di ogni spigolo: assumiamo quindi che, per 1 ≤ h ≤ n − 1, ah < bh e che per ogni coppia (h, k), con 1 ≤ h < k ≤ n − 1 valga ah < ak oppure ah = ak e bh < bk . La lista degli spigoli dell’albero T̃ è quindi: a = [0 0 1 1 1 2 4] b = [1 4 3 6 7 4 5]. Utilizzando l’ordinamento lessicografico abbiamo la proprietà che ad ad ogni albero T (V, E) corrisponde una sola lista degli spigoli, ma di nuovo la rappresentazione è ridondante e ci sono molte più liste che alberi: per esempio, i vettori a = [0, 1, 2, 3, 4, 5] e b = [1, 2, 3, 0, 5, 6] non corrispondono ad alcun albero (a che grafo corrispondono?). • Si noti tuttavia che, dal punto di vista dell’occupazione di memoria, la lista degli spigoli offre una rappresentazione molto più efficiente della matrice di adiacenza: con la prima possiamo memorizzare un albero con 2(n−1)dlog ne bit, con la seconda abbiamo bisogno bit. di n(n−1) 2 15 • Una evoluzione della lista degli spigoli ci permette di memorizzare un albero con soli (n − 1)dlog ne bit. L’idea, molto semplice, richiede innanzitutto di scegliere un vertice dell’albero: nel seguito assumiamo sia il vertice 0. A questo punto, per ogni spigolo i j dell’albero esiste un vertice, per esempio i, che è più vicino a 0 e un vertice j che è più lontano: diciamo che i è il padre di j. L’osservazione fondamentale è che ogni vertice, tranne 0, ha esattamente un padre (e ogni spigolo connette un vertice al padre). Memorizziamo quindi il nostro albero con la lista degli spigoli, ma usiamo la convenzione di memorizzare in a[1] il vertice 1 e in b[1] il padre di 1, in a[2] il vertice 2 e in b[2] il padre di 2 . . . in a[n − 1] il vertice (n − 1) e in b[n − 1] il padre di (n − 1). Banalmente, segue che a = [1, 2, . . . , n − 1] e possiamo quindi memorizzare il solo vettore dei padri b, che va sotto il nome di father code. Il father dell’albero T̃ è : b = [0 4 1 0 4 1 1]. Il father code è quindi un vettore di dimensione n − 1, in cui ogni elemento è un intero tra 0 e n − 1. Osserviamo che, per n = 7 l’albero corrispondente al code [0, 1, 2, 3, 4, 5] è un path, l’albero corrisponde al code [0, 0, 0, 0, 0, 0] è una stella mentre nessun albero corrisponde a [6, 5, 4, 3, 2, 1, 0]! Quindi, al solito, ad ogni albero T (V, E) possiamo associare in modo univoco un father code (facile da verificare), ma non è vero ad ogni vettore di dimensione n − 1, in cui ogni elemento sia un intero tra 0 e n − 1, possiamo associare un albero. • Una evoluzione del father code è il Prüfer code. Si noti che per il father code gli spigoli sono implicitamente ordinati come segue: per primo consideriamo lo spigolo tra il vertice 1 e il padre del vertice 1. . . per ultimo consideriamo lo spigolo tra il vertice n − 1 e il padre del vertice n − 1. In pratica, il codice di Prüfer ordina gli spigoli in modo diverso. Per comprendere questo ordinamento supponiamo di “smontare” il nostro albero secondo la Growing Tree Procedure. La procedura definisce una sequenza di alberi T1 . . . Tn dove T1 = T e Tn è fatto da un unico vertice: ad ogni passo Th+1 si ottiene da Th rimuovendone una foglia (quindi uno spigolo): in particolare, supponiamo di risolvere le situazioni di parità (quando nell’albero corrente Th ci sono più foglie) in favore della foglia con indice più basso che però non sia il vertice 0. Per esempio, procedendo in questo modo, rimuoveremmo i vertici di T̃ in quest’ordine: 2, 3, 5, 4, 6, 7, 1 fino a rimanere appunto con l’albero T8 costituito dal solo vertice 0. La procedura di rimozione dei vertici determina naturalmente anche un ordinamento degli spigoli di T , che nel caso precedente è : 24, 31, 54, 40, 61, 71,10. Consideriamo la lista degli spigoli corrispondente a questo ordiamente, avendo l’accortezza di mettere sempre nel vettore a e nel vettore b i padri. Nel caso precedente: a = [2 3 5 4 6 7 1] b = [4 1 4 0 1 1 0]. Si osservi come necessariamente b[n − 1] = 0, quindi è inutile memorizzare b[n − 1]. Come mostriamo nel seguito, è anche inutile memorizzare il vettore a che può essere in16 duttivamente ricostruito da b. Le osservazioni chiave sono due: la prima è che in ogni albero un vertice è foglia oppure padre. La seconda è che, per 1 ≤ h ≤ n−1, il (sotto)vettore [b[h]b[h + 1] . . . b[n − 1]] contiene tutti e soli i vertici che sono padri per l’albero Th . Quindi, per costruzione, a[1] deve essere uguale al più piccolo intero compreso tra 0 e n − 1 che non è appartiene al vettore [b[1]b[2] . . . b[n]]. Induttivamente, poi, a[i] deve essere uguale al più piccolo intero compreso tra 0 e n − 1 che non appartiene nè al (sotto)vettore [a[1]a[2] . . . a[h − 1]] (foglie che abbiamo rimosso fino al passo h-esimo) nè al (sotto)vettore [b[h]b[h + 1] . . . b[n − 1]] (vertici che sono padri per Th ). • Memorizziamo quindi solo il vettore b0 = [b[1]b[2] . . . b[n − 2]] (come abbiamo osservato prima, b[n − 1] è sempre 0): è questo codice va sotto il nome di Prüfer code. Il Prüfer code dell’albero T̃ è : b0 = [4 1 4 0 1 1]. Il Prüfer code è quindi un vettore di dimensione n − 2, in cui ogni elemento è un intero tra 0 e n − 1 e, al solito, ad ogni albero T (V, E) possiamo associare in modo univoco un Prüfer code (facile da verificare). Questa volta è possibile dimostrare il viceversa: ad ogni vettore di dimensione n − 2, in cui ogni elemento sia un intero tra 0 e n − 1 possiamo associare in modo univoco un albero con insieme dei vertici {0, 1, . . . , n − 2, n − 1} (omettiamo i dettagli, peraltro semplici, di questa dimostrazione). Naturalmente questi due fatti insieme implicano il Teorema di Cayley menzionato in precedenza. • Per concludere osserviamo due fatti molto interessanti. Quanto illustrato in precedenza mostra che possiamo memorizzare un albero con n vertici con (n − 2)dlog ne bit, e che non è possibile fare di meglio. Inoltre, utilizzando il codice di Prüfer è possibile generare in modo random un albero con n vertici in modo tale che tutti gli alberi con n vertici compaiano con la stessa probabilità : questo sarebbe molto difficile da fare se non utilizassimo il codice di Prüfer! References [1] https://en.wikipedia.org/wiki/Seven Bridges of Königsberg [2] N.L. Biggs, E.K. Lloyd, R.J. Wilson. Graph Theory 1736-1936. Clarendon Press Oxford. [3] D. Jungnickel. Graphs, Networks and Algorithms: Capitolo 1. Springer. [4] L. Lovász, J. Pelikán, K.Vesztergombi Discrete Mathematics. Springer. [5] K.H.Rosen. Discrete Mathematics and its Applications. Mc Graw-Hill. [6] http://en.wikipedia.org/wiki/Mathematical induction [7] https://en.wikipedia.org/wiki/Equivalence relation 17 [8] https://en.wikipedia.org/wiki/Handshaking lemma [9] https://en.wikipedia.org/wiki/Route inspection problem [10] https://sites.google.com/site/italianodidattica/didattica/ingegneria-degli-algoritmi [11] https://en.wikipedia.org/wiki/Eulerian path [12] https://en.wikipedia.org/wiki/Prüfer sequence 18