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)