5 Programmazione lineare Risoluzione di modelli Sono disponibili molti programmi per risolvere problemi di PL. Alcuni sono commerciali ed altri liberi. Alcuni sono concepiti puramente per risolvere problemi di PL ed altri permettono di risolvere la PL all interno di programmi generali. Un elenco del software disponibile per la PL aggiornato al 2007 si può reperire al sito [81]. I dati necessari ad identificare un istanza di PL sono costituiti dalla matrice dei vincoli e dai vettori dei costi e dei termini noti, più alcuni indicatori sul tipo di vincolo (, = oppure ). Per la natura tabellare dei dati e delle relative operazioni è abbastanza naturale che i fogli elettronici siano anche predisposti a risolvere problemi di PL. Ad esempio Excel è in grado di farlo, purché si sia installato il Solver (che normalmente richiede una installazione ad hoc). I dati da passare ad un foglio elettronico sono in forma di matrice esplicita. Inoltre, usando le varie funzioni, si possono impostare in modo implicito anche vincoli complessi. I fogli elettronici presentano dei limiti naturali nel numero di vincoli e variabili che possono gestire e nella velocità di calcolo, troppo bassa già per problemi di piccola-media grandezza (poche centinaia di variabili e vincoli) e soprattutto per problemi di PL intera. Inserire i dati direttamente come matrice può essere abbastanza scomodo, specie se la matrice è sparsa (come avviene normalmente, soprattutto quando la matrice è molto grande) ed obbedisce ad una struttura particolare. Per questo motivo sono stati sviluppati programmi che permettono di fornire i dati in forma molto strutturata e poi generano la matrice da passare all algoritmo risolutore in modo trasparente per l utente. Uno di questi programmi è LINGO, disponibile in versioni di potenza crescente. Nella versione industrial, che è servita a risolvere la maggior pare dei modelli presenti in questo testo, si possono affrontare problemi di media grandezza, più esattamente fino a 16.000 vincoli, 32.000 variabili e 3.200 variabili intere. Nella versione extended i limiti sono quelli della macchina stessa. La velocità di calcolo di Lingo è abbastanza elevata e per i problemi di PL fornisce la soluzione praticamente in tempo reale. Anticipando argomenti P. S e r a f i n i, R i c e r c a O p e r a t i v a S p r i n g e r - V e r l a g I t a l i a, M i l a n o 2 0 0 9
80 5 Programmazione lineare risoluzione di modelli del Cap. 7, facciamo presente che, anche se una velocità di calcolo elevata è benvenuta nella risoluzione di problemi di PL intera, tuttavia può avereun impatto pressoché nullo se il modello è inadeguato. Uno dei risolutori più potenti è CPLEX, un insieme di librerie scritte in C che gestiscono vari aspetti della risoluzione di un problema di PL. Le librerie vanno chiamate all interno di programmi in C scritti dall utente, che, nell ipotesi minimale, si limitano a scrivere la matrice dei dati. Simile a CPLEX, ma meno potente e con il grande vantaggio di essere disponibile gratuitamente, è il software glpk, sviluppato all interno del mondo gnu. Quando si affrontano problemi reali di una certa complessità è naturale progettare vari moduli di calcolo interagenti fra loro e basati su idee algoritmiche anche molto diverse. L ultima versione di Lingo permette anche di costruire dei programmi dichiarativi che al loro interno chiamano vari modelli di PL (o PL intera) all interno di un comune ambiente di dati e variabili. Ovviamente è possibile fornire la soluzione (primale o duale) di un modello agli altri. Si tratta di un ambiente di sviluppo e calcolo molto efficace, con l unica limitazione che ognuno dei sottoproblemi deve essere risolto come PL. A dire il vero, dato che l ambiente è un vero programma dichiarativo, con i consueti controlli del flusso e con le consuete strutture dati, è anche possibile costruirsi algoritmi ad hoc all interno del programma. La cosa però presenta problemi dato che non è possibile una vera programmazione strutturata. Se si vuole avere la massima libertà e flessibilità, è indispensabile fare ricorso a risolutori quali CPLEX o glpk, che possono essere chiamati più volte all interno di complessi programmi scritti dall utente. Tuttavia, siccome la scrittura del relativo codice richiede un grande investimento di tempo, può essere opportuno sviluppare dei prototipi con strumenti di più rapido impiego come Lingo (o simili) e passare poi a CPLEX quando il progetto di tutta la metodologia risolutiva si sia dimostrata sufficientemente affidabile. In questa sede utilizzeremo Excel e Lingo per illustrare i vari modelli. Inizialmente diamo una breve descrizione di come impostare i calcoli per un piccolo modello. Una volta imparati i primi rudimenti, il lettore è certamente in grado di sviluppare da solo modelli più complicati, per cui in questo testo non si ritornerà più su descrizioni dettagliate e tecniche dell utilizzo di Excel o di Lingo. In ogni caso i programmi sono disponibili in rete al sito [202]. 5.1 Risoluzione di un problema di PL con Excel Facciamo vedere come risolvere con Excel il problema descritto a pag. 61. Riportiamo i dati con i nomi delle grandezze a cui si riferiscono, ad esempio nel modo indicato in Fig. 5.1. Le celle B2 e C2 conterranno i numeri di pezzi, che saranno calcolati dal programma. Tuttavia possiamo sempre impostare dei valori iniziali, ad esempio possiamo impostare i valori 10 per il numero di pezzi dell oggetto 1 e 5
5.1 PL con Excel 81 1 2 3 4 5 6 7 8 A B C D E oggetto 1 oggetto 2 numero pezzi profitto prezzi 120 180 ore richieste ore disponibili ore-macchina 1 15 55 960 ore-macchina 2 35 45 960 ore-uomo 60 100 1920 Figura 5.1. per l oggetto 2. Noti questi valori possiamo far calcolare ad Excel il profitto indicando nella cella D3 la formula =SUMPRODUCT(B$2:C$2,B3:C3) che automaticamente esegue il prodotto scalare del vettore dei numeri dei pezzi per il vettore dei prezzi (l indirizzo della riga 2 deve essere assoluto dato che ora copieremo la formula per le ore richieste in base al numero di pezzi assegnato). Copiando direttamente la cella D3 sulle celle D6:D8, il foglio si presenta come in Fig. 5.2. 1 2 3 4 5 6 7 8 A B C D E oggetto 1 oggetto 2 numero pezzi 10 5 profitto prezzi 120 180 2100 ore richieste ore disponibili ore-macchina 1 15 55 425 960 ore-macchina 2 35 45 575 960 ore-uomo 60 100 1100 1920 Figura 5.2. Si tratta ora di far intervenire il Solver, che si trova nel Menù dei Tools. Compare una finestra con la quale si dichiara quale è il valore da massimizzare (o minimizzare), quali sono le variabili e quali sono i vincoli, nonché alcune opzioni dell ottimizzatore: - obiettivo: la cella che contiene il valore della funzione obiettivo è nel nostro esempio la cella D3. Quindi bisogna indicare (direttamente cliccando sul foglio) l indirizzo $D$3 nella finestra Set Target Cell cliccando poi max o min a seconda del caso ( max nel nostro caso); - variabili: ci si posiziona nella finestra By changing cells e si selezionano le due celle dei numeri di pezzi. Nella finestra compare l indirizzo (multiplo) $B$2:$C$2. Si possono anche operare selezioni multiple se ad esempio le variabili non sono necessariamente posizionate nel foglio come vettori o matrici. - vincoli: si clicca su Add e compare una tripla finestra di dialogo in cui i valori di sinistra sono vincolati rispetto a quelli di destra. Nel nostro caso dobbiamo fare in modo che le ore richieste in base ai numeri dei pezzi siano
82 5 Programmazione lineare risoluzione di modelli non superiori alle ore disponibili. Quindi nella finestra di sinistra selezionamo il vettore di ore richieste, in quella centrale selezionamo l operatore che ci interessa (nel nostro caso ) e in quella di destra selezionamo il vettore di ore disponibili. Cliccando done il vincolo è inserito (direttamente per tutte le righe). Resterebbe da inserire il vincolo di non negatività, ma di questo si tiene conto in altro modo; - opzioni di calcolo: cliccando su Options compare una finestra in cui bisogna selezionare Assume Linear Model e Assume Non-Negative. Poi si clicca OK. A questo punto ricompare la finestra del Solver. Basta cliccare su Solve e Excel inizia il calcolo, che in questo caso è immediato. I valori dei numeri dei pezzi nella tabella vengono modificati e compaiono i valori ottimi. Excel chiede se si vogliono dei rapporti. Dei tre rapporti il più interessante è quello di sensibilità (Sensitivity Report) che fornisce le variabili duali e le relazioni di complementarità. Notiamo come non sia necessario indicare una soluzione iniziale necessariamente ammissibile (ad esempo la soluzione nulla sarebbe la scelta naturale). Il sistema risolve il problema indipendentemente dalla soluzione iniziale indicata. 5.2 Risoluzione di un problema di PL con LINGO Risolviamo lo stesso problema con LINGO. Ogni modello scritto in LINGO consta di tre parti. Nella prima (racchiusa fra i comandi SETS: e ENDSETS) si descrive la struttura del problema. Nella seconda (racchiusa fra i comandi DATA: e ENDDATA) si inseriscono i dati, basandosi sulla struttura appena definita. Nella terza si scrivono i vincoli e l obiettivo. L esempio considera oggetti e risorse. Quindi strutturiamo il problema definendo il tipo di dati oggetti e il tipo di dati risorse, ad esempio nel seguente modo SETS: ogg/1..2/; ris/1..3/; ENDSETS Con il precedente comando si specifica che vi sono grandezze associate agli oggetti e che gli oggetti sono 2, e che vi sono grandezze associate alle risorse e che le risorse sono 3. I termini ogg e ris sono una scelta dell utente. La quantità di grandezze associata ad un tipo viene indicata dall espressione /1..n/. Questo non è l unico modo di introdurre i tipi. Può spesso essere utile dare un nome ad ogni elemento del tipo. Ad esempio la prima e la seconda risorsa sono ore-macchine mentre la terza sono ore-uomo. In questo caso conviene ridefinire il precedente comando come SETS: ogg/1..2/; ris/macch1 macch2 man/; ENDSETS Dopo aver introdotto i tipi di grandezze è bene definire quali grandezze entrano in gioco nel problema. Nel nostro caso vi sono prezzi e quantità di
5.2 PL con Lingo 83 oggetti (associati agli oggetti) e quantità di risorse (associate alle risorse). Chiamando p il prezzo, x le quantità di oggetti e b le quantità di risorse, le grandezze vengono inserite nel seguente modo SETS: ogg/1..2/:p,x; ris/macch1 macch2 man/:b; ENDSETS Bisogna ancora definire la matrice. Notiamo che la matrice è una struttura composta definita a partire dalle strutture oggetti e risorse. Diamo il nome mat al tipo matrice e chiamiamo a la grandezza associata al tipo matrice. Il comando viene allora riscritto come SETS: ogg/1..2/:p x; ris/macch1 macch2 man/:b; mat(ris,ogg):a; ENDSETS A questo punto bisogna inserire i dati. Automaticamente il programma capirà che le grandezze non definite come dati sono variabili e passerà a formulare ilmodello.sinoticheidatidiunamatricevengonoinseritiperrighe. DATA: p=120 180; b=480 480 960; a=15 55 35 45 60 100; ENDDATA Nelle ultime versioni di Lingo è stata data la possibilità di considerare come dati anche alcuni valori definiti nella sezione SETS. Questo modo di impostare il modello è raccomandabile rispetto al precedente, perché permette di esplicitare e usare nel modello alcuni dati pertinenti alla grandezza dei dati, cosa che nelle vecchie versioni non era possibile (a meno di replicare nei dati i valori minando però la robustezza del modello rispetto a variazioni di dati). Quindi si può impostare il modello nel seguente modo alternativo (e preferibile) dove si è aggiunta la grandezza numogg (numero degli oggetti): SETS: ogg:p x; ris:b; mat(ris,ogg):a; ENDSETS DATA: numogg=2; ogg=1..numogg; ris=macch1 macch2 man; p=120 180; b=480 480 960; a=15 55 35 45 60 100; ENDDATA Nella parte finale, dove si devono indicare i vincoli e il modello, la sommatoria viene realizzata attraverso il comando @SUM, mentre la ripetizione di un vincolo si effettua con il comando @FOR. Nell esempio si ha @FOR(ris(i):@SUM(ogg(j): a(i,j) * x(j) ) < b(i) ); in cui si vede esplicitato il tipo su cui si effettua la somma o l iterazione. Le variabili i e j sono variabili mute e possono essere sostituite da una qualsiasi altra variabile (purché non già presente nel modello). Il segno < significa convenzionalmente (nei problemi reali l insieme delle soluzioni ammissibili è chiuso; infatti se una successione di punti è ammissibile, si considera che lo sia anche il punto limite; quindi diseguaglianze strette non intervengono mai come vincolo).
84 5 Programmazione lineare risoluzione di modelli Il vincolo di non negatività viene assunto implicitamente. Quindi se alcune variabili non avessero questo vincolo, il fatto deve essere esplicitato con il comando @FREE(nomevar). È bene prestare attenzione a questa circostanza perché spesso ci sono delle variabili svincolate e ci si può dimenticare di usare il comando @FREE. Non facendolo, la soluzione che viene calcolata non è ovviamente la soluzione cercata. È quasi sempre conveniente dare un nome anche ai vincoli, per individuare facilmente le variabili duali. Questo viene realizzato nel modo seguente (dove la parola vinc è una scelta dell utente) @FOR(ris(i): [vinc] @SUM(ogg(j): a(i,j) * x(j) ) < b(i) ); L obiettivo viene espresso come (dove il termine MAX è del programma): MAX= @SUM(ogg(j): p(j)* x(j) ) ; Quindi globalmente il problema viene modellato come: SETS: ogg: p, x; ris: b; mat(ris,ogg): a; ENDSETS DATA: numogg=2; ogg=1..numogg; ris=macch1 macch2 man; p= 120 180; b=480 480 960; a=15 55 35 45 60 100; ENDDATA max= @SUM(ogg(j): p(j)* x(j) ) ; @FOR(ris(i): [vinc] @SUM(ogg(j): a(i,j) * x(j) ) < b(i) ); Prima di eseguire conviene scegliere fra le opzioni quella che prevede un output terso anziché verboso. Dato il comando SOLVE, il programma costruisce il modello di PL (segnalando eventuali errori di sintassi) e poi esegue le iterazioni del metodo del simplesso. Alla fine produce una finestra di stato ( Status windows ) in cui si ha la seguente informazione Global optimal solution found. Objective value: 1800.000 Infeasibilities: 0.000000 Total solver iterations: 2
5.3 Dieta: risoluzione del primo modello 85 dove per iterations si intendono le iterazioni del metodo del simplesso, ovvero quanti cambiamenti di base sono stati necessari per arrivare alla soluzione. A richiesta vengono fornite le tabelle delle soluzioni. Chiedendo i valori delle variabili x compare la seguente tabella Variable Value Reduced Cost X( 1) 6.0000.0000000 X( 2) 6.0000.0000000 dove, nella terza colonna sotto il termine reduced cost (si veda a pag. 69) viene riportato il valore della variabile ausiliaria del problema duale. Le condizioni di complementarità impongono che in ottimalità almeno una delle due quantità, o una variabile primale oppure la sua corrispondente variabile ausiliaria nel problema duale, debba essere nulla. Quindi in ogni riga almeno uno dei due valori deve essere nullo. Se sono nulli entrambi significa che siamo in presenza di degenerazione. Chiedendo invece i valori delle variabili duali relativamente ai vincoli vinc, viene prodotta la tabella Row Slack or Surplus Dual Price VINC( MACCH1) 60.0000.0000000 VINC( MACCH2).0000000 1.500000 VINC( MAN).0000000 1.125000 Il valore riportato sotto la colonna slack è il valore della variabile ausiliaria primale. Quindi un valore nullo indica che il vincolo è attivo e viceversa se il valore è positivo. Sotto la colonna dual price è riportato il valore della variabile duale corrispondente al vincolo primale, ovvero il prezzo ombra della risorsa relativa al vincolo. Anche in questo caso la relazione di complementarità impone che almeno uno dei due valori sia nullo. 5.3 Dieta: risoluzione del primo modello Per il problema della dieta descritto nella sezione 2.1 sono stati scelti i seguenti 38 alimenti: pasta, riso, pane, fagioli, piselli, aglio, carote, cipolle, lattuga, melanzane, patate, pomodori, spinaci, succo d arancia, banane, mele, bistecca di manzo, bistecca di maiale, petto di pollo, fettina di vitello, prosciutto crudo, prosciutto cotto, calamari, cefali, cozze, sgombri, tonno, latte, formaggio grana, formaggio latteria, mozzarella, uova, burro, olio d oliva, cioccolato fondente, gelato, birra, vino. Sono stati poi scelti i seguenti 15 nutrienti: calorie, proteine, lipidi, glicidi, sodio (Na), potassio (K), magnesio (Mg), ferro (Fe), calcio (Ca), fosforo (P), vitamina B1, vitamina B2, vitamina B3, vitamina A, vitamina C. I dati numerici sono reperibili in rete, nei due modelli Excel e Lingo al sito [202]. I costi sono valori reali dell inverno 2006. Le preferenze sono espresse in una scala arbitraria da 0 a 10 ed esprimono le preferenze di chi scrive. La
86 5 Programmazione lineare risoluzione di modelli soluzione che si ottiene massimizzando la preferenza e ponendo un vincolo di 5 e sulla spesa è la seguente (i valori degli alimenti sono in hg, i valori dei nutrienti nelle rispettive unità di misura): Global optimal solution found. Objective value: 192.5977 Infeasibilities: 0.000000 Total solver iterations: 19 X( PASTA) 1.50 X( RISO) 0.35 X( FAGIOL) 1.00 X( PISELL) 1.00 X( CAROTE) 1.00 X( CIPOLL) 1.00 X( LATTUG) 3.00 X( MELANZ) 2.00 X( PATATE) 2.00 X( POMODO) 2.00 X( SPINAC) 1.00 X( ARANSU) 3.00 X( BANANE) 2.00 X( MELE) 3.00 X( SGOMBRO) 0.78 X( LATTE) 1.00 X( LATTER) 0.13 X( OLIOOL) 0.46 X( BIRRA) 2.00 X( VINO) 2.00 Y( PRO) 78.46 Y( LIP) 70.00 Y( GLIC) 369.4 Y( NA) 386.6 Y( K) 6559 Y( MG) 317.4 Y( FE) 18.04 Y( CA) 800.0 Y( P) 1216.5 Y( VITB1) 1.91 Y( VITB2) 2.18 Y( VITB3) 23.47 Y( VITA) 2981.966 Y( VITC) 381.0000 Y( CAL) 2500.000 La soluzione ottenuta non è del tutto inaccettabile (si tolga ad esempio la limitazione superiore sugli alimenti e si veda cosa succede). Tuttavia presenta delle difficoltà pratiche. I valori degli alimenti sono a volte molto strani (ad esempio i 13 grammi di formaggio latteria) e in ogni caso non riflettono il fatto che gli alimenti non si mangiano mai crudi o sconditi. Bisogna forse distribuire i 46 grammi d olio d oliva fra la lattuga, le melanzane e lo sgombro? Bisogna tener conto che realisticamente gli alimenti vengono mangiati secondo ricette che li mescolano in vari modi e introdurre esplicitamente questo nuovo legame. 5.4 Dieta: secondo modello Come un alimento è un mix di sostanze nutritive, così un piatto èunmix (ricetta) di alimenti. Realisticamente sono disponibili varie opzioni per ogni piatto, dato che le ricette non sono usualmente rigide. Tuttavia, sempre nello spirito di iniziare dal caso più semplice consideriamo che un piatto corrisponda ad una ricetta rigida. In alcuni casi (ad esempio la frutta, il formaggio, le bevande) non possiamo propriamente parlare di piatti, tuttavia possiamo sempre immaginare che, ad esempio, un singolo frutto sia un piatto di frutta. La presenza dei piatti sposta l obiettivo delle preferenze sui piatti piuttosto che sugli alimenti. Analogamente le limitazioni superiori sugli alimenti si spostano sui piatti e possiamo ragionevolmente pensare di limitare ad uno il valore massimo giornaliero per ogni piatto, e quindi si tratta di variabili che
5.5 Pianificazione di attività: risoluzione del secondo modello 87 possono assumere solo i valori 0 o 1. I costi rimangono invece ancorati agli alimenti. L introduzione dei piatti rende necessario considerare valori interi. Non è molto realistico (anche se possibile) avere una soluzione che richieda un valore frazionario per un piatto. Il fatto di pretendere valori interi per le variabili in un modello di PL ne cambia radicalmente le modalità di soluzione e aumenta in modo considerevole (a volte inaccettabile) i tempi di esecuzione. Nel prossimo capitolo si vedrà come risolvere un problema di PL intera. Per ora ci limitiamo a modellare il problema della dieta con variabili intere. Sia K l insieme dei piatti e siano z k {0, 1}, k K, inumeridipiattida calcolare. Sia r jk la quantità di alimento j presente nel piatto k (la matrice r jk è di fatto una matrice di ricette). Allora deve valere la relazione (vincolo strutturale) r jk z k = x j j J (5.1) k K Siano p k, k K, le preferenze sui piatti espresse in una scala numerica soggettiva. Abbiamo allora max p k z k k K c j x j C j J a ij x j = y i j J r jk z k = x j k K l i y i u i x j 0 z k {0, 1} Questo problema verrà risolto nella Sez. 8.1. i I j J i I j J k K (5.2) 5.5 Pianificazione di attività: risoluzione del secondo modello In questa sezione risolviamo il modello descritto in Sez. 2.5. Il modello più semplice di Sez. 2.4, per la cui risoluzione si può applicare un algoritmo ad hoc, verrà trattato più avanti, in Sez. 9.2. Facciamo riferimento ad un ipotetica costruzione di una casa per la quale sono state identificate le attività elencate in Tabella 5.1 con i rispettivi tempi di esecuzione nominali p e minimi p (espressi in giorni), i relativi costi unitari
88 5 Programmazione lineare risoluzione di modelli di riduzione d (in e) e le operazioni che devono precedere quella indicata (precedenze implicate da altre precedenze non vengono indicate). In Tabella 5.2 sono invece riportate le soluzioni di costo minimo e quelle di tempo minimo. In particolare s 0 e c 0 sono i tempi di inizio e fine attività impiegando durate nominali (e quindi a costo zero), mentre s 1 e c 1 sono i tempi di inizio e fine attività riducendo al massimo il tempo di completamento. Vengono anche indicate le riduzioni delle durate delle attività x 1 con i costi dx 1 per ogni attività. La soluzione di costo minimo prevede un tempo di completamento di 60 giorni e quella di tempo minimo di 40 giorni a fronte di un costo aggiuntivo di e 6780. attività p p d precedenza 1-posacantiere 2 2 2 - fondazioni 5 3 400 1 3 - struttura portante e solai 20 20 2 4 - muri 5 3 250 3 5 - tetto 7 5 300 3 6 - intonaci interni 4 2 350 4, 5, 10, 12, 14 7 - intonaci esterni 4 2 350 4, 5 8 - pittura interna 5 2 180 6, 16 9 - pittura esterna 4 2 200 7 10 - tracce impianto elettrico 4 2 150 4 11 - impianto elettrico 5 2 240 8 12 - telai serramenti 1 1 4 13 - serramenti 3 1 350 8 14 - tracce impianto idraulico 3 1 170 4 15 - sanitari 2 1 230 8, 14 16 - piastrelle 7 3 250 6 17 - pavimenti in legno 5 2 330 13 18 - consegna 0 0 11, 17 Tabella 5.1. Dati Gli ottimi di Pareto per i due obiettivi di tempo e costo minimo sono evidenziati in Fig. 5.3. Si tratta di una linea costituita da segmenti. Ogni segmento corrisponde a ottimi ottenuti variando il vincolo sul tempo finale e tutti relativi alla stessa base. Per questi valori la variabile duale del vincolo sul tempo è esattamente la pendenza del segmento e fornisce direttamente il costo di un ulteriore riduzione di un giorno. Si noti che la funzione che definisce il costo minimo in funzione della durata è necessariamente convessa e quindi una riduzione temporale è tanto più costosa quanto minore è il tempo finale.
5.5 Pianificazione di attività: risoluzione del secondo modello 89 attività s 0 c 0 s 1 c 1 x 1 dx 1 1 0 2 0 2 2 2 7 2 5 2 800 3 7 27 5 25 4 27 32 25 28 2 500 5 27 34 25 30 2 600 6 36 40 30 32 2 700 7 34 38 30 34 0 0 8 47 52 35 37 3 540 9 38 42 34 38 0 0 10 32 36 28 30 2 300 11 52 57 37 40 2 480 12 32 33 28 29 13 52 55 37 38 2 700 14 32 35 28 30 1 170 15 52 54 37 39 0 0 16 40 47 32 35 4 1000 17 55 60 38 40 3 990 18 60 60 40 40 Tabella 5.2. Soluzioni 6000 4000 2000 40 45 50 55 60 Figura 5.3. Ottimi di Pareto