import org.ssclab.log.SscLogger;
import org.ssclab.pl.milp.MILP;
import org.ssclab.pl.milp.Solution;
import org.ssclab.pl.milp.SolutionType;
import org.ssclab.pl.milp.Variable;
public class CommessoViaggiatore {
public static void main(String[] args) throws Exception {
//matrice dei costi
double cij[][] =
{ { 0, 10, 11, 15, 23, 0, 0, 33, 5, 0 },
{ 11, 0, 10, 0, 17, 18, 0, 29, 3, 0 },
{ 13, 10, 0, 8, 12, 0, 0, 18, 0, 0 },
{ 15, 0, 12, 0, 0, 0, 0, 15, 0, 0 },
{ 25, 17, 12, 0, 0, 5, 5, 15, 0, 5 },
{ 0, 18, 0, 0, 5, 0, 6, 0,20, 0 },
{ 0, 0, 0, 0, 5, 6, 0, 20, 0, 6 },
{ 33, 29, 18, 15, 15, 0, 20, 0, 0, 9 },
{ 8, 3, 0, 0, 0, 20, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 5, 0, 6, 7, 0, 0 }};
char ispettori[] = { 'A', 'B' };
int numero_tratte=5;
// definizione funzione obiettivo.
String fo = "min:";
for (int d = 0; d < ispettori.length; d++) {
for (int t = 0; t < numero_tratte; t++) {
for (int i = 0; i < cij.length; i++) {
for (int j = 0; j < cij[0].length; j++) {
if (cij[i][j] != 0) {
fo += "+" + cij[i][j] + "X" + ispettori[d] + (i + 1) + "" + (j + 1) + "" + (t + 1);
}
}
}
}
}
SscLogger.log(fo);
// Vincoli1: gli ispettori A partono dalla citta 1 durante il tratto 1
String vincoli1 = "\n";
for (int j = 0; j < cij[0].length; j++) {
if (cij[0][j] != 0) {
vincoli1 += " +XA1" + (j + 1) + "1";
}
}
vincoli1 += " =1\n";
SscLogger.log(vincoli1);
// Vincoli2: gli ispettori A ritornano alla citta 1 durante il tratto 5
String vincoli2 = "";
for (int i = 0; i < cij.length; i++) {
if (cij[i][0] != 0) {
vincoli2 += " +XA" + (i + 1) + "15";
}
}
vincoli2 += " =1\n";
SscLogger.log(vincoli2);
// Vincoli3: gli ispettori B partono dalla citta 5 durante il tratto 1
String vincoli3 = "";
for (int j = 0; j < cij[4].length; j++) {
if (cij[4][j] != 0) {
vincoli3 += " +XB5" + (j + 1) + "1";
}
}
vincoli3 += " =1\n";
SscLogger.log(vincoli3);
// Vincoli4: gli ispettore B ritornano alla citta 5 durante il tratto 5
String vincoli4 = "";
for (int i = 0; i < cij.length; i++) {
if (cij[i][4] != 0) {
vincoli4 += " +XB" + (i + 1) + "55";
}
}
vincoli4 += " =1\n";
SscLogger.log(vincoli4);
// Vincoli5: ogni gruppo deve visitare una citta una sola volta.
String vincoli5 = "";
for (int j = 0; j < cij[0].length; j++) {
for (int d = 0; d < ispettori.length; d++) {
for (int t = 0; t < numero_tratte; t++) {
for (int i = 0; i < cij.length; i++) {
if (cij[i][j] != 0) {
vincoli5 += " +X" + ispettori[d] + (i + 1) + "" + (j + 1) + "" + (t + 1);
}
}
}
}
vincoli5 += " =1\n";
}
SscLogger.log(vincoli5);
// Vincoli6: ciascun gruppo deve effettuare ciascun tratto solo una volta
String vincoli6 = "";
for (int d = 0; d < ispettori.length; d++) {
for (int t = 0; t < numero_tratte; t++) {
for (int i = 0; i < cij.length; i++) {
for (int j = 0; j < cij[0].length; j++) {
if (cij[i][j] != 0) {
vincoli6 += " +X" + ispettori[d] + (i + 1) + "" + (j + 1) + "" + (t + 1);
}
}
}
vincoli6 += " =1\n";
}
}
SscLogger.log(vincoli6);
// Vincoli7: per ciascun gruppo la localita di arrivo nel tratto t
// deve conincidere con quella di partenza nel tratto t+1
String vincoli7 = "";
for (int d = 0; d < ispettori.length; d++) {
for (int t = 0; t < numero_tratte-1; t++) {
for (int k = 0; k < cij.length; k++) {
for (int i = 0; i < cij.length; i++) {
if (cij[i][k] != 0) {
vincoli7 += " +X" + ispettori[d] + (i + 1) + "" + (k + 1) + "" + (t + 1);
}
}
vincoli7+= " =";
for (int j = 0; j < cij[0].length; j++) {
if (cij[k][j] != 0) {
vincoli7 += " +X" + ispettori[d] + (k + 1) + "" + (j + 1) + "" + (t + 2);
}
}
vincoli7 += "\n";
}
vincoli7 += "\n";
}
}
SscLogger.log(vincoli7);
String pl_problem = fo + vincoli1 + vincoli2 + vincoli3 + vincoli4 + vincoli5 +
vincoli6 + vincoli7 + "\n bin ALL";
MILP milp = new MILP(pl_problem);
SolutionType solution_type = milp.resolve();
if (solution_type == SolutionType.OPTIMUM) {
Solution soluzione = milp.getSolution();
for (Variable var : soluzione.getVariables()) {
if(var.getValue()!=0) SscLogger.log("Variable name :" + var.getName() + " value :" + var.getValue());
}
SscLogger.log("valore f.o. :" + soluzione.getOptimumValue());
}
else SscLogger.log("Soluzione non ottima:" + solution_type);
}
}
Per il trasporto dei rifiuti l'azienda chimica dispone esclusivamente di due tipologie di automezzi (\(A_1,A_2 => A_k\)); queste due tipologie di automezzi presentano una portata massima di trasporto per singolo viaggio (\(q_1,q_2 => q_k \)), un limite massimo di km giornalmente percorribili per singolo automezzo (\(km_1,km_2 => km_k\)) ed un costo complessivo di trasporto al km (\(ct_1,ct_2 => ct_k \)), riassumendo :
Le distanze (\(d_{ij}\)) in km tra i siti produttivi e quelli di smaltimento sono le seguenti :
Nella data presa in esame, le tre discariche forniscono all'azienda chimica le seguenti quantità di rifiuti da trattare, accompagnate dai relativi costi di smaltimento (\(cs_1,cs_2,cs_3 => cs_j\)) per tonnellata:
Nella data presa in esame i due siti produttivi presentano le seguenti quantita di rifiuti da smaltire :
L'azienda chimica dispone di due tipologie di automezzi per il trasporto (\( A_1,A_2 => A_k \) ); più precisamente il sito produttivo \( P_1 \) dispone di un mezzo di tipo \( A_1\) e due mezzi di tipo \( A_2 \) , mentre il sito produttivo \( P_2 \) dispone di due mezzi di tipo \( A_1\) ed un mezzo di tipo \( A_2 \). Per cui considerando il limite di km giornalieri abbiamo complesivamente le seguenti quantità di km effettuabili dai due siti in base al numero di automezzi disponibili:
Indichiamo con \( X_{kij}\) il numero di viaggi che un automezzo di tipo \( A_k\) compie dal sito di produzione \( P_i\) alla discarica \( D_j\). Precisiamo che dal punto di vista del modello ogni viaggio comprende anche il ritorno alla sede di partenza. Le variabili \( X_{kij}\) sono variabili intere non negative. Effettuate queste premesse abbiamo in base alle limitazioni introdotte i seguenti vincoli.
Vincoli di percorrenza per gli automezzi di tipo \( A_1\) e \( A_2\) che partono da \( P_1\) (si moltiplica per due per il viaggio di ritorno):
\[ 2*\sum_{j=1}^{3} X_{11j}*d_{1j} \leq 500\] \[ 2*\sum_{j=1}^{3} X_{21j}*d_{1j} \leq 450*2\]
Vincoli di percorrenza per gli automezzi di tipo \( A_1\) e \( A_2\) che partono da \( P_2\) (si moltiplica per due per il viaggio di ritorno):
\[ 2*\sum_{j=1}^{3} X_{12j}*d_{2j} \leq 500*2\] \[ 2*\sum_{j=1}^{3} X_{22j}*d_{2j} \leq 450\]
Vincoli capacità di smaltimento in tonnellate delle tre discariche \( D_1,D_2,D_3\) :
\[ \sum_{k=1}^{2}\sum_{i=1}^{2} X_{ki1}*q_k \leq 147\] \[ \sum_{k=1}^{2}\sum_{i=1}^{2} X_{ki2}*q_k \leq 115\] \[ \sum_{k=1}^{2}\sum_{i=1}^{2} X_{ki3}*q_k \leq 95\]
Vincoli legati alla produzione di rifiuti; le quantità trasportate e smaltite non possono essere maggiori delle quantita da smaltire presenti nei due siti produttivi :
\[ \sum_{k=1}^{2}\sum_{j=1}^{3} X_{k1j}*q_k \leq 185\] \[ \sum_{k=1}^{2}\sum_{j=1}^{3} X_{k2j}*q_k \leq 210\]
Ricordiamo che uno degli obiettivi e anche quello di minimizzare la quantita di rifiuti non smaltita. Chiamiamo con \( Y_i\) la quantita residua non smaltita dal sito \( P_i\), allora i precedenti vincoli diventano :
\[ \sum_{k=1}^{2}\sum_{j=1}^{3} X_{k1j}*q_k + Y_1 = 185\] \[ \sum_{k=1}^{2}\sum_{j=1}^{3} X_{k2j}*q_k + Y_2 = 210\]
I costi di trasporto risultano essere (si moltiplica per due in quanto i tragitti \( X_{kij}\) comprendono anche il ritorno alla sede di partenza): \[ 2*\sum_{k=1}^{2}\sum_{i=1}^{2}\sum_{j=1}^{3} X_{kij}*d_{ij} *ct_k \]
I costi di smaltimento risultano essere : \[ \sum_{k=1}^{2}\sum_{i=1}^{2}\sum_{j=1}^{3} X_{kij}*q_k*cs_j \]
Un altro costo risulta essere quello di tenere in giacenza rifiuti non smaltiti, per cui la funzione obiettivo deve anche cercare di minimizzare il quantitativo di rifiuti non smaltiti \( Y_i\). Per fare ciò assegnamo nella funzione obiettivo alle variabili \( Y_i\) un coefficente (penalità) \( M\) molto elevato; in definitiva la funzione obiettivo finale da minimizzare risulta: \[ min : 2*\sum_{k=1}^{2}\sum_{i=1}^{2}\sum_{j=1}^{3} X_{kij}*d_{ij} *ct_k + \sum_{k=1}^{2}\sum_{i=1}^{2}\sum_{j=1}^{3} X_{kij}*q_k*cs_j + \sum_{i=1}^{2}Y_i*M \]
Sostituendo nei vincoli e nella funzione obiettivo i valori reali otteniamo:
\[ min: 3250*X_{111} + 3680*X_{112}+ 3340*X_{113} + 3700*X_{121} + 3905*X_{122}+ 3190*X_{123} +\] \[ 1180*X_{211} + 1340*X_{212}+ 1225*X_{213} + 1360*X_{221} + 1430*X_{222}+ 1165*X_{223} +100000*Y_1 + 100000*Y_2 \]
\[ 60* X_{111} +80* X_{112}+ 100*X_{113} \leq 500 \] \[ 60* X_{211} +80* X_{212}+ 100*X_{213} \leq 900 \] \[ 120* X_{121} +110* X_{122}+ 80*X_{123} \leq 1000 \] \[ 120* X_{221} +110* X_{222}+ 80*X_{223} \leq 450 \] \[ 14*X_{111} + 14*X_{121} + 5*X_{211} + 5*X_{221} \leq 147 \] \[ 14*X_{112} + 14*X_{122} + 5*X_{212} + 5*X_{222} \leq 115 \] \[ 14*X_{113} + 14*X_{123} + 5*X_{213} + 5*X_{223} \leq 95 \] \[ 14*X_{111} + 14*X_{112}+ 14*X_{113} + 5*X_{211} + 5*X_{212}+ 5*X_{213} +Y_1 = 185 \] \[ 14*X_{121} + 14*X_{122}+ 14*X_{123} + 5*X_{221} + 5*X_{222}+ 5*X_{223} +Y_2 = 210 \]
\[ X_{kij} \in \mathbb{Z} \,\,e\,\, X_{kij} \ge 0 , \forall k ,\forall i,\forall j \]
Risolvendo il problema SSC sotto riportato otteniamo la seguente soluzione ottima : \[ X_{111}=8 , X_{122}=5 , X_{123} = 5 ,X_{211} = 7 , X_{212} = 6 , X_{223} = 5 , Y_1 = 8.0 , Y_2 = 45 \]
con un costo complessivo di : 83600 Euro.