Previously, we talked about Cplex's direct solution model for VRPTW, and now we call Cplex in the branch and bound algorithm to solve VRPTW.
1. Branch and Bound Algorithms
(1) Definition:
(2) Solving process:
1) Determine a lower bound (initial solution LB) defined as infinite UB
2) Build a node to join the priority queue for the initial problem
3) Determine if the queue is empty, if it is empty jump to 7, otherwise take out and eject the first element of the queue and calculate the target value of the node P
4) If P > UB, return 3.Otherwise, determine if the current node is a legal solution (for any i, j, k, x_i J K are integers), if so, jump 5 or jump 6
5) If P < UB, record UB = P, the current node is the current best solution BS.Return 3
6) Set up two child nodes L, R.L, R is created as above, if L's target value is L.P <= UB, L is queued, and if R's target value is R.P <= UB, R is queued.Return 3
7) End, return the optimal node BS of the record.No solution if BS is empty
2. Branch and Bound Solving VRPTW
The code structure is as follows
The purpose of a Data class is to define variables, initialize variables, and read data.
package com.chb; import java.io.BufferedReader; import java.io.FileReader; import java.util.Scanner; //Define parameters class Data{ public static final double gap= 1e-6; public static final double big_num = 100000; public static int pointnum=102; //All point sets n (including distribution centers and customer points, starting and ending (0 and n) as distribution centers) public static double E; //Distribution Center Time Window Start Time public static double L; //Distribution Center Time Window End Time public static int carnum; //Number of vehicles public static double cap; //Vehicle Load public static int[][] point=new int[pointnum][2];; //Coordinates x,y for all points public static int[] demand=new int[pointnum]; //Requirement public static int[] car=new int[carnum]; //Vehicle number public static double[] a=new double[pointnum]; //Time window start time [a[i],b[i]] public static double[] b=new double[pointnum]; //Time window end time [a[i],b[i]] public static double[] s=new double[pointnum]; //Service time at customer point public static int[][] arcs=new int[pointnum][pointnum]; //arcs[i][j] represents an arc from I to j public static double[][] dist=new double[pointnum][pointnum]; //Distance matrix, satisfies trigonometric relationships, temporary distance means cost C[i][j]=dist[i][j] //Truncate Decimal 3.26434-->3.2 public double double_truncate(double v){ int iv = (int) v; if(iv+1 - v <= gap) return iv+1; double dv = (v - iv) * 10; int idv = (int) dv; double rv = iv + idv / 10.0; return rv; } public Data() { super(); } //Function function: Read data from txt file and initialize parameters public void read_data(String path,Data data) throws Exception{ String line = null; String[] substr = null; Scanner cin = new Scanner(new BufferedReader(new FileReader(path))); //read file //Read 1-4 lines for(int i =0; i < 4;i++){ line = cin.nextLine(); } //Read 5 lines line = cin.nextLine(); line.trim(); substr = line.split(("\\s+")); carnum = Integer.parseInt(substr[1]); cap = Integer.parseInt(substr[2]); //Read 6-9 lines for(int i =0; i < 4;i++){ line = cin.nextLine(); } //Read pointnum-1 row data for (int i = 0; i < pointnum - 1; i++) { line = cin.nextLine(); line.trim(); substr = line.split("\\s+"); point[i][0] = Integer.parseInt(substr[2]); point[i][1] = Integer.parseInt(substr[3]); demand[i] = Integer.parseInt(substr[4]); a[i] = Integer.parseInt(substr[5]); b[i] = Integer.parseInt(substr[6]); s[i] = Integer.parseInt(substr[7]); } cin.close();//Close Stream //Initialize endpoint parameters point[pointnum-1] = point[0]; demand[pointnum-1] = 0; a[pointnum-1] = a[0]; b[pointnum-1] = b[0]; E = a[0]; L = b[0]; s[pointnum-1] = 0; double min1 = 1e15; double min2 = 1e15; //Distance Matrix Initialization for (int i = 0; i < pointnum; i++) { for (int j = 0; j < pointnum; j++) { if (i == j) { dist[i][j] = 0; continue; } dist[i][j] = Math.sqrt(Math.pow(point[i][0]-point[j][0], 2)+Math.pow(point[i][1]-point[j][1], 2)); dist[i][j]=double_truncate(dist[i][j]); } } dist[0][pointnum-1] = 0; dist[pointnum-1][0] = 0; //Distance Matrix Satisfies Triangular Relation for (int k = 0; k < pointnum; k++) { for (int i = 0; i < pointnum; i++) { for (int j = 0; j < pointnum; j++) { if (dist[i][j] > dist[i][k] + dist[k][j]) { dist[i][j] = dist[i][k] + dist[k][j]; } } } } //Initialize to Full Graph for (int i = 0; i < pointnum; i++) { for (int j = 0; j < pointnum; j++) { if (i != j) { arcs[i][j] = 1; } else { arcs[i][j] = 0; } } } //Remove edges that do not meet time window and capacity constraints for (int i = 0; i < pointnum; i++) { for (int j = 0; j < pointnum; j++) { if (i == j) { continue; } if (a[i]+s[i]+dist[i][j]>b[j] || demand[i]+demand[j]>cap) { arcs[i][j] = 0; } if (a[0]+s[i]+dist[0][i]+dist[i][pointnum-1]> b[pointnum-1]) { System.out.println("the calculating example is false"); } } } for (int i = 1; i < pointnum-1; i++) { if (b[i] - dist[0][i] < min1) { min1 = b[i] - dist[0][i]; } if (a[i] + s[i] + dist[i][pointnum-1] < min2) { min2 = a[i] + s[i] + dist[i][pointnum-1]; } } if (E > min1 || L < min2) { System.out.println("Duration false!"); System.exit(0);//Terminate program } //Initialize parameters for 0, n+1 points of the distribution center arcs[pointnum-1][0] = 0; arcs[0][pointnum-1] = 1; for (int i = 1; i < pointnum-1; i++) { arcs[pointnum-1][i] = 0; } for (int i = 1; i < pointnum-1; i++) { arcs[i][0] = 0; } } }
The primary purpose of the Node class is to record branch nodes
package com.chb; import java.util.ArrayList; public class Node implements Comparable{ Data data; int d; double node_cost; //Target value object double[][][]lp_x;//Record lp solution int[][][] node_x_map;//When node_xij=1, node_x_mapijk=1 means it must be accessed, and node_x_mapijk=0 means it cannot be accessed int[][] node_x;//0 means the arc is accessible, 1 means it must be accessed, and -1 means it cannot be accessed ArrayList<ArrayList<Integer>> node_routes; //Define Vehicle Route Chain List ArrayList<ArrayList<Double>> node_servetimes; //Define a time-consuming list public Node(Data data) { super(); this.data = data; node_cost = data.big_num; lp_x = new double [data.pointnum][data.pointnum][data.carnum]; node_x_map = new int[data.pointnum][data.pointnum][data.carnum]; node_x = new int[data.pointnum][data.pointnum]; node_routes = new ArrayList<ArrayList<Integer>>(); node_servetimes = new ArrayList<ArrayList<Double>>(); } //Duplicate node @SuppressWarnings("unchecked") public Node note_copy() { Node new_node = new Node(data); new_node.d = d; new_node.node_cost = node_cost; for (int i = 0; i < lp_x.length; i++) { for (int j = 0; j < lp_x[i].length; j++) { new_node.lp_x[i][j] = lp_x[i][j].clone(); } } for (int i = 0; i < node_x.length; i++) { new_node.node_x[i] = node_x[i].clone(); } for (int i = 0; i < node_x_map.length; i++) { for (int j = 0; j < node_x_map[i].length; j++) { new_node.node_x_map[i][j] = node_x_map[i][j].clone(); } } for (int i = 0; i < node_routes.size(); i++) { new_node.node_routes.add((ArrayList<Integer>) node_routes.get(i).clone()); } for (int i = 0; i < node_servetimes.size(); i++) { new_node.node_servetimes.add((ArrayList<Double>) node_servetimes.get(i).clone()); } return new_node; } public int compareTo(Object o){ Node node = (Node) o; if(node_cost < node.node_cost) return -1; else if(node_cost == node.node_cost) return 0; else return 1; } }
The BaB_Vrptw class is the main function of the program, where
The function init() is used to determine the minimum number of vehicles with a legal solution by building a relaxed cplex model and calculating the number of vehicles used. If aa vehicles are not used, reduce aa available vehicles; otherwise, reduce one until there is no feasible solution.Of course, the fewest vehicles are available in the end, and the relaxed model is one that removes the x_ijk integer constraint from the previous model
branch and bound process: By relaxing the mathematical model of VRPTW into a linear programming problem, a lower bound of the VRPTW problem can be solved. The principle of branching is that for a selected x_ijk with 0<x_ijk<1, then the x_ijk can be divided into two branches, the left branch cannot walk the arc ij, the right branch must walk the arc ij and mustVehicle k must pass by.That is, the left branch has x_ijt = 0 for any t.On the right is x_ijk = 1, and the arc to branch is found by the find_arc() function
package com.chb; import java.util.ArrayList; import java.util.PriorityQueue; import ilog.concert.IloException; import ilog.concert.IloNumExpr; import ilog.concert.IloNumVar; import ilog.concert.IloNumVarType; import ilog.cplex.IloCplex; //Class functions: building models and solving public class BaB_Vrptw { Data data; //Object defining class Data Node node1; Node node2; int deep;//depth public PriorityQueue<Node> queue;//Branch Queue Node best_note;//Current best branch double cur_best;//Best solution int []record_arc;//Record nodes that need branching double x_gap;//Very small number IloCplex model; //Object defining cplex internal class public IloNumVar[][][] x; //x[i][j][k] means that arcs[i][j] are accessed by vehicle K public IloNumVar[][] w; //Time Matrix for Vehicle Access to All Points double cost; //Target value object double[][][] x_map;//cplex parameter x ArrayList<ArrayList<Integer>> routes; //Define Vehicle Route Chain List ArrayList<ArrayList<Double>> servetimes; //Define a time-consuming list public BaB_Vrptw(Data data) { this.data = data; x_gap = data.gap; routes = new ArrayList<>(); //Define Vehicle Route Chain List servetimes = new ArrayList<>(); //Define a time-consuming list //Initialize vehicle routing and time-consuming chain table with length k of vehicles for (int k = 0; k < data.carnum; k++) { ArrayList<Integer> r = new ArrayList<>(); ArrayList<Double> t = new ArrayList<>(); routes.add(r); servetimes.add(t); } x_map = new double[data.pointnum][data.pointnum][data.carnum]; } public void clear_lp() { data=null; routes.clear(); servetimes.clear(); x_map=null; } //Helper lp Solve to node @SuppressWarnings("unchecked") public void copy_lp_to_node(BaB_Vrptw lp, Node node) { node.node_routes.clear(); node.node_servetimes.clear(); node.node_cost = lp.cost; for (int i = 0; i < lp.x_map.length; i++) { for (int j = 0; j < lp.x_map[i].length; j++) { node.lp_x[i][j] = lp.x_map[i][j].clone(); } } for (int i = 0; i < lp.routes.size(); i++) { node.node_routes.add((ArrayList<Integer>) lp.routes.get(i).clone()); } for (int i = 0; i < lp.servetimes.size(); i++) { node.node_servetimes.add((ArrayList<Double>) lp.servetimes.get(i).clone()); } } //Function function: Establish the cplex model of VRPTW according to the mathematical model of VRPTW //Modeling private void build_model() throws IloException { //model model = new IloCplex(); model.setOut(null); // model.setParam(IloCplex.DoubleParam.EpOpt, 1e-9); // model.setParam(IloCplex.DoubleParam.EpGap, 1e-9); //variables x = new IloNumVar[data.pointnum][data.pointnum][data.carnum]; w = new IloNumVar[data.pointnum][data.carnum]; //Time of Vehicle Access Point //Define the data type and range of cplex variables x and w for (int i = 0; i < data.pointnum; i++) { for (int k = 0; k < data.carnum; k++) { w[i][k] = model.numVar(0, 1e15, IloNumVarType.Float, "w" + i + "," + k); } for (int j = 0; j < data.pointnum; j++) { if (data.arcs[i][j]==0) { x[i][j] = null; } else{ //Xijk, Formula (10) -(11) for (int k = 0; k < data.carnum; k++) { x[i][j][k] = model.numVar(0, 1, IloNumVarType.Float, "x" + i + "," + j + "," + k); } } } } //Join objective function //Formula (1) IloNumExpr obj = model.numExpr(); for(int i = 0; i < data.pointnum; i++){ for(int j = 0; j < data.pointnum; j++){ if (data.arcs[i][j]==0) { continue; } for(int k = 0; k < data.carnum; k++){ obj = model.sum(obj, model.prod(data.dist[i][j], x[i][j][k])); } } } model.addMinimize(obj); //Join Constraint //Formula (2) for(int i= 1; i < data.pointnum-1;i++){ IloNumExpr expr1 = model.numExpr(); for (int k = 0; k < data.carnum; k++) { for (int j = 1; j < data.pointnum; j++) { if (data.arcs[i][j]==1) { expr1 = model.sum(expr1, x[i][j][k]); } } } model.addEq(expr1, 1); } //Formula (3) for (int k = 0; k < data.carnum; k++) { IloNumExpr expr2 = model.numExpr(); for (int j = 1; j < data.pointnum; j++) { if (data.arcs[0][j]==1) { expr2 = model.sum(expr2, x[0][j][k]); } } model.addEq(expr2, 1); } //Formula (4) for (int k = 0; k < data.carnum; k++) { for (int j = 1; j < data.pointnum-1; j++) { IloNumExpr expr3 = model.numExpr(); IloNumExpr subExpr1 = model.numExpr(); IloNumExpr subExpr2 = model.numExpr(); for (int i = 0; i < data.pointnum; i++) { if (data.arcs[i][j]==1) { subExpr1 = model.sum(subExpr1,x[i][j][k]); } if (data.arcs[j][i]==1) { subExpr2 = model.sum(subExpr2,x[j][i][k]); } } expr3 = model.sum(subExpr1,model.prod(-1, subExpr2)); model.addEq(expr3, 0); } } //Formula (5) for (int k = 0; k < data.carnum; k++) { IloNumExpr expr4 = model.numExpr(); for (int i = 0; i < data.pointnum-1; i++) { if (data.arcs[i][data.pointnum-1]==1) { expr4 = model.sum(expr4,x[i][data.pointnum-1][k]); } } model.addEq(expr4, 1); } //Formula (6) double M = 1e5; for (int k = 0; k < data.carnum; k++) { for (int i = 0; i < data.pointnum; i++) { for (int j = 0; j < data.pointnum; j++) { if (data.arcs[i][j] == 1) { IloNumExpr expr5 = model.numExpr(); IloNumExpr expr6 = model.numExpr(); expr5 = model.sum(w[i][k], data.s[i]+data.dist[i][j]); expr5 = model.sum(expr5,model.prod(-1, w[j][k])); expr6 = model.prod(M,model.sum(1,model.prod(-1, x[i][j][k]))); model.addLe(expr5, expr6); } } } } //Formula (7) for (int k = 0; k < data.carnum; k++) { for (int i = 1; i < data.pointnum-1; i++) { IloNumExpr expr7 = model.numExpr(); for (int j = 0; j < data.pointnum; j++) { if (data.arcs[i][j] == 1) { expr7 = model.sum(expr7,x[i][j][k]); } } model.addLe(model.prod(data.a[i], expr7), w[i][k]); model.addLe(w[i][k], model.prod(data.b[i], expr7)); } } //Formula (8) for (int k = 0; k < data.carnum; k++) { model.addLe(data.E, w[0][k]); model.addLe(data.E, w[data.pointnum-1][k]); model.addLe(w[0][k], data.L); model.addLe(w[data.pointnum-1][k], data.L); } //Formula (9) for (int k = 0; k < data.carnum; k++) { IloNumExpr expr8 = model.numExpr(); for (int i = 1; i < data.pointnum-1; i++) { IloNumExpr expr9 = model.numExpr(); for (int j = 0; j < data.pointnum; j++) { if (data.arcs[i][j] == 1) { expr9=model.sum(expr9,x[i][j][k]); } } expr8 = model.sum(expr8,model.prod(data.demand[i],expr9)); } model.addLe(expr8, data.cap); } } //Functional functions: Solve the model, generate vehicle routes, and get target values //Get cplex solution public void get_value() throws IloException { routes.clear(); servetimes.clear(); cost = 0; // //Initialize vehicle routing and time-consuming chains, length of which is number of vehicles k for (int k = 0; k < data.carnum; k++) { ArrayList<Integer> r = new ArrayList<>(); ArrayList<Double> t = new ArrayList<>(); routes.add(r); servetimes.add(t); } for (int i = 0; i < data.pointnum; i++) { for (int j = 0; j < data.pointnum; j++) { for (int k = 0; k < data.carnum; k++) { x_map[i][j][k] = 0.0; } if (data.arcs[i][j]>0.5) { for (int k = 0; k < data.carnum; k++) { x_map[i][j][k]=model.getValue(x[i][j][k]); } } } } //Model solvable to generate vehicle routes for(int k = 0; k < data.carnum; k++){ boolean terminate = true; int i = 0; routes.get(k).add(0); servetimes.get(k).add(0.0); while(terminate){ for (int j = 0; j < data.pointnum; j++) { if (doubleCompare(x_map[i][j][k], 0)==1) { routes.get(k).add(j); servetimes.get(k).add(model.getValue(w[j][k])); i = j; break; } } if (i == data.pointnum-1) { terminate = false; } } } cost = model.getObjValue(); } //branch and bound process public void branch_and_bound(BaB_Vrptw lp) throws IloException { cur_best = 3000;//Set Up Last deep=0; record_arc = new int[3]; node1 = new Node(data); best_note = null; queue = new PriorityQueue<Node>(); //Initial solution (illegal solution) for (int i = 0; i < lp.routes.size(); i++) { ArrayList<Integer> r = lp.routes.get(i); System.out.println(); for (int j = 0; j < r.size(); j++) { System.out.print(r.get(j)+" "); } } lp.copy_lp_to_node(lp, node1); // node1.node_cost = lp.cost; // node1.lp_x = lp.x_map.clone(); // node1.node_routes =lp.routes; // node1.node_servetimes = lp.servetimes; node2 = node1.note_copy(); deep=0; node1.d=deep; queue.add(node1); //branch and bound process while (!queue.isEmpty()) { Node node = queue.poll(); //A branch of optimal solution is larger than the current best feasible solution, delete if (doubleCompare(node.node_cost, cur_best)>0) { continue; }else { record_arc = lp.find_arc(node.lp_x); //The legal solution of a branch, the solution of 0,1 combination, the best solution for the current branch if (record_arc[0]==-1) { //Better than the current best solution, update the current solution if (doubleCompare(node.node_cost, cur_best)==-1) { lp.cur_best = node.node_cost; System.out.println(node.d+" cur_best:"+cur_best); lp.best_note = node; } continue; }else {//Can branch node1 = lp.branch_left_arc(lp, node, record_arc);//Left Branch node2 = lp.branch_right_arc(lp, node, record_arc);//Right Branch if (node1!=null && doubleCompare(node1.node_cost, cur_best)<=0) { queue.add(node1); } if (node2!=null && doubleCompare(node2.node_cost, cur_best)<=0) { queue.add(node2); } } } } } //Branch Settings public void set_bound(Node node) throws IloException { for (int i = 0; i < data.pointnum; i++) { for (int j = 0; j < data.pointnum; j++) { if (data.arcs[i][j]>0.5) { if (node.node_x[i][j]==0) { for (int k = 0; k < data.carnum; k++) { x[i][j][k].setLB(0.0); x[i][j][k].setUB(1.0); } }else if (node.node_x[i][j]==-1) { for (int k = 0; k < data.carnum; k++) { x[i][j][k].setLB(0.0); x[i][j][k].setUB(0.0); } }else { for (int k = 0; k < data.carnum; k++) { if (node.node_x_map[i][j][k]==1) { x[i][j][k].setLB(1.0); x[i][j][k].setUB(1.0); }else { x[i][j][k].setLB(0.0); x[i][j][k].setUB(0.0); } } } } } } } public void set_bound1(Node node) throws IloException { for (int i = 0; i < data.pointnum; i++) { for (int j = 0; j < data.pointnum; j++) { if (data.arcs[i][j]>0.5) { for (int k = 0; k < data.carnum; k++) { if (node.node_x_map[i][j][k]==0) { x[i][j][k].setLB(0.0); x[i][j][k].setUB(1.0); }else if (node.node_x_map[i][j][k]==-1) { x[i][j][k].setLB(0.0); x[i][j][k].setUB(0.0); }else { x[i][j][k].setLB(1.0); x[i][j][k].setUB(1.0); } } } } } } //Set Left Branch public Node branch_left_arc(BaB_Vrptw lp,Node father_node,int[] record) throws IloException { if (record[0] == -1) { return null; } Node new_node = new Node(data); new_node = father_node.note_copy(); new_node.node_x[record[0]][record[1]] = -1; for (int k = 0; k < data.carnum; k++) { new_node.node_x_map[record[0]][record[1]][k]=0; } // new_node.node_x_map[record[0]][record[1]][record[2]]=-1; //Set Left Branch lp.set_bound(new_node); if (lp.model.solve()) { lp.get_value(); deep++; new_node.d=deep; lp.copy_lp_to_node(lp, new_node); System.out.println(new_node.d+" "+lp.cost); }else { new_node.node_cost = data.big_num; } return new_node; } //Set Right Branch public Node branch_right_arc(BaB_Vrptw lp,Node father_node,int[] record) throws IloException { if (record[0] == -1) { return null; } Node new_node = new Node(data); new_node = father_node.note_copy(); new_node.node_x[record[0]][record[1]] = 1; // new_node.node_x_map[record[0]][record[1]][record[2]]=1; for (int k = 0; k < data.carnum; k++) { if (k==record[2]) { new_node.node_x_map[record[0]][record[1]][k]=1; }else { new_node.node_x_map[record[0]][record[1]][k]=0; } } //Set Right Branch lp.set_bound(new_node); if (lp.model.solve()) { lp.get_value(); deep++; new_node.d=deep; System.out.println(new_node.d+" right: "+lp.cost); lp.copy_lp_to_node(lp, new_node); }else { new_node.node_cost = data.big_num; } return new_node; } //Find pivot locations that need branching public int[] find_arc1(double[][][] x) { int record[] = new int[3];//Record branch vertices double cur_dif = 0; double min_dif = Double.MAX_VALUE; //Find the arc closest to 0.5 for (int i = 1; i <data.pointnum-1; i++) { for (int j = 1; j < data.pointnum-1; j++) { if (data.arcs[i][j]>0.5) { for (int k = 0; k <data.carnum; k++) { //If the arc value is 0 or 1, continue if (is_one_zero(x[i][j][k])) { continue; } cur_dif = get_dif(x[i][j][k]); if (doubleCompare(cur_dif, min_dif)==-1) { record[0] = i; record[1] = j; record[2] = k; min_dif = cur_dif; } } } } } //depot if (doubleCompare(min_dif, Double.MAX_VALUE)==0) { for (int i = 1; i < data.pointnum-1; i++) { if (data.arcs[0][i]>0.5) { for (int k = 0; k < data.carnum; k++) { if (is_fractional(x[0][i][k])) { cur_dif = get_dif(x[0][i][k]); if (doubleCompare(cur_dif, min_dif)==-1) { record[0] = 0; record[1] = i; record[2] = k; min_dif = cur_dif; } } } } if (data.arcs[i][data.pointnum-1]>0.5) { for (int k = 0; k < data.carnum; k++) { if (is_fractional(x[i][data.pointnum-1][k])) { cur_dif = get_dif(x[i][data.pointnum-1][k]); if (doubleCompare(cur_dif, min_dif)==-1) { record[0] = i; record[1] = data.pointnum-1; record[2] = k; min_dif = cur_dif; } } } } } } if (doubleCompare(min_dif, data.big_num)==1) { record[0] = -1; record[1] = -1; record[2] = -1; } return record; } // Find the arc to branch public int[] find_arc(double[][][] x) { int record[] = new int[3];//Record branch vertices for (int i = 0; i <data.pointnum; i++) { for (int j = 0; j < data.pointnum; j++) { if (data.arcs[i][j]>0.5) { for (int k = 0; k <data.carnum; k++) { //If the arc value is 0 or 1, continue if (is_one_zero(x[i][j][k])) { continue; } // cur_dif = get_dif(x[i][j][k]); record[0] = i; record[1] = j; record[2] = k; return record; } } } } record[0] = -1; record[1] = -1; record[2] = -1; return record; } //Compare the size of two double s public int doubleCompare(double a, double b){ if(a - b > x_gap) return 1; if(b - a > x_gap) return -1; return 0; } //Decimal number between 0 and 1 public boolean is_fractional(double v){ if( v > (int) v + x_gap && v < (int) v + 1 - x_gap) return true; else return false; } //Determine whether 0 or 1 public boolean is_one_zero(double temp) { if (doubleCompare(temp, 0)==0 || doubleCompare(temp, 1)==0) { return true; }else { return false; } } //Get a distance of 0.5 public double get_dif(double temp) { double v = (int)temp+0.5; if (v>temp) { return v-temp; } else { return temp-v; } } public BaB_Vrptw init(BaB_Vrptw lp) throws IloException { lp.build_model(); if (lp.model.solve()) { lp.get_value(); int aa=0; for (int i = 0; i < lp.routes.size(); i++) { if (lp.routes.get(i).size()==2) { aa++; } } System.out.println(aa); if (aa==0) { data.carnum -=1; lp.model.clearModel(); lp = new BaB_Vrptw(data); return init(lp); }else { data.carnum -=aa; lp.model.clearModel(); lp = new BaB_Vrptw(data); return init(lp); } }else { data.carnum +=1; System.out.println("vehicle number: "+data.carnum); lp.model.clearModel(); lp = new BaB_Vrptw(data); lp.build_model(); if (lp.model.solve()) { lp.get_value(); return lp; }else { System.out.println("error init"); return null; } } } public static void main(String[] args) throws Exception { Data data = new Data(); //Manually modify the vetexnum parameter before reading in different files. The parameter value is equal to the number of all points, including the distribution center String path = "data/c102.txt";//Example address data.read_data(path,data); System.out.println("input succesfully"); System.out.println("cplex procedure###########################"); BaB_Vrptw lp = new BaB_Vrptw(data); double cplex_time1 = System.nanoTime(); //Delete unused vehicles to reduce solution space lp=lp.init(lp); System.out.println(": "+lp.data.carnum); lp.branch_and_bound(lp); Check check = new Check(lp); check.fesible(); double cplex_time2 = System.nanoTime(); double cplex_time = (cplex_time2 - cplex_time1) / 1e9;//Solution time, units s System.out.println("cplex_time " + cplex_time + " bestcost " + lp.cur_best); for (int i = 0; i < lp.best_note.node_routes.size(); i++) { ArrayList<Integer> r = lp.best_note.node_routes.get(i); System.out.println(); for (int j = 0; j < r.size(); j++) { System.out.print(r.get(j)+" "); } } } }
The purpose of the Check class is to check the feasibility of the solution, including whether it satisfies the number of vehicles constraint, capacity constraint, time window constraint, and so on.
package com.chb; import java.util.ArrayList; import ilog.concert.IloException; //Class Functions: Feasibility Judgment of Solutions (can be skipped directly) class Check{ double epsilon = 0.0001; Data data = new Data(); ArrayList<ArrayList<Integer>> routes = new ArrayList<>(); ArrayList<ArrayList<Double>> servetimes = new ArrayList<>(); public Check(BaB_Vrptw lp) { super(); this.data = lp.data; this.routes = lp.routes; this.servetimes = lp.servetimes; } //Function function: Compare the size of two numbers public int double_compare(double v1,double v2) { if (v1 < v2 - epsilon) { return -1; } if (v1 > v2 + epsilon) { return 1; } return 0; } //Functional Functions: Feasibility of Solutions public void fesible() throws IloException { //Feasibility Judgment of Vehicle Quantity if (routes.size() > data.carnum) { System.out.println("error: vecnum!!!"); System.exit(0); } //Feasibility Judgment of Vehicle Load for (int k = 0; k < routes.size(); k++) { ArrayList<Integer> route = routes.get(k); double capasity = 0; for (int i = 0; i < route.size(); i++) { capasity += data.demand[route.get(i)]; } if (capasity > data.cap) { System.out.println("error: cap!!!"); System.exit(0); } } //Feasibility Judgment of Time Window and Vehicle Capacity for (int k = 0; k < routes.size(); k++) { ArrayList<Integer> route = routes.get(k); ArrayList<Double> servertime = servetimes.get(k); double capasity = 0; for (int i = 0; i < route.size()-1; i++) { int origin = route.get(i); int destination = route.get(i+1); double si = servertime.get(i); double sj = servertime.get(i+1); if (si < data.a[origin] && si > data.b[origin]) { System.out.println("error: servertime!"); System.exit(0); } if (double_compare(si + data.dist[origin][destination],data.b[destination]) > 0) { System.out.println(origin + ": [" + data.a[origin] + ","+data.b[origin]+"]"+ " "+ si); System.out.println(destination + ": [" + data.a[destination] + ","+data.b[destination]+"]"+ " "+ sj); System.out.println(data.dist[origin][destination]); System.out.println(destination + ":" ); System.out.println("error: forward servertime!"); System.exit(0); } if (double_compare(sj - data.dist[origin][destination],data.a[origin]) < 0) { System.out.println(origin + ": [" + data.a[origin] + ","+data.b[origin]+"]"+ " "+ si); System.out.println(destination + ": [" + data.a[destination] + ","+data.b[destination]+"]"+ " "+ sj); System.out.println(data.dist[origin][destination]); System.out.println(destination + ":" ); System.out.println("error: backward servertime!"); System.exit(0); } } if (capasity > data.cap) { System.out.println("error: cap!!!"); System.exit(0); } } } }
Run result:
Note: Text is extracted and reproduced from
https://mp.weixin.qq.com/s?__biz=MzU0NzgyMjgwNg==&mid=2247484626&idx=1&sn=fa7ab4ea6e02084f4e8ed52c6c203eda&chksm=fb49c96bcc3e407db8c7bb9433dfb1fa787518328922bf061a4421c46557089b3429c9aeb391&mpshare=1&scene=1&srcid=0819ACkwrk90AoXHmuTfOewW&sharer_sharetime=1566189897645&sharer_shareid=054592193644de509623829748e83807&key=39e8dcac62579260153e141217c0c82d2c6e85d9ab908f37737e7a53942622438640915d71ffff9fcb2a13fe7ce9bddfcdfbd31c41de4e97aa05544e971a3b9da9c1048280222095be4e5c215e240a6b&ascene=1&uin=MjYzMDA1MzAyMQ%3D%3D&devicetype=Windows+10&version=62060834&lang=zh_CN&pass_ticket=XkzfvjtynhPBmAjeYlUVZf95yJsB8gJ7VNbzHQQ9yaInrn1wKsdFjIxND%2FyV40nF