Politecnico di Milano Anno Accademico 2010/2011 Fondamenti di Ricerca Operativa Corso del Prof. Edoardo Amaldi Stefano Invernizzi Facoltà di Ingegneria dell’Informazione Corso di Laurea Magistrale in Ingegneria Informatica Fondamenti di Ricerca Operativa Capitolo 1: La Ricerca Operativa Capitolo 1: La Ricerca Operativa Il concetto di Ricerca Operativa Definizione La Ricerca Operativa (in inglese detta Operations Research) è quella branca della matematica applicata nella quale si utilizzano dei modelli matematici e dei metodi quantitativi avanzati per analizzare e risolvere problemi decisionali complessi, con lo scopo generale di fornire uno strumento di supporto alla presa di decisioni. Per brevità, la Ricerca Operativa viene spesso indicata mediante l’acronimo RO. Problema decisionale Il termine problema decisionale (o problema di scelta) è utilizzato per indicare un problema che consiste nella scelta tra diverse alternative ammissibili, sulla base di uno o più criteri. Esempi di problemi decisionali Esempio n. 1: assegnamento di task Come primo esempio di problema decisionale, supponiamo di avere un certo numero di progetti da assegnare ad altrettanti ingegneri, ciascuno dei quali deve essere assegnatario di uno e un solo progetto. Supponendo che ogni ingegnere possa svolgere uno qualsiasi tra i progetti dati e indicando con il tempo che il -esimo ingegnere impiega per lo svolgimento dell’ -esimo progetto, vogliamo che l’assegnamento dei progetti sia tale da rendere minimo il tempo totale di esecuzione degli progetti. Ingegnere Progetto 2 6 3 8 4 9 5 7 8 Tabella 1: Tabella dei tempi di esecuzione degli m progetti da parte degli m ingegneri (m = 3). A tale scopo, possiamo innanzitutto osservare che i possibili assegnamenti sono in numero finito, e precisamente sono . Di conseguenza, è possibile valutare i tempi totali per ciascuna delle soluzioni ammissibili e risolvere il problema mediante un’analisi esaustiva di tutte le possibilità. Ogni possibile soluzione può poi essere rappresentata mediante una matrice nella quale l’elemento di posizione è uno zero se l’ -esimo progetto viene assegnato al -esimo ingegnere, e in caso contrario è pari a zero. Ad esempio, se supponiamo che l’ -esimo progetto venga sempre assegnato all’ -esimo ingegnere, la matrice che si ottiene è: Come messo in evidenza dalla precedente formula la crescita del numero di casi possibili avviene in maniera esponenziale rispetto al numero di ingegneri e progetti, perciò i tempi di esecuzione di un simile algoritmo con un moderno calcolatore diventerebbero ben presto enormi (già con un numero non particolarmente elevato si raggiungerebbero tempi di esecuzione dell'ordine dei secoli). Esempio n. 2: problema di network design Un altro esempio di problema risolvibile mediante la ricerca operativa è quello che consiste nell’individuare il modo per collegare un certo numero di città (o di uffici) mediante un insieme di collegamenti (sapendo che solo alcune coppie prestabilite di città o uffici possono essere collegate in maniera diretta), facendo in modo che il costo totale di collegamento risulti essere il minimo possibile. 3 Capitolo 1: La Ricerca Operativa Fondamenti di Ricerca Operativa La figura seguente mostra un possibile grafo nel quale sono rappresentate le varie città da collegare, mettendo in evidenza i collegamenti diretti possibili con i relativi costi, indicati dai numeri associati ai collegamenti stessi. Si noti che si suppone che tutti i collegamenti siano bidirezionali. 1 2 7 1 3 5 1 Figura 1: Grafo dei possibili collegamenti diretti tra città e dei costi associati ai collegamenti stessi. Per risolvere tale problema, è necessario costruire il grafo ricoprente connesso e aciclico con costo minimo tra tutti quelli possibili. Il numero di sottografi con tali caratteristiche che si possono ottenere a partire da G dipende naturalmente, oltre che dal numero di nodi, anche dal numero di possibili collegamenti diretti. Indichiamo con m il numero di possibili collegamenti diretti. Se si suppone che la scelta di inserire oppure no uno di tali collegamenti nel sottografo finale non influenzi le scelte successive (anche se è bene sottolineare che in realtà tali scelte non sono indipendenti), allora il numero di sottografi possibili da analizzare sarebbe pari a . Supponendo che siano possibili tutti i collegamenti diretti tra le coppie di nodi del grafo, ovvero ipotizzando , allora tale considerazione ci porta facilmente a dedurre che il numero di diverse soluzioni ammissibili è non superiore a situazione è pari a . Più in particolare, è stato dimostrato che il numero di soluzioni alternative in questa . Esempio n. 3: organizzazione dei turni del personale La Ricerca Operativa può anche essere utilizzata per organizzare i turni del personale, andando ad individuare la soluzione che rispetti al meglio le varie esigenze e minimizzando al contempo il numero di persone coinvolte (al fine di ridurre il costo da affrontare per il pagamento degli stipendi). Esempio n. 4: organizzazione degli sportelli della banca Un altro possibile problema di scelta si pone nel momento in cui si deve determinare il numero di sportelli che è necessario aprire in una banca al fine di minimizzare il tempo di attesa per i clienti, stabilendo eventualmente anche la tipologia di tali sportelli. Esempio n. 5: scelta di un PC portatile Un esempio di problema di scelta che è più facile trovarsi ad affrontare nella vita di tutti i giorni è quello della scelta del PC portatile da comprare: in tal caso, occorre tenere conto di diversi criteri, tra i quali i più importanti sono il prezzo, il peso e le prestazioni. Cenni storici Le origini della Ricerca Operativa sono legate all’ambito militare: tale disciplina è infatti nata durante la Seconda Guerra Mondiale, con lo scopo di sfruttare al meglio le limitate risorse che gli eserciti dovevano assegnare alle varie operazioni da compiere. In tale contesto nacque il termine Operations Research, che veniva allora utilizzato per indicare il modo più efficace di condurre le operazioni. Tra i problemi risolti dalla Ricerca Operativa nell’ambito militare si ricordano quelli di posizionamento dei radar, di approvvigionamento dei convogli e di logistica. Nel dopoguerra ci si rese conto che le tecniche della Ricerca Operativa potevano essere usate anche in ambito industriare ed economico: con il boom economico le dimensioni delle imprese e delle organizzazioni crebbe notevole, e di conseguenza si rese necessario affrontare problemi molto più complessi di quelli ai quali si era abituati. Lo sviluppo della Ricerca Operativa è stato inoltre fortemente incentivato prima dai progressi teorici nel Calcolo Numerico, e poi soprattutto dalla massiccia diffusione dell’informatica, che consente di risolvere rapidamente gli algoritmi utilizzati in tale disciplina. In inglese si utilizza oggi il termine Management Science, che indica un ambito più “soft” della Ricerca Operativa. 4 Fondamenti di Ricerca Operativa Capitolo 1: La Ricerca Operativa Schema generale di uno studio di Ricerca Operativa In generale, lo schema che si segue per la risoluzione di un problema decisionale mediante la Ricerca Operativa è rappresentato nella figura seguente: Problema Modello Algoritmo Programma Figura 2: Schema generale di uno studio di Ricerca Operativa. Come messo in evidenza dal diagramma, si parte da un problema, dal quale si ricava un modello, ovvero una rappresentazione semplificata della realtà. Il modello deve essere un giusto compromesso tra le esigenze di semplificazione e la necessità di esprimere i dettagli relativi al problema in analisi. A partire da tale modello si ricava poi un algoritmo, il quale verrà risolto per mezzo di un opportuno programma. Caratteristiche dei problemi decisionali I problemi decisionali si differenziano tra loro per una serie di caratteristiche, in base alle quali vengono identificate le varie sotto-branche della Ricerca Operativa stessa. Le caratteristiche principali dei problemi decisionali sono: Numero di decisori Alcuni problemi decisionali prevedono la presenza di un solo soggetto incaricato di prendere la decisione stessa; in altri casi invece i decisori in gioco sono più di uno: esempi di questo tipo sono i problemi relativi ad una relazione di coppia, l’incontro tra la domanda e l’offerta nella compravendita di un bene, le decisioni prese all’interno del C.d.A. di un’azienda, … . I problemi decisionali che prevedono più di un decisore vengono studiati dalla branca della Ricerca Operativa nota come Teoria dei Giochi. Numero degli obiettivi Talvolta i criteri in base ai quali effettuare la scelta sono più di uno (come nell’esempio della scelta del computer portatile da acquistare). Tali problemi vengono risolti mediante la Programmazione multi-obiettivo. Grado di certezza dei parametri I parametri del modello matematico realizzato per la risoluzione del problema possono essere noti solamente con un certo livello di incertezza, e non in maniera esatta: in tal caso si parla di Programmazione stocastica. La Programmazione Matematica Definizione La Programmazione Matematica (PM) è quel ramo della Ricerca Operativa che si occupa di risolvere i problemi decisionali con variabili di decisione reali, caratterizzati da un solo decisore, un solo criterio di scelta e che si svolgono in ambito certo. La Programmazione Matematica è detta a volte anche Ottimizzazione Matematica. Formalizzazione di un problema di PM Un problema di Programmazione Matematica può perciò essere formalizzato mediante un modello costituito da: Variabili di decisione Indichiamo con il vettore delle variabili di decisione. Le variabili di decisione sono grandezze numeriche il cui valore individua una soluzione del problema. Esse sono dette talvolta anche variabili di scelta e rappresentano di fatto le variabili indipendenti del problema. In un problema di PM, tutte le variabili di decisione devono essere reali, ovvero si deve avere: 5 Capitolo 1: La Ricerca Operativa Fondamenti di Ricerca Operativa Regione ammissibile Chiamiamo regione ammissibile il sottoinsieme di contenente tutti e soli i valori che può assumere la soluzione da esso individuata sia ammissibile. Avremo perciò: perché La regione ammissibile è a volte indicata con il termine campo di scelta e identifica di fatto il range entro il quale si troveranno i valori delle variabili di decisione. Essa è costituita da un insieme dei vincoli, solitamente rappresentato da un sistema di equazioni o, più spesso, di disequazioni. Tali vincoli possono essere: Vincoli tecnici, ovvero legati alle risorse utilizzabili e disponibili. Altri vincoli, legati alla non negatività delle variabili (che di solito rappresentano delle quantità e perciò sono sempre maggiori o uguali a zero), alla loro interezza, … . Funzione obiettivo La funzione obiettivo è una funzione definita da in , la quale esprime in modo quantitativo il gradimento o il costo di ciascuna soluzione ammissibile. Essa corrisponde perciò, in maniera intuitiva, alla variabile dipendente nel problema analizzato. L’obiettivo che ci si pone è quello di minimizzare o di massimizzare la funzione obiettivo: opt con , opt = min oppure opt = max. Si noti che minimizzare una funzione equivale a massimizzare la sua opposta, perciò in seguito non ci soffermeremo molto sulla distinzione tra le due diverse situazioni. Risoluzione di un problema di Programmazione Matematica Individuazione di un ottimo globale Risolvere un problema di PM consiste nell’individuare una soluzione ammissibile globalmente ottima, ovvero un vettore tale che: 1. Se opt = min, allora valga la relazione: 2. Se opt = max, allora valga la relazione: Casi particolari Talvolta può avvenire che: 1. Il problema sia inammissibile, ovvero si abbia . Questo significa che non è possibile fissare in alcun modo i valori delle variabili di decisione rispettando i vincoli imposti dalla regione ammissibile, perché quest’ultima è vuota. 2. Il problema sia illimitato, ovvero per una qualsiasi soluzione ammissibile, è possibile trovare un’altra soluzione ammissibile migliore rispetto alla precedente. In simboli: Se opt = min, allora Se opt = max, allora 3. Vi sia un numero elevatissimo (anche infinito) di soluzioni ottime (cioè con lo stesso valore ottimo). Ottimo locale Per le problematiche appena elencate, in alcuni casi ci si deve accontentare di una soluzione ammissibile localmente ottima, ovvero di una soluzione tale che: - Se opt = min, allora: - Se opt = max, allora: Per un valore opportuno. Naturalmente, un problema può ammettere più di un ottimo locale. 6 Fondamenti di Ricerca Operativa Capitolo 1: La Ricerca Operativa Casi particolari di Programmazione Matematica Vediamo ora alcuni casi particolari di Programmazione Matematica. Programmazione Lineare (PL) Si parla di Programmazione Lineare (PL) quando la funzione è lineare e la regione ammissibile è del tipo: , con Si noti che con e è indicato il numero di variabili di decisione e con Programmazione Lineare Intera (PLI) Si parla di Programmazione Lineare Intera (PLI) quando la funzione , con è lineare per ogni . il numero di vincoli. è lineare e la regione ammissibile è del tipo: e è lineare per ogni . Si osserva perciò che la PLI è equivalente alla PL, con la sola aggiunta del vincolo di interezza di tutte le variabili di decisione. Programmazione Non Lineare Si parla di Programmazione Non Lineare quando la funzione racchiuso tra superfici regolari, ovvero del tipo: , dove è regolare e la regione ammissibile è un volume è regolare per ogni . Possiamo perciò affermare che la Programmazione Non Lineare comprende al proprio interno sia i problemi di PL, sia i problemi di PLI. In ogni caso, parleremo in seguito soprattutto di problemi di Programmazione Lineare relativi a reti o grafi e, in un secondo momento, di Programmazione Lineare Intera, senza invece analizzare il caso generale di Programmazione Non Lineare. Programmazione a molti obiettivi Come abbiamo già accennato, in alcuni casi gli obiettivi possibili sono più di uno (programmazione a molti obiettivi). In questo caso, solitamente si cerca di ricondursi al caso di programmazione ad un solo obiettivo. A tale scopo, gli approcci possibili sono diversi, ma i due più importanti sono quelli che indicheremo come approccio senza priorità e approccio con priorità. Approccio senza priorità L’approccio senza priorità prevede che, detto un obiettivo da minimizzare e detto un obiettivo da massimizzare, si crei un nuovo obiettivo a partire da e convertiti opportunamente in unità di misura omogenee, mediante l’utilizzo di due opportuni scalari e . Si otterrà così: min Approccio con priorità Una soluzione alternativa è quella che consiste nell’ottimizzare uno degli obiettivi, considerato prioritario rispetto agli altri, riducendo questi ultimi a semplici vincoli di qualità: Dove è una costante opportuna. Ad esempio, nel caso della scelta del computer portatile, è possibile ottimizzare rispetto alle prestazioni, imponendo però che il prezzo ed il peso siano inferiori a opportune soglie. 7 Capitolo 1: La Ricerca Operativa Fondamenti di Ricerca Operativa Programmazione Matematica e simulazione Oltre all’approccio della Programmazione Matematica, è possibile adottare l’approccio della simulazione. Così come la PM, quello della simulazione è un approccio di tipo modellistico, che prevede che si effettui una ricostruzione del sistema reale, sul quale effettuare sperimentalmente la valutazione delle soluzioni possibili per individuare la migliore. Ovviamente, non possono essere valutate tutte le soluzioni ammissibili, e perciò non si ha garanzia di ottimalità. In generale, la simulazione si utilizza quando si devono affrontare dei problemi difficili da formalizzare. Inoltre, spesso si utilizza la Programmazione Matematica per individuare le soluzioni di un problema, e poi attraverso la simulazione si effettua una validazione delle soluzioni trovate o si valuta la qualità di queste ultime. Esempi di modelli Esempio n. 1: mix produttivo Il testo del problema Una azienda produce tre tipi di apparecchiature elettroniche, indicate con , e . Le fasi critiche del ciclo produttivo sono 3: assemblaggio, finitura e controllo di qualità. Si conoscono inoltre i tempi, espressi in ore-uomo, necessari per il completamento di ciascuna fase produttiva per ogni singola apparecchiatura dei 3 tipi prima elencati: Prodotto Fase Assemblaggio 80 70 120 Finitura 70 90 20 Controllo di qualità 40 30 20 Tabella 2: Ore-uomo necessarie per completare le fasi produttive in relazione ai tipi di apparecchiature prodotte. La disponibilità produttiva dell’azienda nell’orizzonte di pianificazione espressa di 1000 ore-uomo per la fase di assemblaggio, di 800 ore-uomo per la finitura e di 300 ore-uomo da dedicare al controllo di qualità. È noto inoltre che il margine lordo unitario espresso in migliaia di euro è pari a 15 per il prodotto prodotto e a 2 per il prodotto . , a 10 per il Si suppone che l’azienda possa vendere tutto ciò che produce. Formulare in termini matematici il problema di determinare un “piano” di produzione che massimizzi il margine lordo complessivo. Il modello corrispondente Variabili di decisione Nel caso analizzato, è opportuno usare 3 variabili di decisione, che indichino per ciascuna tipologia di apparecchiatura la quantità di pezzi da produrre: quantità di apparecchiature della tipologia prodotte dall’azienda ( ) Funzione obiettivo In questo caso, la funzione obiettivo è il margine di guadagno dell’azienda, che si desidera rendere massimo: Vincoli I vincoli da imporre sono i seguenti: - Limite della disponibilità produttiva relativa alla fase di assemblaggio: - Limite della disponibilità produttiva relativa alla fase di finitura: - Limite della disponibilità produttiva relativa alla fase di controllo di qualità: - Non negatività delle quantità prodotto: Si noti che in questo caso non è stato posto il vincolo di interezza delle variabili, perché ciò consente di rendere molto più semplice la risoluzione del problema. Il problema così formulato è un problema di ottimizzazione lineare. 8 Fondamenti di Ricerca Operativa Capitolo 1: La Ricerca Operativa Esempio n. 2: pianificazione degli investimenti Il testo del problema Una società di investimenti finanziari deve decidere la composizione di un portafoglio di titoli. Gli investimenti possibili sono riassunti nella seguente tabella: Investimento A (settore auto) B (settore auto) C (settore informatica) D (settore informatica) E (settore elettrodomestici) F (settore elettrodomestici) G (titoli di stato a breve) H (titoli di stato a lungo) Area Germania Italia U.S.A. Italia Italia Francia Italia Inghilterra Capitale [ ] (KEuro) 150 150 60 100 125 100 50 80 Rendimento atteso [ 11% 9% 13% 10% 8% 7% 3% 5% ] Tabella 3: Investimenti a disposizione della società di investimenti finanziari. Il capitale a disposizione della società è di 600 KEuro. È noto anche che la società può compiere al massimo 5 investimenti, allo scopo di evitare un’eccessiva frammentazione della gestione. Inoltre, è necessario mantenere una certa diversificazione per area geografica, e perciò non si possono effettuare più di 3 investimenti in Italia, né più di 3 investimenti all’estero. Formulare in termini matematici il problema di selezione degli investimenti in modo tale da massimizzare il ritorno atteso rispettando i vincoli che mirano a ridurre il rischio. Il modello corrispondente Variabili di decisione In questo caso, possiamo utilizzare una variabile di decisione per ogni investimento, assegnando a tale variabile il valore 0 se il corrispondente investimento non viene effettuato, 1 in caso contrario: - ( ) Funzione obiettivo La funzione obiettivo è il ritorno atteso, che deve essere massimizzato e che è dato dall’espressione seguente: Vincoli I vincoli da imporre sono i seguenti: - Investire un capitale non superiore a quello a disposizione della società: - Non superare i 3 investimenti effettuati in Italia: - Non superare i 3 investimenti effettuati all’estero: - Imposizione del fatto che le variabili abbiano il valore 0 o 1, e nessun altro valore. Se ad esempio il testo del problema avesse richiesto anche che in presenza di almeno un investimento nel settore informatico si dovesse effettuare almeno un investimento in titoli di stato, allora sarebbe stato necessario aggiungere il vincolo: 9 Capitolo 1: La Ricerca Operativa Fondamenti di Ricerca Operativa Esempio n. 3: problema di assegnazione Il testo del problema Proviamo ora a formalizzare un modello matematico relativo all’esempio n. 1 di problema decisionale, già esposto in precedenza. Il modello corrispondente Variabili di decisione - - Funzione obiettivo Vogliamo minimizzare il tempo totale per l’esecuzione dei progetti, ovverto: Vincoli - Ogni progetto deve essere assegnato ad uno e un solo ingegnere: - Ogni ingegnere deve essere assegnatario di uno e un solo progetto: - Imposizione del fatto che le variabili abbiano il valore 0 o 1, e nessun altro valore. Se ad esempio il testo del problema avesse richiesto anche che in presenza di almeno un investimento nel settore informatico si dovesse effettuare almeno un investimento in titoli di stato, allora sarebbe stato necessario aggiungere il vincolo: Esempio n. 4: localizzazione di impianti Il testo del problema Supponiamo che si abbiano 3 pozzi petroliferi, situati nei punti , e , ciascuno dei quali estrae greggio. Si vuole costruire una raffineria, e a tale scopo si desidera individuare la posizione in cui localizzarla in modo da minimizzare il costo degli oleodotti (che si assume essere proporzionale al quadrato della lunghezza degli oleodotti stessi). Si sa che è vietato costruire una raffineria nel raggio di 100 km attorno al punto . Gli oleodotti possono però attraversare anche l’area vietata. Il modello corrispondente Variabili di decisione ascissa e ordinata del punto in cui situare la raffineria. Funzione obiettivo È il costo degli oleodotti, che deve essere minimizzato: Vincoli 10 - Il ( ) deve avere una distanza da - I valori di e devono essere reali: non inferiore a 100: Fondamenti di Ricerca Operativa Capitolo 2: Ottimizzazione su grafi Capitolo 2: Ottimizzazione su grafi Molti problemi decisionali possono essere formulati utilizzando il linguaggio messo a disposizione dalla teoria dei grafi, anche se in alcuni casi si tratta di problemi che apparentemente non hanno la struttura di una rete. Riprendiamo perciò il concetto di grafo per poi andare ad analizzare le tecniche di ottimizzazione sui grafi stessi. Il concetto di grafo Definizione di grafo non orientato Un grafo non orientato (detto anche grafo semplice) è una coppia ordinata di insiemi, nella quale è un insieme di nodi (o vertici) e è un insieme di coppie di nodi non ordinate, ciascuna delle quali è detta lato o collegamento, che rappresenta appunto il collegamento tra i due nodi facenti parte della coppia stessa. Si noti che le coppie non ordinate verranno in seguito distinte dalle coppie ordinate mediante l’utilizzo delle parentesi quadre anziché delle parentesi tonde. In genere indicheremo e . Esempio di grafo non orientato La figura seguente mostra la rappresentazione schematica del grafo matematicamente così descritto: 1 2 3 5 4 Figura 3: Rappresentazione grafica di un esempio di grafo non orientato. Definizione di grafo orientato Un grafo orientato (o grafo diretto) è una coppia ordinata di insiemi, nella quale è un insieme di nodi (o vertici) e è un insieme di coppie di nodi ordinate, ciascuna delle quali è detta arco, che rappresenta appunto il collegamento tra i due nodi facenti parte della coppia stessa. In sostanza quindi un grafo orientato è del tutto simile ad un grado orientato, salvo il fatto che in un grafo orientato vi è la possibilità che alcuni collegamenti possano essere percorsi solo in un verso e non nel verso opposto. Esempio di grafo orientato La figura seguente mostra la rappresentazione schematica del grafo matematicamente così descritto: 1 2 3 4 Figura 4: Rappresentazione grafica di un esempio di grafo orientato. 11 Capitolo 2: Ottimizzazione su grafi Fondamenti di Ricerca Operativa Definizioni relative ai grafi Nodi adiacenti (successori) In un grafo non orientato, due nodi sono detti adiacenti se esiste un lato che li collega, ovvero se esiste un lato avente per estremi i due nodi in analisi. In un grafo orientato, un nodo si dice adiacente a (o successore di ) se esiste l’arco . Lato (arco) incidente in un nodo e arco uscente da un nodo In un grafo non orientato un lato si dice incidente in un nodo se il nodo è un estremo del lato stesso. In un grafo orientato, un arco si dice incidente su un nodo se esiste un nodo dice uscente da un nodo se esiste un nodo per il quale si ha . per il quale si ha , mentre si Grado di un nodo In un grafo non orientato, chiamiamo grado di un nodo il numero di lati incidenti nel nodo stesso. In un grafo orientato, si chiama grado di ingresso di un nodo il numero di archi incidenti sul nodo stesso, mentre si chiama grado di uscita del nodo il numero di archi uscenti dal nodo stesso. Cammino (orientato) da un nodo ad un altro In un grafo non orientato, si chiama cammino dal nodo al nodo una sequenza di lati del tipo: Analogamente, in un grafo orientato, si chiama cammino orientato dal nodo al nodo un insieme di archi del tipo: Nodi connessi In un grafo qualsiasi, un nodo si dice connesso a un nodo se esiste un cammino (orientato nel caso di grafo adiacente) da a . Si osserva facilmente che due nodi adiacenti sono necessariamente connessi. Grafo connesso Un grafo si dice connesso se per ogni possibile coppia di nodi oppure esiste un cammino (eventualmente orientato) da a . In altri termini, un grafo connesso è un grafo nel quale ogni nodo è connesso ad ogni altro nodo. Ciclo e circuito In un grafo non orientato si dice ciclo un cammino da un nodo al nodo stesso. In altri termini, il ciclo è un cammino nel quale vale . In un grafo orientato si dice circuito un cammino da un nodo al nodo stesso. Il circuito è quindi un cammino orientato nel quale vale . Grafo ciclico e grafo aciclico Un grafo si dice ciclico se contiene almeno un ciclo (o circuito). IN caso contrario, il grafo si dice aciclico. Grafo completo Un grafo si dice completo se ognuno dei suoi nodi è adiacente a tutti i restanti nodi del grafo stesso. In altri termini, un grafo è completo se esistono tutti i lati (o tutti gli archi) possibili tra i nodi appartenenti all’insieme . In un grafo non orientato completo si ha: Mentre in un grafo orientato completo vale la relazione: Si può affermare che nel caso generale il numero di archi non supera il valore indicato dalle formule sopra riportate. 12 Fondamenti di Ricerca Operativa Capitolo 2: Ottimizzazione su grafi Grafo bipartito Un grafo si dice bipartito se esiste una bi-partizione dell’insieme tale che nessun lato colleghi nodi dello stesso sottoinsieme di . Per bi-partizione di si intende chiaramente una coppia di insiemi tale che e . Si osserva che in un grafo bipartito il numero massimo di archi è dato dal prodotto tra le cardinalità delle due partizioni di , ovvero: Esempio: La figura seguente mostra un esempio di grafo bipartito: 1 4 2 5 3 Figura 5: Esempio di grafo bipartito. Si nota infatti in maniera immediata che ogni arco del grafo collega un nodo della bi-partizione nodo della bi-partizione . con un Taglio, taglio entrante e taglio uscente Dato un grafo e dato un insieme di nodi appartenenti a , si chiama taglio (o cut) indotto da e si indica con l’insieme di tutti e soli i lati aventi un estremo in e un estremo nel suo complemento, , ovvero: Si chiama inoltre taglio uscente l’insieme di tutti e soli i lati (o archi) uscenti da un nodo appartenente a incidenti su un nodo appartenente al complemento di S, indicato con . In simboli: e Si chiama infine taglio entrante l’insieme di tutti e soli i lati (o archi) incidenti su un nodo appartenente a uscenti da un nodo appartenente al complemento di S, indicato con . In simboli: e Si può facilmente osservare che in un grafo non orientato si ha: Inoltre, vale sempre la relazione: Sottografo Dato un grafo , diciamo che il grafo contiene solamente lati che collegano nodi appartenenti a è un sottografo di G se , , e inoltre . 13 Capitolo 2: Ottimizzazione su grafi Fondamenti di Ricerca Operativa Gli alberi Albero Un albero è un grafo non orientato, connesso e aciclico. Gli alberi sono anche detti tree. I nodi di grado 1 di un albero sono anche detti foglie. Albero di supporto Dato un grafo tutti i nodi di , ovvero e dato un albero . , diciamo che Lato di diminuzione Sia un albero di supporto del grafo all’insieme T si crea un ciclo ed esiste un lato . Un lato con è un albero di supporto di se contiene è detto di diminuzione se aggiungendolo . Questo significa che un lato di diminuzione è un lato che, sostituito ad un altro lato all’interno dell’albero di supporto, ci fa ottenere un nuovo albero di supporto con costo inferiore rispetto a quello di partenza. Proprietà degli alberi Proprietà 1 Ogni albero che abbia nodi ha almeno 2 foglie. DIM: Procediamo per assurdo. Supponiamo di avere 0 foglie. In tal caso, tutti i nodi dell’albero hanno grado maggiore di 1, perciò tutti i nodi dell’albero hanno almeno due lati incidenti. Siccome però non si possono avere cicli, ogni nodo può essere visitato una sola volta, e perciò ogni volta che introduciamo un nodo, dobbiamo aggiungere anche un lato che lo collega ad un nuovo nodo, con un procedimento che è infinito (cioè sarebbe possibile un viaggio infinito su un grafo finito). Lo stesso discorso può essere ripetuto ipotizzando la presenza di una sola foglia. Proprietà 2 Ogni albero che abbia nodi ha esattamente lati. DIM: Procediamo per induzione: 1. Caso base: consideriamo il caso . In tal caso, non saranno presenti lati, e perciò la proprietà risulta verificata, ovvero i lati presenti sono . 2. Passo induttivo: dobbiamo verificare che se per un albero di nodi si hanno lati, per un albero di nodi si hanno lati. Consideriamo a tal proposito un albero con nodi. Se eliminiamo dall’albero una foglia e l’unico lato su essa incidente, otteniamo un albero con nodi che, per la nostra ipotesi, ha lati. Siccome per ottenere tale albero abbiamo eliminato un solo lato dall’albero di partenza, significa necessariamente che i lati di tale albero erano , ovvero . Proprietà 3 In ogni albero, esiste un unico cammino tra ogni coppia di nodi. Proprietà 4 Aggiungendo ad un albero qualsiasi un qualsiasi lato, si crea uno ed un solo ciclo, formato dall’unico cammino che congiunge i due lati uniti dal nuovo lato, e dal lato stesso. Proprietà 5 (di scambio) Sia un albero di supporto di . Sia inoltre un lato tale che e sia l’unico ciclo dell’albero (come noto dalla proprietà 4, infatti, aggiungendo all’albero si ottiene un nuovo albero con uno ed un solo ciclo). Per ogni lato 14 , il grafo è ancora un albero di supporto di . Fondamenti di Ricerca Operativa Capitolo 2: Ottimizzazione su grafi Applicazioni dei grafi A questo punto è opportuno andare ad analizzare quali sono le situazioni nelle quali i grafi si rendono utili per la realizzazione del modello matematico di un certo problema. Relazioni di precedenza Una possibile applicazione dei grafi è quella che consiste nell’utilizzarli per la rappresentazione di relazioni di precedenza, ad esempio tra attività. Nel dettaglio, per precedenza tra attività si intende dire che una certa attività è in relazione di precedenza con un’altra se la prima deve necessariamente essere completata prima che possa iniziare la seconda. A tale scopo, è possibile utilizzare un grafo orientato nel quale i nodi rappresentano le singole attività, facendo poi in modo che per ogni arco l’attività sia prioritaria rispetto a . Raggiungibilità tra luoghi L’ambito nel quale risulta più intuitivo utilizzare i grafi è quello dello studio della raggiungibilità tra luoghi: ogni luogo (ad esempio, ogni città) è rappresentato da un nodo del grafo e ogni collegamento diretto tra una città ed un’altra è rappresentato mediante un arco dalla città di partenza a quella di arrivo (in generale è più opportuno utilizzare grafi orientati, perché è possibile che vi siano strade a senso unico). Relazioni di compatibilità e incompatibilità Utilizzando una grafo bipartito è possibile mettere in evidenza una relazione di compatibilità o incompatibilità. Supponiamo ad esempio di avere un insieme di persone ed un insieme di incarichi, e ipotizziamo che alcune persone possano essere incompatibili con uno o più incarichi: possiamo rappresentare questa situazione mediante un grafo bipartito, in modo che i nodi di una delle due partizioni del grafo siano le persone, i nodi dell’altra partizione siano gli incarichi e i lati del grafo colleghino solo le persone agli incarichi con i quali sono incompatibili. Rappresentazioni di un grafo All’interno di un programma, un grafo può essere rappresentato mediante strutture dati di tipo diverso. Le soluzioni più di frequente adottate sono 2: la matrice di incidenza nodo-nodo e la lista degli archi. Matrice di incidenza nodo-nodo Nel caso in cui il grafo sia molto denso o “quasi completo”, una maniera efficiente per rappresentare il grafo è quella di utilizzare una matrice quadrata (dove si ricorda che è il numero di nodi, ovvero ), nella quale l’elemento alla posizione ha valore 1 se e solo se esiste un arco (o lato) uscente dal nodo e incidente nel nodo del grafo, mentre in caso contrario tale elemento assume il valore 0. Naturalmente, in caso di grafo non orientato la matrice di incidenza nodo-nodo è sempre simmetrica. Lista dei nodi incidenti (o dei nodi successori) Una soluzione alternativa, adatta soprattutto nel caso in cui il grafo sia piuttosto sparso, consiste nel memorizzare tutti i nodi del grafo, associando a ciascuno di essi una lista la quale rappresenti nel caso di grafo orientato l’elenco di tutti i nodi successori al nodo al quale è associata la lista stessa; nel caso in cui il grafo in analisi sia un grafo non orientato, tale lista rappresenta invece tutti i nodi incidenti nel nodo in analisi. 15 Capitolo 2: Ottimizzazione su grafi Fondamenti di Ricerca Operativa Il problema di raggiungibilità in un grafo orientato Definizione del problema di raggiungibilità Dato un grafo orientato e un nodo , il problema della raggiungibilità consiste nel determinare l’insieme di tutti e soli i nodi del grafo raggiungibili da . Algoritmo di esplorazione del grafo La soluzione a tale problema è data dall’algoritmo di esplorazione del grafo, il quale prevede che si utilizzi un insieme , il quale potrebbe ad esempio, ma non necessariamente, essere gestito mediante una coda. Si utilizzerà inoltre l’insieme che al termine conterrà l’insieme dei nodi raggiungibili da . In particolare, l’algoritmo prevede che si parta dal nodo , aggiungendo tale nodo a . Fino a quando ci sono elementi in , si procede prelevando da esso un certo nodo, che viene poi eliminato dall’insieme stesso e aggiunto ad . Si aggiungono quindi a tutti i nodi adiacenti al nodo prelevato dallo stesso , e poi si passa ad un eventuale ulteriore nodo presente in , ripetendo le operazioni appena illustrate. Di seguito è riportato lo pseudo-codice relativo a tale algoritmo: BEGIN Q := {s}; M := ; WHILE Q ≠ DO /* elabora un nodo raggiungibile h scegliere un nodo h Q e porre Q := Q \ {h}; M := M {h}; /* marcare h */ FOR EACH j S(h) DO IF j M AND j Q THEN Q := Q {j} END-IF END-FOR END-WHILE END Q */ Complessit{ dell’algoritmo di esplorazione del grafo Ad ogni iterazione del ciclo while: 1. 2. si sceglie un nodo non ancora elaborato, lo si estrae da e lo si marca inserendolo in ; si inseriscono in i vertici non ancora marcati che possono essere raggiunti direttamente da . Siccome però ogni nodo viene considerato una sola volta, di fatto tutti i lati volta, e perciò il contenuto del ciclo for each viene eseguito in totale esattamente while ha invece una complessità pari a , perciò si ha: Siccome nel caso peggiore sappiamo che vale la relazione: Possiamo concludere: 16 vengono considerati una sola volte. La parte restante del ciclo Fondamenti di Ricerca Operativa Capitolo 2: Ottimizzazione su grafi Alberi di supporto di costo minimo Il problema dell’individuazione degli alberi di supporto di costo minimo Formulazione matematica del problema Il problema dell’individuazione di un albero di supporto di costo minimo può essere così formulato: dato un grafo e data una certa funzione di costo , ovvero , per ogni si vuole determinare un albero di supporto di che abbia il costo minimo tra tutti i possibili alberi di supporto di . Possiamo perciò affermare che la regione ammissibile è in questo caso l’insieme di tutti i possibili alberi di supporto di , e che la funzione obiettivo è: Dove è il costo associato al lato del grafo di partenza. Osservazioni Il problema appena formulato ha numerose applicazioni, tra le quali la più classica è la progettazione di reti di comunicazione, di teleriscaldamento, ... . Altre applicazioni sono ad esempio la memorizzazione compatta di sequenze (DNA) e la diffusione di messaggi segreti. Prendiamo in considerazione il caso applicativo in cui si voglia costruire una rete stradale per collegare tra loro un certo numero di città (che saranno i nodi del grafo) mediante delle possibili strade (i lati del grafo), in modo tale che il costo totale risulti essere minimo (potremmo ipotizzare di usare come funzione di costo i chilometri di strada da costruire, ad esempio). Si nota che: 1. Ogni coppia di città deve poter comunicare, e perciò la rete stradale deve essere un sottografo connesso del grafo di partenza, che contenga tutti i nodi di quest’ultimo. 2. Per avere costo minimo, è chiaro che non possono esserci cicli. Da tali osservazioni consegue in maniera molto semplice che il sottografo ricercato è proprio un albero di supporto di costo minimo. Teorema di Arthur Cayley (1889) Il teorema di Cayley afferma che, dato un grafo completo costituito da supporto di tale grafo è pari a , per . nodi, il numero di possibili alberi di Di conseguenza, il numero di grafi possibili cresce in maniera estremamente rapida al crescere di : anche se il problema è apparentemente molto semplice da risolvere (perché comunque è chiaro che il numero di sottografi possibili di un grafo finito è finito, e perciò non può essere messa in dubbio la decidibilità del problema), occorre individuare un algoritmo efficiente per risolverlo, perché l’esplorazione esaustiva di tutte le possibili soluzioni non è attuabile nella pratica. Proprietà degli alberi ottimi Possiamo ora enunciare una proprietà che ci tornerà utile in seguito, e che risulta in realtà piuttosto ovvia dalla definizione che abbiamo in precedenza dato del termine lato di diminuzione: un albero di supporto è ottimo se e solo se non esistono lati di diminuzione. 17 Capitolo 2: Ottimizzazione su grafi Fondamenti di Ricerca Operativa L’algoritmo di Prim L’algoritmo di Prim è un algoritmo che consente di risolvere il problema dell’individuazione di uno tra gli alberi di supporto di costo minimo di un grafo dato. In particolare, si tratta di un algoritmo greedy, ovvero un algoritmo che ad ogni passo compie la scelta localmente migliore , senza rimettere in discussione le scelte precedenti. Spiegazione intuitiva In sostanza, l’algoritmo di Prim prevede che si parta da un nodo qualsiasi del grafo e che lo si inserisca in un insieme di nodi inizialmente vuoto. Ad ogni passo si considera il taglio uscente da tale insieme di nodi e si seleziona uno dei lati ad esso appartenente avente il costo minimo tra tutti quelli del taglio stesso. Il lato appena selezionato viene inserito nel grafo di supporto minimo che costituirà al termine la soluzione del problema, mentre il nodo sul quale incide tale lato verrà aggiunto all’insieme di nodi utilizzato dall’algoritmo stesso. Il procedimento viene iterato e la procedura termina quando tutti i nodi sono stati aggiunti all’insieme (o, equivalentemente, quando sono stati inseriti nell’albero di supporto esattamente lati, perché è noto che un albero con nodi ha lati). Pseudocodice Di seguito è riportato lo pseudocodice dell’algoritmo (ipotizzando di avviare la procedura dal nodo 1): BEGIN T:= ; S:={1}; WHILE |T| < n - 1 DO individuare [v,h] T := T {[v,h]}; S := S {h}; END-WHILE δ(S) di costo minimo, con v S e h N \ S; END Analisi di complessità La precedente implementazione ha un costo : il ciclo while viene iterato lato di costo minimo tra quelli del taglio ha, ad ogni iterazione, un costo . volte e l’individuazione del Riduzione della complessità Tuttavia, è possibile implementare l’algoritmo in una maniera più efficiente, con costo . L’idea di base è quella di conservare in un’opportuna struttura dati l’insieme dei lati appartenenti al taglio indotto sul grafo dall’insieme dei nodi già aggiunti all’albero di supporto, conservando però solo un lato per ogni nodo di arrivo, e precisamente quello a costo minimo tra tutti i nodi del taglio che incidono sul nodo di arrivo. Per farlo, utilizziamo una struttura dati che prevede l’uso di: 18 - un sottoinsieme dei lati del grafo, il quale rappresenta tutti i lati già inseriti nell’albero; - un sottoinsieme , contenente tutti i nodi incidenti ai lati selezionati (perciò è l’insieme di tutti i nodi già considerati nell’albero di supporto); - Un array che contiene, per ogni nodo non ancora inserito nell’albero di supporto, il costo minimo del lato che colleghi un nodo qualsiasi di con tale nodo (nel caso in cui non esistano collegamenti diretti, il corrispondente valore viene posto ad ). In simboli: - Un array contenente, per ogni nodo non ancora inserito nell’albero di supporto, il nodo precedente per raggiungerlo con il lato il cui costo è individuato dal corrispondente valore inserito nell’array . In simboli: Fondamenti di Ricerca Operativa Capitolo 2: Ottimizzazione su grafi L’algoritmo risulta perciò essere il seguente: BEGIN T := ; S := {1}; FOR j:=2 TO n DO C[j] := c1j ; pred[j] := 1; END-FOR FOR k:=1 TO n–1 DO min := +∞; FOR j := 2 TO IF j /* nodi j S */ /* selezionare n – 1 lati albero */ n DO /* scegliere lato min in δ(S) */ S AND (C[j] < min) THEN min := C[j]; h := j; END-IF END-FOR S := S {h}; T := T {[pred[h],h]}; /* estendere S e T */ FOR j:=2 TO n DO /* aggiornare C[j] e pred[j] j S */ IF j S AND (chj < C[j]) THEN C[j] := chj; pred[j] := h; END-IF END-FOR END-FOR END Ovvero: iniziamo considerando il nodo 1. Aggiorniamo le strutture e in modo che contengano i dati iniziali (cioè inizialmente il predecessore di tutti i nodi è 1 con un costo pari al costo del lato che collega 1 al corrispondente nodo). Dopodiché, si itera per volte il procedimento che consente di inserire un nuovo lato nell’albero di supporto (sappiamo che un albero con nodi ha lati). Ad ogni iterazione, si trova il minimo tra tutti i valori contenuti in e si inseriscono in il lato corrispondente e in il nodo che viene raggiunto aggiungendo tale lato. Si aggiornano poi le strutture e . La fase di inizializzazione ha una complessità ; si ha poi (in “serie” al precedente) un ciclo ripetuto volte, all’interno del quale si hanno l’operazione di scelta del lato, che ha a sua volta un costo , e quella di aggiornamento, anch’essa con costo . In conclusione, l’algoritmo ha complessità . Per grafi sparsi, ove complessità a , si può usare una struttura dati più sofisticata che consente di ridurre la . Esattezza dell’algoritmo L’algoritmo di Prim è esatto, cioè fornisce sempre un albero di supporto di costo minimo, indipendentemente dalla scelta del nodo di partenza e dalle scelte dei lati nei casi in cui, durante una certa iterazione, si abbiano più lati di costo minimo. Possiamo dimostrarlo sfruttando la proprietà che afferma che se un albero di supporto è ottimo, allora non esistono lati di diminuzione. In particolare, da ciò consegue che, dato un sottoinsieme dei nodi del grafo di partenza e dato un lato appartenente al taglio tale che sia un lato di costo minimo tra quelli di , allora esiste un albero di supporto ottimo contenente (cioè ogni scelta fatta dall’algoritmo di Prim ci porta ad un albero di costo ottimo). Dimostriamo tale proposizione per assurdo: se esistesse un albero ottimo tale che , allora aggiungendo all’albero si ottiene un ciclo nel quale esiste uno e un solo lato . Si hanno perciò due possibilità: 1. Il costo di e quello di sono uguali ( ), e quindi l’albero di partenza è ottimo perché ha lo stesso costo di che, per ipotesi, è ottimo; 2. Il costo è superiore a quello di , perciò non è ottimo, perché è un lato di diminuzione. 19 Capitolo 2: Ottimizzazione su grafi Fondamenti di Ricerca Operativa Dimostrazione della proposizione sugli alberi di supporto a costo minimo A questo punto, possiamo anche dimostrare la proposizione che abbiamo in precedenza solo enunciato, secondo la quale un albero di supporto è a costo minimo se e solo se non possiede lati di diminuzione. - Risulta ovvio che se un albero è ottimo, esso non possiede lati di diminuzione (questo deriva dalla definizione di lato di diminuzione, ed è la cosiddetta proprietà degli alberi ottimi). Del resto, abbiamo usato questa proprietà per dimostrare l’ottimalità della soluzione fornita dall’algoritmo di Prim. - Vogliamo ora dimostrare che, se un albero non ha lati di diminuzione, allora è ottimo. Per farlo, possiamo sfruttare il fatto che le soluzioni fornite dall’algoritmo di Prim sono sempre alberi ottimi. Possiamo infatti mostrare che con semplici operazioni di scambio si può trasformare l’albero ottimo fornito dall’algoritmo di Prim in senza modificare il costo. Supponiamo che lati di non appartengano a e mostriamo come con una operazione di scambio si può trasformare in un albero con lo stesso costo di ma con lati che non appartengono a . Applicando volte un’operazione di questo tipo si ottiene un albero identico a con , e perciò è ottimo. Supponiamo che sia il primo lato appartenente a ma non a . Se aggiungiamo tale lato a , otteniamo un ciclo. Naturalmente, nel ciclo così creatosi deve esistere un lato adiacente ad uno dei lati in comune tra e . A questo punto possiamo osservare che: 1. 2. Siccome per ipotesi non ci sono lati di diminuzione in , avremo . Siccome per ipotesi è il risultato dell’algoritmo di Prim, allora dovremo avere . Di conseguenza, avremo , e perciò abbiamo verificato che possiamo ottenere un grafo con lati diversi da , ma con lo stesso costo. Iterando il procedimento, otterremo , che perciò risulta avere lo stesso costo dell’albero fornito dall’algoritmo di Prim, e perciò risulta essere ottimo. L’algoritmo di Kruskal L’algoritmo di Kurskal è un algoritmo alternativo rispetto a quello di Prim, che consente ancora l’individuazione di un albero di supporto ottimo a partire da un certo grafo. Spiegazione intuitiva L’idea sulla quale si basa l’algoritmo di Kruskal è quella di ordinare secondo il loro costo tutti i lati che costituiscono il grafo di partenza. Si procede poi inserendo nell’albero di supporto (inizialmente privo di lati) i lati così ordinati, partendo da quelli con costo minore ed inserendoli solo a patto che l’inserimento del lato stesso non provochi alcun ciclo. Una volta inseriti lati, si è ottenuto un albero di supporto minimo. Pseudocodice Di seguito è riportato lo pseudocodice relativo all’algoritmo di Kruskal: BEGIN ordinare i lati di G secondo costi non decrescenti; T* := ; WHILE |T*| < (n-1) DO scegliere un lato e E di costo minimo; E := E \ {e}; IF T* {e} non ha cicli THEN T* := T* {e}; END IF END-WHILE END 20 Fondamenti di Ricerca Operativa Capitolo 2: Ottimizzazione su grafi Analisi della complessità 1. Come noto, l’algoritmo degli m lati che costituiscono il grafo di partenza ha un costo 2. Si ha poi un ciclo iterato volte, all’interno del quale si deve verificare se l’inserimento del nuovo lato comporta la creazione di cicli. Tale verifica può avvenire in costo costante, a patto che si conosca la componente connessa alla quale ciascun nodo appartiene: in questo caso infatti per verificare se si creano cicli è sufficiente controllare se i nodi congiunti dal nuovo lato appartengono alla stessa componente connessa (e in questo caso si avrà un ciclo) oppure no. 3. In questo modo è però poi necessario eseguire, all’interno dello stesso ciclo iterato un’operazione di aggiornamento delle etichette, che richiede un costo . volte, In conclusione, la complessità dell’algoritmo è: Siccome nel caso peggiore sappiamo che , possiamo anche scrivere: Si può però notare che, mediante l’uso di opportune strutture dati, è possibile ridurre ulteriormente la complessità dell’algoritmo di Kruskal. In ogni caso, la complessità dell’algoritmo di Kruskal è superiore rispetto a quella dell’algoritmo di Prim. Esattezza dell’algoritmo di Kruskal Consideriamo un generico lato scartato dall’albero ottenuto come risultato dell’algoritmo di Kruskal. Inoltre, siccome i lati vengono esplorati in ordine non decrescente di costo, è chiaro che l’inserimento del lato darebbe vita ad un ciclo nel quale tutti i lati diversi da hanno un costo non superiore al costo di , e perciò è facile concludere che non esistono lati di diminuzione, e quindi per la condizione di ottimalità possiamo affermare che l’albero è ottimo. Verifica di ottimalità La condizione di ottimalità può essere utilizzata per verificare se un certo albero di supporto è ottimo oppure no: è sufficiente verificare che tutti i lati appartenenti al grafo di partenza ma non appartenenti a non siano lati di diminuzione; se almeno uno è un lato di diminuzione, l’albero non è ottimo, altrimenti lo è. 21 Capitolo 2: Ottimizzazione su grafi Fondamenti di Ricerca Operativa Il problema dei cammini ottimi Formulazione del problema dei cammini ottimi Introduzione al problema Il problema dei cammini ottimi (o minimi, o massimi) è un problema molto diverso da quello dell’individuazione dell’albero di supporto minimo. Ricercare i cammini ottimi significa infatti trovare per una certa coppia di nodi il cammino più breve che consente di collegare i due nodi della coppia stessa. Come vedremo, in realtà se si sovrappongono tutti i cammini ottimi a partire da una certa sorgente si ottiene ancora un albero di supporto, che però non è affatto l’albero di supporto ottimo. Formulazione matematica del problema Dato un grafo orientato , data una funzione di costo , che associa ad ogni lato appartenente ad il relativo costo , e dati due nodi e si vuole determinare un cammino di costo ottimo (cioè o minimo o massimo) da a , dove è detto origine. Applicazioni del problema I problemi di cammini ottimi (minimi o massimi) hanno innumerevoli applicazioni; ad esempio: 1. Pianificazione e gestione di reti di trasporto, elettriche, idrauliche, di comunicazione, … 2. Pianificazione di progetti complessi (relazioni logiche tra entità). Osservazioni Si noti che la formulazione che abbiamo fornito fa riferimento ai grafi orientati, i quali però sono di fatto una generalizzazione dei grafi non orientati: ogni grafo non orientato è rappresentabile mediante un grafo orientato nel quale per ogni lato , si ha sempre anche il lato all’interno del grafo stesso. Si noti inoltre che, come vedremo, in questo caso non è applicabile un approccio di tipo greedy: la soluzione fornita dall’algoritmo non sarebbe infatti una soluzione ottima. L’algoritmo di Dijkstra L’algoritmo Sia dato un grafo orientato e sia il costo associato all’arco , con se . Sia inoltre un nodo del grafo . L’algoritmo di Dijkstra consente di individuare un cammino minimo che ha come sorgente per ognuno dei nodi appartenenti ad come destinazione, a patto che per ogni nodo appartenente ad A si abbia . Pseudocodice Lo pseudocodice che rappresenta formalmente l’algoritmo è il seguente: BEGIN S := {s}; L[s] := 0; pred[s] := s; WHILE |S| ≠ n DO individuare (v,h) L[h] := L[v] + cvh; pred[h] := v; S := S {h}; END-WHILE END 22 δ+(S) : L[v] + cvh = min{L[i] + cij : (i,j) δ+(S)}; Fondamenti di Ricerca Operativa Capitolo 2: Ottimizzazione su grafi Spiegazione dell’algoritmo L’idea sulla quale si basa l’algoritmo è quella di esplorare tutti i nodi del grafo in ordine crescente rispetto al costo di un cammino minimo da al nodo stesso. Per fare ciò, si associa ad ogni nodo appartenente ad un’etichetta che, al termine dell’algoritmo, rappresenta il costo minimo del cammino da a . Si associa inoltre ad ogni nodo anche un valore , che rappresenta il predecessore di nel cammino da a . L’etichetta è perciò del tipo . Nel dettaglio, si usano le seguenti strutture di dati: 1. Un insieme che, in ogni istante, rappresenta l’insieme di tutti i nodi le cui etichette sono definitive. 2. I valori 3. I predecessori di ogni nodo, ovvero Di fatto quindi si inizia fissando l’etichetta del nodo , con il valore . Dopodiché, si inserisce il nodo nell’insieme , inizialmente vuoto, e si inizia ad iterare un procedimento che termina solo quando contiene tutti gli nodi del grafo, e che prevede che ad ogni iterazione si considerino tutti gli archi del taglio uscente da , e si individui quello tale che la somma tra il costo dell’arco stesso e il costo per arrivare ad , ovvero , sia minima. A questo punto, si inserisce in il nodo e si aggiorna la sua etichetta, in modo che il predecessore di sia e il costo sia dato dal costo dell’arco stesso , sommato al valore . Esattezza dell’algoritmo di Dijkstra Possiamo ora dimostrare l’esattezza dell’algoritmo. Per farlo è sufficiente dimostrare che effettivamente abbia in ogni passo il significato precedentemente descritto, ovvero: Infatti, al termine dell’algoritmo avremo , perciò tutti i costi saranno quelli dei cammini minimi. Dimostriamolo per induzione sul numero di passi : 1. 2. Base induttiva: se , l’affermazione è senz’altro vera, perché abbiamo , e Passo induttivo: ipotizziamo che l’affermazione sia vera al passo -esimo. Al passo , inseriamo in il nodo . Chiamiamo il cammino ottenuto da a e verifichiamo che per ogni altro cammino da a si ha . Il cammino può essere scomposto in tre parti: , e , dove è il primo arco tra quelli di che appartiene anche al taglio uscente di (certamente ne esiste uno, ma potrebbe esisterne anche più di uno). Avremo allora: Per l’ipotesi induttiva, avremo . Inoltre, applicabilità dell’algoritmo di Dijkstra. Perciò: è certamente non negativo, per l’ipotesi di Ma se applicando l’algoritmo abbiamo scelto il nodo , questo significa necessariamente che: E perciò la proprietà continua a valore anche per il passo successivo . 23 Capitolo 2: Ottimizzazione su grafi Fondamenti di Ricerca Operativa Analisi della complessità La complessità dell’algoritmo dipende dal modo attraverso il quale viene individuato l’arco del taglio di in ogni singola iterazione. tra tutti quelli Se tale arco viene individuato mediante una scansione completa di tutti gi archi del grafo, risulta ovvio che il costo totale dell’algoritmo è: Se invece le etichette vengono determinate per aggiornamento, ovvero si memorizza qual è il lato tra quelli dell’arco uscente di che consente di raggiungere il nodo con il costo minimo (tra tutti quelli del taglio uscente da stesso), sarà sufficiente considerare per ogni nodo non appartenente a un solo arco, e quindi la complessità totale diventa: L’algoritmo di Floyd-Warshall L’algoritmo Come abbiamo già sottolineato, l’algoritmo di Dijkstra non può essere applicato se i costi degli archi non sono tutti non negativi. In questo caso si usa allora l’algoritmo di Floyd-Warshall, il quale permette di determinare i cammini minimi tra tutte le coppie di nodi di un grafo, anche in presenza di archi di costo negativo, a patto però che il problema sia ben definito, ovvero che non vi siano cicli di costo complessivo negativo: in tal caso infatti più volte si percorre il ciclo, minore sarà il costo, e perciò è chiaro che non si ha un cammino di costo minimo. Nel caso in cui il problema sia mal posto, l’algoritmo consente di individuare l’anomalia. Spiegazione dell’algoritmo È dato un grafo orientato , descritto mediante la matrice di incidenza nodo-nodo nella quale ogni elemento è il costo dell’arco , e nel caso in cui tale arco non appartenga ad è infinito. Si utilizzano due matrici : una matrice il cui generico elemento è, al termine dell’algoritmo, il costo del cammino minimo da a , ed una matrice il cui elemento rappresenta alla fine il predecessore di nel cammino minimo da a stesso. La matrice viene inizializzata in modo che sia uguale alla matrice di incidenza, mentre la matrice inizializzata in modo che per ogni e si abbia . viene Si procede poi considerando tutti gli nodi del grafo. Ad ognuna delle iterazioni, si considera, per ogni coppia di nodi tale che e siano diversi da (senza però escludere il caso ), il cammino che si ottiene triangolando rispetto ad . In altri termini, si confronta con il costo del cammino che prevede di andare da ad e poi da a , ovvero . Se tale costo risulta inferiore rispetto a , si pone e si pone . Al termine dell’iterazione, si verifica se esiste sulla diagonale principale almeno un elemento con costo totale negativo: in caso affermativo, significa che il problema è mal posto; in caso contrario, si procede con la prossima iterazione. 24 Fondamenti di Ricerca Operativa Capitolo 2: Ottimizzazione su grafi Pseudocodice Di seguito è riportato lo pseudocodice che riassume l’algoritmo: BEGIN FOR i:=1 TO n DO FOR j:=1 TO n DO pij := i; END-FOR END-FOR FOR h:=1 TO n DO /* operazione triangolare su h */ FOR i:=1 TO n WITH i ≠ h DO FOR j:=1 TO n WITH j ≠ h DO IF (dih + dhj < dij) THEN dij = dih + dhj; pij := phj; END-IF END-FOR END-FOR FOR i:=1 TO n DO IF dii < 0 THEN STOP; END-IF /* un circuito negativo */ END-FOR END-FOR END Complessità La complessità dell’algoritmo è prevede iterazioni. : risulta infatti chiaro che si hanno 3 cicli annidati, ciascuno dei quali La programmazione dinamica In Informatica, la programmazione dinamica è una tecnica di progettazione di algoritmi basata sulla divisione del problema in sottoproblemi e sull'utilizzo di sottostrutture ottimali. In particolare, diciamo che un problema possiede una sottostruttura ottimale se è possibile costruire efficientemente una soluzione ottimale a partire dalle soluzioni ottimali dei suoi sottoproblemi. Cercheremo ora di applicare il metodo della programmazione dinamica per l’individuazione dei cammini minimi all’interno di un grafo orientato, nel caso particolare in cui il grafo non abbia circuiti. Premessa: ordinamento topologico Definizione Dato un grafo orientato aciclico , è sempre possibile definire un ordinamento, detto topologico, tra tutti gli archi appartenenti ad , in modo tale che per ogni , si abbia , dove il simbolo indica la relazione “minore di” definita dall’ordinamento stesso. Naturalmente, l’ordinamento topologico equivale ad assegnare un numero ad ognuno dei nodi del grafo. Algoritmo per individuare l’ordinamento topologico L’ordinamento topologico di un grafo può essere ottenuto mediante un semplice algoritmo: 1. Si individua il nodo tale che , ovvero tale che non esista alcun arco incidente su (ovvero entrante in ), e si assegna a tale nodo il minore numero tra quelli non ancora assegnati (1 se è il primo). 2. Si elimina dal grafo il nodo , insieme a tutti gli archi da esso uscenti. 3. Si ripetono i punti precedenti, e si termina solo quando tutti i nodi sono stati eliminati dal grafo di partenza. La complessità dell’algoritmo è: 25 Capitolo 2: Ottimizzazione su grafi Fondamenti di Ricerca Operativa Come usare la programmazione dinamica? Il problema Come già accennato, il problema che vogliamo risolvere è quello di individuare, all’interno di un grafo orientato e aciclico , il cammino minimo da un nodo ad un nodo (dove ). Spiegazione dell’algoritmo Qualsiasi cammino minimo che abbia almeno due archi può essere scomposto in due cammini quali il secondo sia costituito solamente da un arco, ovvero . s s t i e dei t Figura 6: Scomposizione del cammino. Il costo di un cammino dal nodo a un qualsiasi nodo può essere indicato con . Si può osservare che: Nelle ipotesi che abbiamo fatto, è possibile definire l’ordinamento topologico dei nodi del grafo, e, considerata la definizione di ordinamento topologico, possiamo con certezza affermare che se un nodo è incidente in , avremo , quindi possiamo riscrivere la formula come: Ponendo per semplicità precedente: , possiamo a questo punto scrivere una serie di equazioni da aggiungere alla E possiamo facilmente risolvere tali equazioni in ordine inverso. In questo modo individuiamo i costi dei cammini minimi da a tutti gli altri nodi. Naturalmente poi il predecessore del nodo di arrivo finale sarà sempre il valore scelto per ottenere il minimo, e quindi sulla base di questa informazione possiamo costruire l’albero dei cammini minimi. Analisi di complessità 1. Come accennato, l’ordinamento topologico ha complessità . 2. Dopodiché, si esegue l’algoritmo vero e proprio, nel quale ogni arco viene considerato esattamente una volta, e perciò la complessità totale di questa fase è . Possiamo allora concludere che la complessità dell’algoritmo è . Esattezza L’algoritmo di Programmazione Dinamica per il problema dei cammini di costo ottimo (minimo o massimo) nei grafi senza circuiti è esatto. Possiamo verificarlo grazie al seguente principio di ottimalità: Per ogni cammino minimo (massimo) esiste un nodo tale che il cammino può essere scomposto in due sottocammini 26 e , dove il sottocammino è di costo minimo (massimo) da s a i. Fondamenti di Ricerca Operativa Capitolo 2: Ottimizzazione su grafi 27 Capitolo 4: Programmazione lineare Fondamenti di Ricerca Operativa Capitolo 4: Programmazione lineare I problemi di programmazione lineare La programmazione lineare (PL oppure LP) è quella branca della ricerca operativa che si occupa di studiare algoritmi di risoluzione per problemi di ottimizzazione lineari. Un problema è detto lineare se sia la funzione obiettivo sia i vincoli sono funzioni lineari. Ipotesi alla base dei modelli di PL Di conseguenza, i modelli che vengono risolti mediante la programmazione lineare si basano su alcune ipotesi: Ipotesi di linearità Questa ipotesi può in realtà essere vista come l’unione di due diverse ipotesi: a) Proporzionalità: il contributo di ogni variabile è dato dal prodotto tra il valore della variabile stessa ed una certa costante. Non si può perciò tenere conto, ad esempio, delle economie di scala. b) Additività: il contributo di tutte le variabili è dato dalla somma dei contributi delle singole variabili. Questo non ci permette ad esempio di rappresentare alcune particolari situazioni, come quelle che si verificano nel caso di prodotti in competizione tra di loro (in tal caso infatti i guadagni non sono tra loro indipendenti). Ipotesi di parametri costanti Si assume che tutti i parametri numerici che compaiono nel modello assumano dei valori costanti e che possano essere stimati con sufficiente precisione. Ipotesi di divisibilità Tutti i parametri che compaiono all’interno del modello possono assumere un qualsiasi valore reale. Problema di programmazione lineare: forma generale La forma generale di un problema di programmazione lineare prevede che si abbia una certa funzione da minimizzare o da massimizzare e che la regione ammissibile sia costituita da vincoli di uguaglianza e di disuguaglianza debole. Inoltre, alcune variabili devono essere non negative, altre invece possono anche essere libere: , , Forma matriciale Il problema appena introdotto può essere rappresentato anche in forma matriciale: Soluzione ammissibile e soluzione ottima Una soluzione del problema è quindi un vettore del problema stesso. Una soluzione è ammissibile se Una soluzione è ottima se 28 che assegna un valore a ciascuna delle variabili di decisione . (si ipotizza che il problema sia di minimizzazione). Fondamenti di Ricerca Operativa Capitolo 4: Programmazione lineare Risoluzione grafica dei problemi di PL Il metodo grafico Se il problema di PL è in 2 sole variabili, la regione ammissibile può essere facilmente rappresentata all’interno dello spazio in due dimensioni. Inoltre, le linee di livello sono delle rette e, calcolando il gradiente della funzione obiettivo, si può facilmente individuare qual è la direzione di massimo aumento della funzione obiettivo stessa. Graficamente si può perciò “seguire” questa direzione (o, se si deve minimizzare, ci si può muovere nella direzione opposta), ed individuare quale è l’ultimo punto della regione ammissibile che si incontra seguendo la direzione prescelta: tale punto rappresenterà la soluzione ottima del problema. Formalizzazione dei concetti geometrici legati alla regione ammissibile Ogni vincolo di disuguaglianza definisce un semispazio affine nello spazio delle variabili. Il semispazio affine in è infatti un sottoinsieme di del tipo , dove rappresenta il gradiente. Il semispazio affine è sempre delimitato dall’iperpiano . Nel caso in cui si abbia , un semispazio affine non è altro che un semipiano. La regione ammissibile è un poliedro P, ovvero l’intersezione di un numero finito di semispazi affini. La regione ammissibile può essere vuota, limitata oppure illimitata. La regione ammissibile è sempre convessa. Si ricorda che un insieme è convesso se, per ogni coppia di punti appartenenti all’insieme stesso, il segmento che congiunge tali punti appartiene interamente ad . Il segmento che congiunge due punti e è l’insieme di tutte le combinazioni convesse di e : Possiamo facilmente verificare che la regione ammissibile è convessa: intuitivamente un semispazio affine è convesso, e l’intersezione finita di insiemi convessi è sempre a sua volta un insieme convesso. Chiamiamo vertici del poliedro tutti e soli i punti del poliedro stesso che non possono essere espressi come combinazione convessa di una coppia di due diversi punti del poliedro stesso. Un poliedro non vuoto ha sempre un numero di vertici finito e maggiore o uguale ad 1. Una direzione ammissibile di un poliedro P è un vettore ( ) tale che per ogni punto ad , il raggio di tale punto è completamente contenuto in P. Il raggio di è così definito: Intuitivamente, ciò significa che ci si può “muovere illimitatamente” da all’interno del poliedro P. nella direzione appartenente , restando sempre A questo punto, abbiamo tutti gli elementi che ci consentono di definire formalmente una rappresentazione dei poliedri: ogni punto del poliedro P può essere rappresentato come una combinazione convessa dei suoi vertici, più una eventuale direzione ammissibile . Se il poliedro P è limitato, ogni punto di P può essere rappresentato come combinazione convessa dei suoi vertici. 29 Capitolo 4: Programmazione lineare Fondamenti di Ricerca Operativa Teorema fondamentale Dato un problema di PL (di minimizzazione), se il poliedro delle sue soluzioni ammissibili non è vuoto, allora o esiste almeno un vertice ottimo, oppure il valore della funzione obiettivo non è limitato inferiormente (cioè non esiste una soluzione ottima). Possiamo fare alcune osservazioni: I punti interni del poliedro delle soluzioni ammissibili non sono mai soluzioni ottime: spostandosi anche di poco nella direzione di massimo aumento (o diminuzione) della funzione obiettivo, si troverà sempre una soluzione migliore. In un vertice ottimo, tutte le direzioni che permettono di rispettare l’ammissibilità sono delle direzioni peggioranti. Il problema di programmazione lineare può sempre essere risolto in un tempo finito: è sufficiente esaminare tutti i vertici del poliedro delle soluzioni ammissibili, che sappiamo essere in numero finito. Naturalmente però il numero di vertici è molto spesso elevatissimo, e la complessità di una soluzione di questo genere è del tutto inaccettabile nella pratica. Per tale motivo, vengono utilizzati opportuni algoritmi, dei quali il più noto è il metodo del simplesso. Possibili soluzioni dei problemi di PL I problemi di PL possono essere: Problemi con soluzione ottima e unica (nel caso con ottimo del poliedro delle soluzioni ammissibili). Problemi con un numero infinito di soluzioni ottime (nel caso con , ciò equivale ad individuare un intero lato del poliedro delle soluzioni ammissibili costituito da soluzioni ottime). Problemi di PL illimitati (il poliedro delle soluzioni ammissibili è illimitato e non esiste alcuna soluzione ottima). Problemi di PL inammissibili (la regione ammissibile è vuota). , ciò equivale ad individuare uno ed un solo vertice Rappresentazione di un problema di PL in forma standard Forma standard Ogni problema in forma generale può essere rappresentato da un problema equivalente in una forma diversa, detta forma standard, nella quale la funzione obiettivo deve sempre essere minimizzata, tutti i vincoli vengono trasformati in vincoli di uguaglianza e tutte le variabili devono essere non negative: (o equivalentemente scriviamo 30 ) Fondamenti di Ricerca Operativa Capitolo 4: Programmazione lineare Dalla forma generale alla forma standard Per passare dalla forma generale alla forma standard è necessario seguire un semplice procedimento, nel corso del quale è possibile che vengano introdotte nuove variabili e nuovi vincoli. 1. Se il problema era di massimizzazione, occorre trasformarlo in un problema di minimizzazione, semplicemente cambiando il segno a e ricordando che il valore ottimo di trovato al termine, dovrà essere cambiato di segno: 2. I vincoli del tipo “minore o uguale” vengono trasformati in uguaglianze aggiungendo una variabile di scarto , non negativa, che viene sommata alla parte sinistra della disuguaglianza: diventa 3. I vincoli del tipo “maggiore o uguale” vengono trasformati in uguaglianze aggiungendo una variabile di surplus , non negativa, che viene sottratta alla parte sinistra della disuguaglianza: diventa 4. Se una variabile è libera, allora viene eliminata dal problema e sostituita con altre 2 variabili non negative , che rappresentano la “componente positiva” di quindi e quella negativa. Ad ogni occorrenza di e sostituiamo . Equivalenza tra le due rappresentazioni Le due rappresentazioni, come già accennato, sono equivalenti: 1. Data una soluzione ammissibile di un problema in forma generale, possiamo estendere questa soluzione, assegnando le variabili aggiuntive nell’unico modo possibile, imposto dalle equazioni che definiscono la regione ammissibile del corrispondente problema in forma standard. 2. Per passare dalla soluzione di un problema di PL in forma standard alla soluzione del problema di PL di partenza, in forma generale, è sufficiente eliminare dalla soluzione delle variabili di scarto e di surplus. Naturalmente, questa corrispondenza vale anche per le soluzioni ottime. 31