Capitolo dodicesimo Problemi di Percorso Introduzione I problemi di determinazione di percorsi ottimi sono tra i più noti problemi di ottimizzazione su rete. Essi si presentano in innumerevoli campi, nella pianificazione dei trasporti e nella gestione del traffico, nelle telecomunicazioni, nell’ingegneria dell’informazione, nella gestione aziendale. Su una rete connessa, per una assegnata coppia di vertici o/d (origine/destinazione), possono esistere uno o più percorsi (su un grafo fortemente connesso almeno uno), ciascuno caratterizzato da un valore di costo o di profitto, calcolato come somma dei valori di costo e di profitto degli archi che costituiscono il percorso stesso. Il problema che si incontra più frequentemente nelle applicazioni reali è quello del minimo percorso, che consiste nell’individuare il percorso al quale corrisponde il costo minimo C o d . In questo capitolo si dà grande spazio al problema di minimo percorso. Si formula inizialmente il modello relativo ad una singola coppia o/d. Si presenta quindi la classificazione dei problemi e degli algoritmi e vengono descritti i principali algoritmi utilizzabili per la soluzione dei problemi definiti nella classificazione. Viene descritto inoltre un altro problema di percorso che pure interviene nelle pratiche applicazioni. Si definisce infatti il problema del percorso massimo e si illustra un semplice algoritmo risolutivo. 288 Capitolo dodicesimo 12.1 IL MODELLO DEL MINIMO PERCORSO PER UNA COPPIA o/d Si consideri la rete riportata in figura 12.1. Bisogna determinare il percorso di minimo costo dal vertice 1 al vertice 5. In tabella 12.1 sono riportati tutti i possibili percorsi tra 1 e 5 con i relativi costi, in termini di matrice di incidenza arco-percorso (così come definita nel paragrafo 11.3.1). Per trovare la soluzione ottima del problema (il percorso p 1 : 1-3-4-5 di costo 7) bisogna determinare quali archi appartengono al percorso di minimo costo. FIG. 12.1 Una semplice rete per la descrizione del modello 5 1 5 9 2 3 2 3 2 3 4 3 TAB. 12.1 Matrice di incidenza arco-percorso per la coppia 1-5 archi 1-2 1-3 2-3 2-4 2-5 3-4 4-5 cij 5 2 3 3 9 3 2 Costo del percorso p1 0 1 0 0 0 1 1 7 p2 1 0 1 0 0 1 1 13 p3 1 0 0 1 0 0 1 10 p4 1 0 0 0 1 0 0 14 Il problema può essere formulato attraverso un modello in programmazione intera binaria. Indicando con o e d i vertici origine e destinazione del percorso minimo da individuare, con c i j il costo associato all’arco generico i j (i j ∈A) e con x i j una variabile binaria pari a 1 se l’arco i j appartiene al percorso minimo, pari a 0 altrimenti, il modello assume la seguente configurazione, nella quale ∑ i sta per ∑ i : i j ∈ A : Problemi di Percorso 289 Min z = ∑ i j ∈ A c i j x i j s.a ∑k xok = 1 ∑i xid = 1 ∑ k x j k − ∑ i x i j = 0 (∀j ≠o,d ) x i j = 0/1 Il primo vincolo esprime la condizione che uno solo degli archi uscenti dall’origine o può appartenere al percorso minimo e quindi una sola delle corrispondenti variabili può essere pari ad 1. Analogamente il secondo vincolo esprime la condizione che uno solo degli archi entranti nella destinazione d può appartenere al percorso minimo. Il terzo vincolo va scritto per ogni vertice j diverso da o e d ed esprime una condizione di bilancio: nel vertice generico j se c’è un arco uscente appartenente al percorso, ci deve essere anche un arco entrante appartenente al percorso, se non c’è un arco uscente non c’è neanche un arco entrante. Come si può facilmente vedere tutte le soluzioni riportate in tabella 12.1 rispettano questi vincoli. L’algoritmo risolutivo sceglierà tra esse la migliore. Questo modello ha una struttura particolare. Nella matrice A dei coefficienti associata ai vincoli di continuità le righe corrispondono ai vertici della rete e le colonne corrispondono agli archi. Essa ha dunque dimensioni vxa , se v è il numero di vertici e a il numero di archi della rete. La matrice A corrisponde quindi alla matrice di incidenza vertice-arco. In essa ogni colonna ha soltanto due elementi non nulli, +1 e −1. Infatti la colonna corrispondente all'arco i j contiene +1 nella riga i, −1 nella riga j e 0 altrove. La riga corrispondente al vertice i invece contiene +1 nelle colonne corrispondenti agli archi i k uscenti da i, −1 nelle colonne corrispondenti agli archi j i entranti in i, 0 altrove. Si può dimostrare che la matrice A dei tassi di assorbimento è totalmente unimodulare. Una matrice è totalmente unimodulare se il determinante di ogni minore da essa estratto ha valore −1, 0 o +1. Si può dimostrare in questo caso che la soluzione ottima del modello è intera anche se non si impone il vincolo di interezza x i j = 0/1, a condizione che i termini noti siano interi. In questo caso i termini noti dei vincoli sono pari a 0 e ad 1 e pertanto le variabili assumeranno valore ottimo intero. Il modello può essere risolto dunque in programmazione lineare continua, con i vincoli 0 ≤ x i j ≤ 1 in luogo di x i j = 0/1. Per avere una conferma di ciò, può essere utile operare una rappresentazione grafica del modello di minimo percorso nel caso semplice di un problema con due variabili. Si consideri a questo 290 Capitolo dodicesimo scopo il semplice caso di una rete costituita da due nodi e dai due archi che li congiungono (Fig.12.2). Il nodo 1 è il nodo origine del percorso, il nodo 2 è il nodo destinazione del percorso. Si vuol determinare il minimo percorso tra i due nodi. Si supponga che il costo di spostamento sui due archi sia costante (c (1-2)' = 10 e c (1-2)'' = 5). I vincoli esprimono il bilancio nel nodo 1 (o nel nodo 2), x(1-2)' + x(1-2)'' = 1. Il modello avrà dunque la seguente formulazione: Min z = 10 x (1-2)' + 5 x (1-2)'' s.a x (1-2)' + x (1-2)'' = 1 0 ≤ x (1-2)' ≤ 1 0 ≤ x (1-2)'' ≤ 1 Il dominio, riportato in figura 12.3, è costituito dal segmento CD. I vertici del dominio, che sono anche i punti interi ammissibili del problema intero, sono C (x (1-2)' =0, x (1-2)'' =1, z =5) e D (x (1-2)' =1, x (1-2)'' =0, z =10). La soluzione ottima del problema è rappresentata dal punto C cui corrisponde il valore minimo di z , pari a 5. FIG. 12.2 Un semplice problema di minimo percorso in due variabili 1 (1-2)' (1-2)'' 2 FIG. 12.3 Analisi grafica del problema di minimo percorso per il grafo di fig.12.2 x(1-2)'' 1 C z = 10 z=0 O z=5 D 1 x(1-2)' Problemi di Percorso 12.2 CLASSIFICAZIONE DEI PROBLEMI E DEGLI ALGORITMI DI 291 MINIMO PERCORSO Si individuano in generale le seguenti classi di problemi nella determinazione del minimo percorso: a) da un vertice ad un altro vertice della rete; b) da un vertice a tutti gli altri vertici della rete (o a un sottoinsieme); c) da tutti i vertici a tutti gli altri vertici (o da un sottoinsieme di vertici ad un altro sottoinsieme di vertici), cioè per tutte le coppie di vertici individuabili sulla rete. Il problema della classe (a), per il quale è stato formulato il modello del paragrafo precedente, è chiaramente contenuto nella classe (b), che, a sua volta, è contenuta nella classe (c). La soluzione del problema (a) genera, come si è detto, un valore di costo minimo C o d per la singola coppia o-d. La soluzione del problema (b) genera valori di costo minimo C o k da un vertice o a tutti i vertici k della rete. La soluzione del problema (c) genera tutti i valori di costo minimo C o d da tutti i vertici origine a tutti i vertici destinazione. E’ possibile quindi definire una matrice C delle distanze minime (o dei costi minimi) il cui generico elemento C o d fornisce il valore del minimo percorso tra i vertici o e d. Il problema delle classi (b) e (c) viene risolto nelle pratiche applicazioni mediante algoritmi “ad hoc” che sono disponibili in grande varietà (la letteratura degli algoritmi di minimo percorso è veramente sterminata). Questi algoritmi possono essere classificati in base al problema che risolvono. La distinzione che più di frequente si adotta è tra: 1) algoritmi arborescenti e 2) algoritmi matriciali. I primi si definiscono in questo modo perché una singola implementazione dell’algoritmo genera l’arborescenza (l’albero) dei minimi percorsi da un vertice origine a tutti gli altri vertici assunti come destinazione. Essi risolvono dunque la classe (b) dei problemi e quindi naturalmente anche la classe (a). La costruzione dell’arborescenza dei minimi percorsi con origine nel vertice i generico fornisce la riga i -esima della matrice C delle distanze minime. 292 Capitolo dodicesimo I secondi invece risolvono la classe (c) dei problemi e dunque forniscono in una singola implementazione la matrice C delle distanze minime tra tutte le coppie di vertici individuabili sulla rete. Non c’è naturalmente una rigida corrispondenza tra classe di problema e tipo di algoritmo idoneo a risolverlo. E’ possibile infatti applicare un algoritmo arborescente per ciascuno dei vertici origine della rete (dunque al limite v volte) per risolvere il problema (c) del cammino minimo per tutte le coppie di vertici, così come sarebbe possibile applicare un algoritmo matriciale ed utilizzare solo parte dei risultati ottenuti per dare risposta ad un problema del tipo (b). Nei paragrafi che seguono si descrivono i fondamentali algoritmi arborescenti e matriciali utilizzati per la soluzione del problema. 12.3 ALGORITMI ARBORESCENTI Si prenda ancora in considerazione il grafo di figura 12.1 e si calcoli il minimo percorso dal vertici 1 a ciascuno degli altri vertici. Si può agevolmente verificare che il minimo percorso da 1 a 2 è l’arco 1-2 di costo 5, da 1 a 3 è l’arco 1-3 di costo 2, da 1 a 4 è il percorso 1-3-4 di costo 5, da 1 a 5 è il percorso 1-3-4-5 di costo 7. Il risultato complessivo di questa operazione è in figura 12.4. Si può vedere che i percorsi determinati, riportati in tratto spesso, costituiscono un’arborescenza con radice in 1. FIG. 12.4 Arborescenza dei minimi percorsi con origine in 1 (5) 5 (0) 1 5 (7) 9 2 3 2 3 2 3 4 (5) 3 (2) Il problema della determinazione dei minimi percorsi da un vertice o della rete R(V, A, C ) a tutti gli altri vertici, può essere ricondotto quindi a Problemi di Percorso 293 quello della determinazione di una arborescenza T(V, AT) con radice in o, dove A T è l’insieme degli archi dell’arborescenza. I metodi proposti per la soluzione di questo problema sono metodi di etichettamento dei vertici e possono essere divisi in due classi: - metodi “label setting” - metodi “label correcting” Entrambi i metodi partono con un’arborescenza T(V T , A T ) tale che V T ={o} ed A T = ∅. I metodi del tipo label setting ad ogni iterazione fissano il valore del minimo percorso dalla origine ad uno o più vertici della rete. Un metodo label setting in ciascuna iterazione incrementa V T ed A T rispettivamente con un vertice v∈V ed un arco (u ,v)∈A, in modo tale che u ∈V T , v∈V T e che l’unico percorso da o a v in T sia un minimo percorso. Esso termina quando tutti gli archi di A che hanno il vertice origine in V T hanno anche il vertice destinazione in V T . Per il loro modo di lavorare questi algoritmi forniscono risultati intermedi di minimo percorso dall’origine fino ad un vertice destinazione che sono definitivi e quindi possono essere utilizzati prima ancora che l’algoritmo termini completamente. I metodi del tipo label correcting ad ogni iterazione aggiornano il vettore dei minimi percorsi da O a tutti gli altri vertici, e quindi solo dopo l’ultima iterazione determinano simultaneamente i minimi percorsi tra l’origine fissata e tutti gli altri vertici. Un metodo label correcting incrementa o modifica in ciascuna iterazione gli archi di A T , in modo da aggiornare il percorso dalla radice o a ciascun vertice v in T, ma non garantisce che il nuovo percorso sia minimo, fino al termine della procedura. Questi algoritmi forniscono risultati intermedi provvisori che non hanno alcun significato pratico e dunque non possono essere utilizzati. Nel seguito si descrivono tre algoritmi arborescenti: l’algoritmo di Dantzig, del tipo label setting, l’algoritmo di Dijkstra, di tipo misto (label settingcorrecting), l’algoritmo di Ford-Moore-Bellman, di tipo label correcting. 12.3.1 ALGORITMO DI DANTZIG L’algoritmo di Dantzig è un algoritmo arborescente del tipo label setting. Sia o il vertice origine. Si supponga di conoscere al k° stadio della procedu- 294 Capitolo dodicesimo ra i cammini minimi da o a k vertici. Si indichi questo insieme di vertici con S (Fig.12.5). Poiché gli algoritmi arborescenti generano progressivamente un albero di minimi percorsi, i vertici di S sono collegati in modo da costituire un albero parziale. L’insieme V dei vertici può essere dunque suddiviso in due sottoinsiemi: S, contenente i vertici etichettati definitivamente con il valore di minimo costo dall’origine o, e (V-S), contenente i vertici per i quali il minimo percorso non è stato ancora determinato e quindi non hanno ancora una etichetta definitiva. Si indichi con: i il generico vertice foglia in S; δ i la distanza minima di i dall’origine o; j i il vertice più vicino ad i , non in S, collegato ad i da un arco i , j i ; a i la lunghezza dell’arco i , j i . Tra tutti i vertici j i si sceglierà come vertice da inserire in S il vertice (o i vertici) j r , tale che δ r + a r = Mini = 1 , k (δ i + a i ). Il valore δ r +a r fornisce certamente il valore minimo del percorso da o a j r in quanto ogni altro percorso da o a j r utilizzerebbe un vertice t non in S avente una distanza minima da o pari a δ t + a t > δ r + a r . A maggior ragione quindi si verificherebbe δ t + a t + a t , r > δ r + a r . Questo step fondamentale dell’algoritmo si itera inserendo ogni volta uno o più nuovi vertici nell’insieme S. L’algoritmo termina quando tutti i vertici del grafo fanno parte di S . FIG. 12.5 Step generico dell’algoritmo di Dantzig l δl S al jl S' O ai δi i ji Problemi di Percorso 295 Struttura dell’algoritmo E’ opportuno effettuare una operazione di pre-processing costruendo per ciascun vertice la lista degli archi uscenti ordinati per costo crescente. Inoltre, nel corso dell’algoritmo, vengono eliminati dalle liste gli archi che hanno per destinazione un vertice già etichettato definitivamente, cioè già appartenente ad S . In questo modo i passi dell’algoritmo sono i seguenti: Passo 0: L’insieme S dei vertici etichettati contiene la sola origine o, si pone C o = 0. Si cancellano, nelle liste create, tutti gli archi con destinazione o. Passo 1: Per ogni vertice i ∈ S, per il quale non siano stati cancellati tutti gli archi della lista relativa, si calcola C i + mink { c i k }= γ i , dove mink { c i k } è il peso del primo arco non cancellato della lista relativa al vertice i . Passo 2: Si confrontano i valori γ i (i ∈S ). Il valore γ s = mini { γ i }= c s + mint { c s t }, indica il vertice t da introdurre in S . Si pone C t = γ s e si memorizza s come predecessore di t. Passo 3: Si cancellano nelle diverse liste gli archi con destinazione t . Passo 4: Se S ≡V l’algoritmo ha termine, altrimenti si torna al passo 1. Esempio numerico Si consideri il grafo orientato con 8 vertici e 13 archi riportato in figura 12.6. Si vuole calcolare l’arborescenza dei minimi percorsi con origine nel vertice 1. In tabella 12.2 si riportano le liste degli archi uscenti dai vertici ordinate per costo crescente. L’insieme S contiene inizialmente il solo vertice 1 cui si associa un valore del minimo percorso pari a 0. Gli archi “uscenti” dall’insieme S sono gli archi uscenti dal vertice 1: 1-2 e 1-3. Tra essi si sceglie l’arco 1-2, cui corrisponde il costo minimo, e si associa al vertice 2 un valore di cammino minimo pari a 0+1=1. Si cancellano dalle liste tutti gli archi che eventualmente hanno destinazione nel vertice 2. Il vertice 1 viene memorizzato come predecessore del vertice 2. L’insieme S contiene ora i vertici 1 e 2. Le possibilità di “uscita” dall’insieme S sono riportate di seguito con i relativi costi: Arco costo 1-3 0 + 2 = 2(*) 2-3 1+3=4 2-4 1+3=4 2-5 1+3=4 296 Capitolo dodicesimo FIG. 12.6 Grafo G e costi c i j 1 5 3 2 3 3 6 2 1 1 3 2 3 3 4 3 7 1 3 8 4 TAB. 12.2 Insiemi di archi uscenti dai vertici 1 c1j 2 c2j 3 c3j 4 c4j 5 c5j 6 c6j 1-2 1 2-3 3 3-4 3 4-5 2 5-6 3 6-7 1 1-3 2 2-4 3 3-8 4 4-6 3 2-5 3 4-8 3 7 c7j 8 c8j 8-7 1 All’arco 1-3 corrisponde il costo minimo e quindi il vertice 3 entra nell’insieme S, con predecessore 1, con un valore di cammino minimo pari a 2. L’insieme S contiene ora i vertici 1, 2, 3. A questo punto si determinano le seguenti possibilità: Arco costo 2-5 2-4 1 + 3 = 4(*) 1 + 3 = 4(*) 3-4 2+3=5 3-8 2+4=6 Entrano in S i vertici 4 e 5, cui si associa un valore del cammino minimo pari a 4 . Predecessore del vertice 5 è il vertice 2. Predecessore del vertice 4 è il vertice 2. Si cancellano tutti gli archi che hanno come destinazione i vertici 4 e 5. L’insieme S contiene ora i vertici 1, 2, 3, 4, 5. Si ha pertanto: Arco costo 3-8 2 + 4 = 6(*) 4-8 4+3=7 4-6 4+3=7 5-6 4+3=7 Entra in S il vertice 8, con predecessore 3, cui si associa un valore del cammino minimo pari a 6. L’insieme S contiene ora i vertici 1, 2, 3, 4, 5, 8. Problemi di Percorso 297 Si ha pertanto: Arco costo 4-6 5-6 8-7 4 + 3 = 7(*) 4 + 3 = 7(*) 6 + 1 = 7(*) Entrano in S i vertici 6 e 7 cui si associa un valore del cammino minimo pari a 7. Predecessore del vertice 7 è il vertice 8. Si noti che il vertice 6 ha due possibili predecessori, il vertice 4 o il 5. Essendo tutti i vertici etichettati, il procedimento ha termine. In figura 12.7 è riportata l’arborescenza dei minimi percorsi con origine nel vertice 1. FIG. 12.7 Arborescenza (albero) dei minimi percorsi con origine in 1 1 2 1 4 5 3 7 6 3 1 4 4 2 3 2 3 4 7 6 8 7 1 Complessità computazionale Se v è il numero dei vertici, questo algoritmo richiede v 2 /2 addizioni e 2 v /2 confronti per il calcolo dei valori da assegnare ai vertici. Richiede inoltre v 2 l o g v confronti aggiuntivi per l’ordinamento preliminare dei dati e 3v 2 confronti per l’aggiornamento dei dati che è necessario operare nel corso dell’algoritmo (cancellazione dalle liste degli archi che hanno come vertice destinazione un vertice già etichettato). La sua complessità computazionale è dunque O (v 2 l o g v). 298 Capitolo dodicesimo 12.3.2 ALGORITMO DI DIJKSTRA L’algoritmo di Dijkstra assegna a ciascuno dei v vertici del grafo dei valori di tentativo, che costituiscono dei limiti superiori al valore del cammino minimo dal vertice origine ad essi. In ciascuna iterazione dell’algoritmo un valore di etichetta viene reso definitivo. Dopo un numero di iterazioni al massimo pari a quello dei vertici destinazione, le etichette diventano tutte definitive e forniscono i valori dei cammini minimi dal vertice origine a tutti gli altri. Questo algoritmo presenta quindi un meccanismo misto nella determinazione delle etichette. Può essere definito quindi un algoritmo arborescente del tipo label setting-correcting. Si assegna inizialmente al vertice origine il valore definitivo 0 ed a tutti gli altri vertici il valore di tentativo ∞. Si parte quindi dall’origine e si va negli altri vertici calcolando il valore del costo del percorso come somma dell’etichetta dell’origine e del costo dell’arco utilizzato (∞ qualora l’arco non esista). Per ciascun vertice si confronta il valore così calcolato con l’etichetta precedente. Il valore minore viene assunto come nuovo valore di tentativo del minimo percorso verso quel vertice. Tra i v−1 valori di tentativo ottenuti si sceglie il minore e lo si assume come etichetta definitiva del vertice cui corrisponde. Si supponga essere k il vertice etichettato definitivamente nel passo di procedura precedente. Si parte quindi dal vertice k e si va negli altri vertici non ancora etichettati definitivamente, calcolando il valore del costo del percorso come somma dell’etichetta del vertice k e del costo dell’arco utilizzato (∞ qualora l’arco non esista). Per ciascun vertice si confronta il valore così calcolato con l’etichetta precedente ed il valore minimo viene assunto come nuovo valore di tentativo. Tra i v−2 valori di tentativo ottenuti si sceglie il minore e lo si assume come etichetta definitiva del vertice cui corrisponde, per esempio j , assumendolo come punto di partenza per un ulteriore step della procedura. A differenza di quanto avviene nell’algoritmo di Dantzig, in ogni iterazione ci si muove dunque dall’ultimo vertice etichettato definitivamente e non da tutto l’insieme dei vertici etichettati definitivamente. Dopo al massimo v−1 iterazioni (nel caso in cui ad ogni iterazione venga etichettato definitivamente un solo vertice) tutti i vertici sono etichettati definitivamente e il procedimento ha termine. Problemi di Percorso 299 Struttura dell’algoritmo Passo 0: L’insieme S contiene la sola origine 0. Si pone C o = 0 [etichetta definitiva] per i ≠ o, si pongono le etichette di tentativo: C i = ∞ [se l’arco o-i non esiste] C i = c o i [se l’arco o-i esiste] (∀ i ≠ o) Si pone inoltre: predecessore (i )=o. Passo 1: Se t è l’ultimo vertice introdotto in S , per ogni vertice i non appartenente ad S, si calcola m = min [C i ; C t + c t i ]. Se risulta m < C i , si aggiorna: C i = m , predecessore (i )=t. Passo 2: Si sceglie il vertice, non appartenente ad S, cui corrisponde il valore mini C i . Si introduce il vertice in S. Passo 3: Se S ≡V l’algoritmo ha termine altrimenti si torna al passo 1. Esempio numerico Si consideri il grafo di figura 12.6 già utilizzato. In tabella 12.3 si riporta un quadro riassuntivo delle operazioni effettuate dall’algoritmo di Dijkstra per determinare l’albero dei minimi percorsi con origine nel vertice 1. Ciascuna riga corrisponde ad un vertice del grafo. Le colonne corrispondono alle iterazioni. I simboli adoperati corrispondono alla assegnazione di valore (→), al confronto (::), ed alla somma di valori (+). Nella iterazione 0 viene assegnata l’etichetta definitiva 0 al vertice origine 1 e le etichette di tentativo ∞ a tutti gli altri vertici. Il predecessore di 1 viene posto per definizione pari a 0. Nella iterazione 1 si parte dal vertice 1 e vengono calcolate le nuove etichette dei vertici. Queste vengono confrontate con le precedenti e per ciascun vertice si sceglie il valore di tentativo migliore. L’etichetta del vertice 2, pari ad 1, è la minima e viene resa definitiva. Ogni valore di etichetta definitiva porta con sé un predecessore, indicato nello schema in corsivo e tra parentesi. Il predecessore del vertice 2 è il vertice 1. Nella iterazione 2 si parte dal vertice 2, che è quello etichettato per ultimo, e vengono calcolate le nuove etichette, che vengono confrontate con le precedenti, scegliendo per ciascun vertice la migliore. L’etichetta del vertice 3, pari a 2, è la minima e viene resa definitiva. Il predecessore del vertice 3 è il vertice 1. 300 Capitolo dodicesimo TAB. 12.3 Operazioni effettuate dall’algoritmo di Dijkstra per il grafo di figura 12.6 Iterazioni 0 1 Vertici → Etichette provvisorie iniziali 2 3 4 5 6 7 Nuove etichette provvisorie e (predecessori) 1→0 2→∞ 3→∞ 4→∞ 5→∞ 6→∞ 7→∞ 8→∞ :: 0+1 1 (1) :: 0+2 2 (1) :: 0+∞ ∞ :: 0+∞ ∞ :: 0+∞ ∞ :: 0+∞ ∞ :: 0+∞ ∞ :: 1+3 2 (1) :: 1+3 :: 2+3 4 (2) 4 (2) :: 1+3 :: 2+∞ :: 4+2 4 (2) 4 (2) 4 (2) :: 1+∞ :: 2+∞ :: 4+3 :: 4+3 :: 6+∞ 7 (4) 7 (4) 7 (4) ∞ ∞ :: 1+∞ :: 2+∞ :: 4+∞ :: 4+∞ :: 6+1 7 (8) ∞ ∞ ∞ ∞ :: 1+∞ :: 2+4 :: 4+3 :: 4+∞ 6 (3) 6 (3) 6 (3) ∞ ↓ Etichette definitive e predecessori ↓ :: 7+∞ 7 (4) C(1) = 0 C(2) = 1 C(3) = 2 C(4) = 4 C(5) = 4 C(8) = 6 C(7) = 7 C(6) = 7 p (1) = 0 p (2) = 1 p (3) = 1 p (4) = 2 p (5) = 2 p (8) = 3 p (7) = 8 p (6) = 4 Nella iterazione 3 si parte dal vertice 3. L’etichetta relativa al vertice 4 (4) viene resa definitiva con predecessore 2. Nella iterazione 4 si parte dal vertice 4. L’etichetta del vertice 5 (4) viene resa definitiva, con predecessore 2. Nella iterazione 5 si parte dal vertice 5. L’etichetta del vertice 8 (6), viene resa definitiva con predecessore 3. Nella iterazione 6 si parte dal vertice 8. L’etichetta del vertice 7 (7), viene resa definitiva con predecessore 8. Nella iterazione 7 si parte dal vertice 7. L’etichetta del vertice 6 (7), viene resa definitiva con predecessore 4. Problemi di Percorso 301 Essendo stati etichettati definitivamente tutti i vertici la procedura ha termine. La soluzione finale corrispondente all’albero dei minimi percorsi viene ricostruita utilizzando il vettore dei predecessori di ciascun vertice. Complessità computazionale Se v è il numero dei vertici, questo algoritmo richiede v(v−1)/2 addizioni e v(v−1)/2 confronti per il calcolo dei valori di tentativo. Richiede inoltre v(v−1)/2 confronti per determinare in ogni iterazione il valore minimo tra le etichette di tentativo. Dunque, nel caso in cui vengano effettuate v−1 iterazioni, sono richieste v(v−1)/2 addizioni e v(v−1) confronti. Per indicare se il valore di etichetta è definitivo o provvisorio si può collegare a ciascun vertice un indice 0/1. Per la consultazione di questo valore sono richiesti (v−1)2 confronti. In totale quindi questo algoritmo richiede v 2 /2 addizioni e 2v 2 confronti. La sua complessità è dunque O (v 2 ). Con una struttura dati del tipo heap la sua complessità è invece O (a log v ). 12.3.3 ALGORITMO DI FORD-MOORE-BELLMAN L’algoritmo di Ford-Moore-Bellman è un algoritmo arborescente del tipo label correcting che opera sulla matrice di adiacenza della rete. Supponendo di essere giunti alla k-esima iterazione, l’etichetta f i ( k ) associata al generico vertice i rappresenta il valore del cammino minimo che congiunge il vertice origine al vertice i con un numero di archi al più pari a k+1. L’insieme delle etichette f i ( k ) , per i =1,..v, costituisce il vettore f ( k ) . Indicando con 1 l’origine dell’arborescenza, l’algoritmo viene inizializzato ponendo f 1 ( 0 ) = 0; f i ( 0 ) = c 1 i (i ≠1). Ad ogni iterazione viene applicata la seguente relazione: f i ( k ) = minj ≠ i [ f j ( k - 1 ) + c j i ] ∀i = 2, 3,..., v; j = 1, 2,...,v (12.1) In base a questa relazione f i ( k ) viene calcolato con la cosiddetta operazione di minisomma tra il vettore f ( k - 1 ) e la colonna i della matrice dei costi associati agli archi. Tale operazione consiste nel sommare gli elementi di posto omologo dei due vettori e nello scegliere tra le somme ottenute il valore minimo. L’esistenza di accoppiamenti a somma non nulla e finita indica infatti l’esistenza di cammini al più di ordine k+1 tra l’origine ed il vertice i. Tra questi si sceglie quello di costo minimo. La procedura termina quando si verifica [ f i ( k - 1 ) = f i ( k ) , i = 1,...,v], ovvero quando, in due iterazioni successive, i valori delle etichette assegnate ai ver- 302 Capitolo dodicesimo tici non variano. A questo punto i valori di tutte le etichette vengono dichiarati definitivi. Poiché un cammino può essere costituito al massimo da v−1 archi, le iterazioni sono al più v−2, v essendo il numero dei vertici. La individuazione della composizione dei cammini minimi può essere ottenuta memorizzando ad ogni iterazione per ciascun vertice i il valore di j in corrispondenza del quale è stato determinato f i ( k ) . In questo modo ad ogni iterazione si ottiene un vettore delle precedenze che viene aggiornato nelle iterazioni successive tutte le volte in cui si verifica un aggiornamento dei valori f i ( k ) . Struttura dell’algoritmo Passo 0: Si pone f 1 ( 0 ) = 0; f i (0) = c 1 i (i ≠1); Si calcola f i ( k ) = minj ≠ i [ f j ( k - 1 ) + c j i ], ∀i = 2, 3,…, v, ∀j = 1, 2,…, v Passo 2: Si confronta f i ( k ) con f i ( k - 1 ) , i = 1,..., v Se f i ( k ) = f i ( k - 1 ) , i = 1,..., v, l’algoritmo ha termine altrimenti si ritorna al passo 1; Passo 1: Esempio numerico Si prenda in considerazione il grafo già utilizzato per le applicazioni numeriche relative agli altri algoritmi di minimo percorso (Fig.12.6). In tabella 12.4 è riportata la matrice dei costi associati agli archi, indicando con un tratto il valore di costo infinito, relativo all’assenza dell’arco. TAB. 12.4 Matrice dei costi della rete di figura 10.4 1 2 3 4 5 6 7 8 1 0 - 2 1 0 - 3 2 3 0 - 4 3 2 0 - 5 3 2 0 - 6 3 3 0 - 7 1 0 1 8 4 3 0 Problemi di Percorso 303 L’algoritmo viene inizializzato ponendo: f ( 0 ) = [0 , 1 , 2 , - , - , - , - , - ]. Questo vettore fornisce i minimi percorsi di ordine 1 tra l’origine 1 e tutti gli altri vertici. Il vettore f ( 1 ) viene calcolato utilizzando la relazione (12.1): f i ( k ) = minj ≠ i [ f j ( k - 1 ) + c j i ]; i = 2,3,...,8; j = 1, 2,...,8. Per il calcolo delle componenti del vettore f ( 1 ) si ottiene : f1(1) = 0 f 2 ( 1 ) = minj ≠ 2 [ f j ( 0 ) +c j 2 ] = = min [ f1(0)+c12, f3(0)+c32, f4(0)+c42, f5(0)+c52, f6(0)+c62, f7(0)+c72, f8(0)+c82] = min [0+1, 2+ ∞, ∞ + ∞, ∞ + ∞, ∞ + ∞, ∞ + ∞, ∞ + ∞] = 1 Il valore f 2 ( 1 ) = 1 rappresenta il valore del minimo percorso al più di ordine 2 tra il vertice 1 e il vertice 2. f 3 ( 1 ) = minj ≠ 3 [f j ( 0 ) +c j 3 ] = = min [ f1(0)+c13, f2(0)+c23, f4(0)+c43, f5(0)+c53, f6(0)+c63, f7(0)+c73, f8(0)+c83] = min [0+2 , 1+3 , ∞ + ∞ , ∞ + ∞ , ∞ + ∞ , ∞ + ∞ ,∞ + ∞] = 2 Il valore f 3 ( 1 ) rappresenta il valore del cammino minimo tra il vertice 1 e il vertice 3, al più di ordine 2. In modo analogo è possibile calcolare i valori delle altre componenti del vettore f (1): f 4 ( 1 ) = 4 , f 5 ( 1 ) = 4 , f 6 ( 1 ) = ∞ , f 7 ( 1 ) = ∞ , f 8 ( 1 ) =6, cioè i valori dei minimi percorsi al più di ordine 2 dal vertice 1 ai vertici 4, 5, 6, 7, 8. Il vettore f (1) viene utilizzato per determinare il vettore f ( 2), cioè i valori dei minimi percorsi al più di ordine 3 dal vertice 1 a tutti gli altri. I valori assunti dal vettore f (k) nelle successive iterazioni sono riassunti nella tabella 12.5. Si può notare che f (3) = f (2). Ciò significa che non esistono cammini minimi di ordine superiore a 3 dal vertice 1 a tutti gli altri. L’algoritmo quindi ha termine. TAB. 12.5 Valori assunti da f (k) nelle iterazioni dell’algoritmo k=0 k=1 k=2 k=3 f1(k) f2(k) f3(k) f4(k) f5(k) f6(k) f7(k) f8(k) 0 1 2 ∞ ∞ ∞ ∞ ∞ 0 1 2 4 4 6 ∞ ∞ 0 1 2 4 4 7 7 6 0 1 2 4 4 7 7 6 304 Capitolo dodicesimo 12.4 ALGORITMI MATRICIALI Il calcolo della matrice C dei minimi percorsi può essere effettuato utilizzando un algoritmo di tipo matriciale, in grado di calcolare tutti gli elementi della matrice delle distanze minime. 12.4.1 ALGORITMO DI FLOYD L’algoritmo matriciale più comunemente usato è l’algoritmo di Floyd. Esso lavora su una matrice C, di dimensioni pari al numero di vertici. Tale matrice, inizialmente coincidente con la matrice di adiacenza vertice-vertice, viene modificata nelle successive iterazioni. La k-esima di tali matrici può essere interpretata come quella che fornisce il costo dei cammini minimi per tutte le coppie di vertici della rete, caratterizzati dalla proprietà di utilizzare soltanto i vertici numerati da 1 a k. Alla fine del procedimento la matrice contiene quindi i valori dei cammini minimi tra tutte le coppie di vertici del grafo che utilizzano tutti i vertici del grafo. Un algoritmo matriciale è quindi per definizione un algoritmo label correcting. L’algoritmo viene inizializzato ponendo C i i = 0 ed inoltre per i ≠j : C i j ( 0 ) = cij se l’arco i j esiste C i j = ∞ se l’arco i j non esiste Alla k+1-esima iterazione i valori di C vengono aggiornati utilizzando la seguente relazione, in cui compaiono i valori di costo calcolati utilizzando i vertici numerati da 1 a k: C i , j ( k + 1 ) = min [C i , j ( k ) ; C i , k + 1 ( k ) + C k + 1 , j ( k ) ] In particolare: Ci,j(k) Ci,k+1(k) Ck+1,j(k) è il costo minimo da i a j ; è il costo minimo da i a k+1 è il costo minimo da k+1 a j . Se l’utilizzazione del vertice k+1 consente di produrre un cammino di costo inferiore a quello di cui già si disponeva, si sostituisce C i , j ( k ) con il nuovo valore ottenuto. Dopo la (k+1)-esima iterazione il generico elemento C i j ( k + 1 ) fornisce, dunque, il valore del minimo percorso tra i vertici i e j ottenuto utilizzando i vertici il cui indice non sia superiore a k+1. Problemi di Percorso 305 Se v è il numero di vertici l’algoritmo effettua esattamente v iterazioni sulla matrice A. Struttura dell’algoritmo Passo 0: La matrice C viene inizializzata ponendo C i i ( 0 ) = 0 ed inoltre: se l’arco i j esiste Cij(0) = cij (0) Cij = ∞ se l’arco i j non esiste Passo 1: Per k =1, v i =1, v j =1, v C i , j ( k + 1 ) = min [C i , j ( k ) ; C i , k + 1 ( k ) + C k + 1 , j ( k ) ] L’algoritmo termina dopo l’esecuzione, ripetuta v 3 volte, del passo 1. Esempio numerico Si consideri il grafo rappresentato in figura 12.8, con la matrice dei costi riportata in tabella 12.6 e si calcoli la matrice dei minimi percorsi utilizzando l’algoritmo di Floyd. FIG. 12.8 Rete esempio 2 3 1 TAB. 12.6 Matrice di adiacenza della rete di fig. 12.8 2 3 2 4 1 5 1 2 3 4 1 2 - 2 5 1 - 3 3 2 3 2 La matrice C 0 coincide con la matrice di adiacenza della rete. Step 1: utilizzando l’espressione a i j 1 = min (a i j 0 , a i 1 0 + a 1 j 0 ) per ogni i j , si calcola la matrice C 1 (Tab.12.7a). Step 2: utilizzando l’espressione a i j 2 = min (a i j 1 , a i 2 1 + a 2 j 1 ) per ogni i j , si calcola la matrice C 2 (Tab.12.7b). Step 3: utilizzando l’espressione a i j 3 = min (a i j 2 , a i 3 2 + a 3 j 2 ) per ogni i j , si calcola la matrice C 3 (Tab.12.7c). 4 3 2 - 306 Capitolo dodicesimo Step 4: utilizzando l’espressione a i j 4 = min (a i j 3 , a i 4 3 + a 4 j 3 ) per ogni i j , si calcola la matrice C 4 (Tab.12.7d). C 4 è la matrice dei minimi percorsi del grafo assegnato. TAB. 12.7a-d (a) Matrice C 1 1 2 3 4 1 2 - 2 5 1 - 3 3 2 (c) Matrice C 3 1 2 3 4 1 2 4 2 4 1 3 3 3 2 4 3 2 4 5 3 2 - (b) Matrice C 2 1 2 3 4 1 2 - 2 5 1 - 3 3 2 (d) Matrice C 4 1 2 3 4 1 7 2 4 2 4 1 3 3 3 5 2 4 8 3 2 4 5 3 2 - 12.5 CONFRONTO TRA GLI ALGORITMI DI MINIMO PERCORSO I parametri rispetto ai quali è possibile effettuare un confronto tra le due classi di algoritmi, arborescenti e matriciali, sono ovviamente la capacità di memoria ed il tempo di calcolo richiesti per la elaborazione automatica degli algoritmi. Nella tabella 12.8, indicando con a il numero di archi del grafo e con v il numero di vertici, sono riportate le dimensioni di memoria richieste dai due tipi di algoritmi. TAB. 12.8 Richieste di memoria degli algoritmi di minimo percorso Albero Costo dei percorsi Flag di ispezione Rete e costi su archi Totale Arborescenti v v v v+2a 4v+2a Matriciali v2 v2 v2 3v 2 Problemi di Percorso 307 Assumendo a = 4v , condizione ricorrente per molti tipi di grafi corrispondenti a problemi reali, si hanno i valori 12v per un algoritmo arborescente e 3v 2 per un algoritmo matriciale. Da questo confronto si evince che, tranne che per valori molto ridotti di v, gli algoritmi arborescenti richiedono una quantità di memoria minore rispetto a quelli matriciali. Per quanto concerne il tempo di calcolo si può dire che esso dipende, oltre che dalle caratteristiche e prestazioni del sistema di calcolo, anche da configurazione, dimensione e densità della rete e dalla struttura dei dati di rete. Si può affermare inoltre in generale che, quando sono richiesti i cammini minimi tra tutti i vertici della rete e quando la densità della rete (rapporto tra numero di archi e numero di vertici) è molto alta (al limite se il grafo è pieno), è opportuno utilizzare un algoritmo matriciale. Quando invece la densità della rete è bassa, cioè la sua matrice di adiacenza è sparsa, e non sono richiesti tutti i cammini minimi, ma solo quelli relativi ad una parte delle coppie di vertici, è opportuno utilizzare un algoritmo arborescente. 12.6 IL PROBLEMA DEL PERCORSO MASSIMO Si consideri la rete riportata in figura 12.1 e la matrice di incidenza arcopercorso riportata in tabella 12.1. Per determinare il percorso di massimo costo tra i vertici 1 e 5 (il percorso p 4 : 1-2-5, di costo 14) bisogna determinare quali archi appartengono ad esso. Il problema può essere formulato con un modello in tutto analogo a quello del minimo percorso, ma con una diversa funzione obiettivo: Max z = ∑ i j ∈ A c i j x i j s.a ∑k xok = 1 ∑i xid = 1 ∑ k x j k − ∑ i x i j = 0 (∀j ≠o,d ) x i j = 0/1 Si supponga di dover calcolare il massimo percorso da 1 a ciascuno degli altri vertici. Si può agevolmente verificare in figura 12.9 che il massimo percorso da 1 a 2 è l’arco 1-2, di costo 5, da 1 a 3 è il percorso 1-2-3, di costo 8, da 1 a 4 è il percorso 1-2-3-4, di costo 11, da 1 a 5 è il percorso 1-2-5, di costo 14. 308 Capitolo dodicesimo L’insieme dei percorsi determinati forma un’arborescenza con radice in 1 (Fig. 12.9). FIG. 12.9 Arborescenza dei percorsi massimi con origine in 1 (14) (5) 2 5 1 9 5 3 2 3 3 2 4 (11) 3 (8) Anche il problema della determinazione dei massimi percorsi da un vertice o della rete a tutti gli altri vertici, può essere ricondotto quindi a quello della determinazione di un’arborescenza con radice in o. Per la soluzione di questo problema è possibile utilizzare un semplice algoritmo di tipo label setting, basato su una particolare regola di visita. Si parte dal vertice o, che si etichetta con 0. Si visitano e si etichettano in sequenza i vertici che siano destinazione di archi il cui vertice origine sia già etichettato. Per ciascuno di questi vertici si confrontano i costi associati alle alternative di percorso e si sceglie il valore massimo, registrando opportunamente il vertice predecessore. Esempio numerico Si consideri la rete di figura 12.10, analoga a quella di figura 12.6. Si etichetta il vertice 1 con 0 (fig. 12.11). Si va al vertice 2, che è l’unico ad essere destinazione di archi i cui vertici sono già etichettati, e si etichetta con un costo 1, che è il valore dell’unico arco in esso incidente. Si va quindi al vertice 3, destinazione degli archi 1-3 e 2-3, aventi origine in vertici già etichettati. Le alternative di percorso sono pertanto 2, la prima di costo 0 + 2, la seconda di costo 1 + 3. Si sceglie la massima e si etichetta 3 con valore 4. Si procede in questo modo etichettando in successione il vertice 4 con valore Problemi di Percorso 309 7, il vertice 5 con valore 9, il vertice 6 con valore 12, il vertice 8 con valore 10 e il vertice 7 con valore 13. L’albero dei percorsi massimi è riportato in figura 12.11. FIG. 12.10 Grafo e costi c i j 1 5 3 2 3 3 6 2 1 1 3 2 3 4 3 3 7 1 3 8 4 FIG. 12.11 Arborescenza dei percorsi massimi con origine in 1 (9) (1) (0) 1 5 3 2 3 3 (12) 6 2 1 1 3 2 3 3 3 (4) 4 (7) 4 (13) 3 7 1 8 (10)