机器调度问题(JSP问题)描述为:在给定每个工件的加工流程、每个工件使用机器的序列及每个工件每道工序的加工时间确定的情况下,安排工件的加工顺序,使得待加工的工件在机器上进行加工的最大完工时刻最小。
接着上次的JSP模型( ),我们给出一个具体的问题,并用java调用cplex进行求解。
问题如下:一个有3个待加工工件和4台加工机器的模型。每个工件都必须由相对应的机器按照给定的顺序进行处理,并且不存在循环加工。每个工件i在机器j上面的处理称为操作(k、i),其持续时间为,我们的目标是最小化最大完工时间。
条件假设工件1依次被机器1,2,3加工,工件2依次被机器2,1,4,3加工,工件3依次被机器1,2,4加工,因为工件的生产加工是要按照一定的流程进行,所以我们可以先绘制每一个工件的工作的图的模型。工件1被机器1,2,3加工的时间分别为10,8,4;工件2被机器2,1,4,3加工的时间分别为8,3,5,6;工件3被机器加工的时间分别为4,7,3。因为工件的生产加工是要按照一定的流程进行,所以我们可以先绘制工件加工的图的模型。
我们知道,图是由顶点和连接顶点的边构成,于是我们在eclipse中需要定义三个类分别来描述顶点、边和图。
1.定义关于顶点的类:问题中,每一个顶点都与工件和加工工件机器相关,每一个工件被机器加工都涉及一个特定的加工时间和完工时刻。同时,我们还需要指出每一个顶点流出的边和流入的边的集合。代码如下:
package JSPProblem;
import java.util.ArrayList;
import java.util.List;
import ilog.concert.IloNumExpr;
import ilog.concert.IloNumVar;
public class Vertex {
int id;
int processedTime;//加工时间
IloNumVar completionTime;//完工时刻
List<Edge> flowOutEdges = new ArrayList<>();//流出边的集合
List<Edge> flowInEdges = new ArrayList<>();//流入边的集合
}
2.定义关于边的类:每一条弧都有一个头顶点和尾顶点,对于图中的虚线弧还需要定义一个0-1变量,以确定其是否被选中。
package JSPProblem;
import ilog.concert.IloIntVar;
import ilog.concert.IloNumVar;
public class Edge {
public int id;
public Vertex head;//弧的头顶点
public Vertex tail;//弧的尾顶点
public IloIntVar X;//根据每一条弧是否被选择定义的0-1变量,如果被选择取1,否则取0
}
3.定义关于图的类:在图的类中,我们需要将顶点和边关联起来,为此我们建立两个关于顶点和边的可变长度的数组。同时我们需要定义每一台机器对应的虚线弧的集合A,每一个工件对应的实线弧的集合E。定义同时含有流出弧的和流入弧的可变长度的顶点集合,定义关于每一台机器的虚拟开始顶点集合和虚拟终止顶点的集合。
package JSPProblem;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Graph {
List<Vertex> V = new ArrayList<>();
List<Edge> E = new ArrayList<>();
//定义E_{I}
public ArrayList<Edge> EI = new ArrayList<>();
//定义A
public ArrayList<Edge> A = new ArrayList<>();
//定义UJOUT,虚拟的只具有流出弧的顶点集合
public ArrayList<Vertex> UJOUT = new ArrayList<>();
//定义UJIN,虚拟的只具有流入弧的顶点集合
public ArrayList<Vertex> UJIN = new ArrayList<>();
//定义UJMid,同时具有流入和流出弧的顶点集合
public ArrayList<Vertex> UJMid = new ArrayList<>();
}
4.使用Java来实现数学模型的建立
下图是在前文机器调度(JSP)问题数学模型中给出的数学模型:
我们调用cplex对问题进行求解,首先我们导入下载好的cplex.jar包,步骤如下:
在eclipse中右键,在选项栏中找到Build Path,在子菜单栏中选择Configure Build Path;
在弹出的界面中点击Libraries;
在右边的选项栏中点击Add External JARS…
找到cplex.jar所在包的路径,导入cplex即可
现在我们就可以调用cplex里面的相关方法了。
1.回到问题,我们先定义cplex环境变量:
//定义cplex环境变量
IloCplex cplex = new IloCplex();
2.初始化决策变量
//初始化决策变量completionTime
for(Vertex v:graph.UJMid) {
v.completionTime = cplex.numVar(0, Double.MAX_VALUE);
}
//初始化决策变量X
for(Edge e:graph.A) {
e.X = cplex.intVar(0, 1);
}
for(Edge e:graph.EI) {
e.X = cplex.intVar(0, 1);
}
for(Vertex v:graph.UJIN) {
for(int i = 0; i < v.flowInEdges.size(); i++){
v.flowInEdges.get(i).X = cplex.intVar(0, 1);
}
}
for(Vertex v:graph.UJOUT) {
for(int i = 0; i < v.flowOutEdges.size(); i++){
v.flowOutEdges.get(i).X = cplex.intVar(0, 1);
}
}
for(Vertex v:graph.UJMid) {
for(int i = 0; i < v.flowOutEdges.size(); i++){
v.flowOutEdges.get(i).X = cplex.intVar(0, 1);
}
}
for(Vertex v:graph.UJMid) {
for(int i = 0; i < v.flowInEdges.size(); i++){
v.flowInEdges.get(i).X = cplex.intVar(0, 1);
}
}
3.定义目标函数:这里我们的目标函数obj是最小化最大完工时刻,这里我们先假设一个变量c,使得c满足约束条件c大于等于所有的完工时刻,然后最小化c即可。代码如下:
//定义目标函数
IloNumExpr obj = cplex.numExpr();
IloNumVar c = cplex.intVar(0, (int) Double.MAX_VALUE);
for(Vertex v: graph.UJMid) {
cplex.addGe(c,v.completionTime);
}
obj = cplex.sum(obj,cplex.prod(c, 1));
cplex.addMinimize(obj);
4.约束条件:
- 对于约束条件(1)中所包含的点的集合,就是我们在Graph中定义的UJMid
//约束条件1:
for(Vertex v : graph.UJMid) {
IloNumExpr cs1 = cplex.numExpr();
cs1 = cplex.sum(cs1,cplex.prod(v.completionTime,1));
cplex.addGe(cs1,v.processedTime);
}
- 约束条件(2)指的是对于同一个工件而言,工件被机器加工的紧前工序的完工时刻加上紧后工序的加工时间要小于等于紧后工序的完工时刻。工序对应于我们定义的集合EI,即工件加工的图的模型中的实线边。因此我们可以建立如下约束:
//约束条件2
for(Edge edge: graph.EI) {
IloNumExpr cs2 = cplex.numExpr();
Vertex v = edge.head;
Vertex u = edge.tail;
cs2 = cplex.sum(cs2,cplex.prod(v.completionTime,1));
cs2 = cplex.sum(cs2,cplex.prod(u.completionTime,-1));
cplex.addGe(cs2,v.processedTime);
}
- 约束条件(3)(4)指的是对于同一台机器而言,机器加工工件流程存在一定先后顺序。为方便叙述问题,对于每一台机器,我们虚拟一个开始加工顶点和一个结束加工顶点,虚拟的开始加工节点和结束加工顶点有且只有一条弧流出和流入。代码如下:
//约束条件3:流出来的弧的数量之和等于1
for(Vertex vertexOut:graph.UJOUT) {
IloNumExpr cs3 = cplex.numExpr();
for(int i = 0; i < vertexOut.flowOutEdges.size(); i++) {
Edge outEdge = vertexOut.flowInEdges.get(i);
cs3 = cplex.sum(cs3,cplex.prod(outEdge.X, 1));
}
cplex.addEq(cs3,1);//流出开始加工顶点的边数之和等于1
}
//约束条件4:流进来的弧的数量之和等于1
for(Vertex vertexIn:graph.UJIN) {
IloNumExpr cs4 = cplex.numExpr();
for(int i = 0; i < vertexIn.flowInEdges.size(); i++) {
Edge inEdge = vertexIn.flowInEdges.get(i);
cs4 = cplex.sum(cs4,cplex.prod(inEdge.X, 1));
}
cplex.addEq(cs4,1);//流入结束加工顶点的边数之和等于1
}
- 约束条件(5)指的是对于同一台机器上加工的所有工件,每个工件只有一个紧前工序和一个紧后工序,即流出来的弧和流入的虚线弧有且只有一条。代码如下:
//约束条件5:每个中间节点指向该节点的边数等于指出该节点的边数且等于1
for(Vertex vertexMid:graph.UJMid) {
IloNumExpr cs5 = cplex.numExpr();
IloNumExpr cs6 = cplex.numExpr();
for(int i = 0; i < vertexMid.flowInEdges.size(); i++) {
for(int j = 0; j < vertexMid.flowOutEdges.size(); j++) {
Edge outEdge = vertexMid.flowInEdges.get(j);
Edge inEdge = vertexMid.flowInEdges.get(i);
cs5 = cplex.sum(cs5,cplex.prod(outEdge.X, 1));
cs5 = cplex.sum(cs5,cplex.prod(inEdge.X, -1));
cs6 = cplex.sum(cs6,cplex.prod(outEdge.X, 1));
}
cplex.addEq(cs5,0);
cplex.addEq(cs6,1);
}
}
- 约束条件(6)是指在同一台机器上面加工的零件,对于选中的边,要求满足紧前工序的完工时刻加上紧后工序的加工时间小于等于紧后工序的完工时刻;而对于没有被选中的边而言,要求约束条件始终成立。代码如下:
//约束条件6:在同一台机器上面加工的零件,紧前工序的完工时刻加上紧后工序的加工时间小于等于紧后工序的完工时刻
for(Edge edge: graph.A) {
IloNumExpr cs7 = cplex.numExpr();
Vertex v = edge.head;
Vertex u = edge.tail;
cs7 = cplex.sum(cs7,cplex.prod(v.completionTime,1));
cs7 = cplex.sum(cs7,cplex.prod(u.completionTime,-1));
cs7 = cplex.sum(cs7,cplex.prod(edge.X,-10000));
cs7 = cplex.sum(cs7,10000);
cplex.addGe(cs7,v.processedTime);
}
4.现在给出问题的实例,并进行求解。定义问题对应的求解方法,将问题中的数值进行输入:
public static Graph Data(){
//创建一个graph对象
Graph graph = new Graph();
//工件1的工序对应的点
Vertex v1 = new Vertex();
Vertex v2 = new Vertex();
Vertex v3 = new Vertex();
//工件2的工序对应的点
Vertex v4 = new Vertex();
Vertex v5 = new Vertex();
Vertex v6 = new Vertex();
Vertex v7 = new Vertex();
//工件3的工序对应的点
Vertex v8 = new Vertex();
Vertex v9 = new Vertex();
Vertex v10 = new Vertex();
graph.V.add(v1);
graph.V.add(v2);
graph.V.add(v3);
graph.V.add(v4);
graph.V.add(v5);
graph.V.add(v6);
graph.V.add(v7);
graph.V.add(v8);
graph.V.add(v9);
graph.V.add(v10);
v1.processedTime = 10;
v2.processedTime = 8;
v3.processedTime = 4;
v4.processedTime = 8;
v5.processedTime = 3;
v6.processedTime = 5;
v7.processedTime = 6;
v8.processedTime = 4;
v9.processedTime = 7;
v10.processedTime = 3;
Edge e1 = new Edge();
e1.head = v2;
e1.tail = v1;
Edge e2 = new Edge();
e2.head = v3;
e2.tail = v2;
Edge e3 = new Edge();
e3.head = v5;
e3.tail = v4;
Edge e4 = new Edge();
e4.head = v6;
e4.tail = v5;
Edge e5 = new Edge();
e5.head = v7;
e5.tail = v6;
Edge e6 = new Edge();
e6.head = v9;
e6.tail = v8;
Edge e7 = new Edge();
e7.head = v10;
e7.tail = v9;
//向集合E中添加边
graph.E.add(e1);
graph.E.add(e2);
graph.E.add(e3);
graph.E.add(e4);
graph.E.add(e5);
graph.E.add(e6);
graph.E.add(e7);
//向集合EI中添加边
graph.EI.add(e1);
graph.EI.add(e2);
graph.EI.add(e3);
graph.EI.add(e4);
graph.EI.add(e5);
graph.EI.add(e6);
graph.EI.add(e7);
//定义集合A
//机器1对应的集合
Edge e121 = new Edge();
Edge e211 = new Edge();
e121.head = v5;
e121.tail = v1;
e211.head = v1;
e211.tail = v5;
graph.A.add(e121);
graph.A.add(e211);
Edge e131 = new Edge();
Edge e311 = new Edge();
e131.head = v8;
e131.tail = v1;
e311.head = v1;
e311.tail = v8;
graph.A.add(e131);
graph.A.add(e311);
Edge e231 = new Edge();
Edge e321 = new Edge();
e231.head = v8;
e231.tail = v5;
e321.head = v5;
e321.tail = v8;
graph.A.add(e231);
graph.A.add(e321);
//机器2对应的集合
Edge e122 = new Edge();
Edge e212 = new Edge();
e122.head = v4;
e122.tail = v2;
e212.head = v2;
e212.tail = v4;
graph.A.add(e122);
graph.A.add(e212);
Edge e132 = new Edge();
Edge e312 = new Edge();
e132.head = v9;
e132.tail = v2;
e312.head = v2;
e312.tail = v9;
graph.A.add(e132);
graph.A.add(e312);
Edge e232 = new Edge();
Edge e322 = new Edge();
e232.head = v9;
e232.tail = v4;
e322.head = v4;
e322.tail = v9;
graph.A.add(e232);
graph.A.add(e322);
//机器3对应的集合
Edge e123 = new Edge();
Edge e213 = new Edge();
e123.head = v7;
e123.tail = v3;
e213.head = v3;
e213.tail = v7;
graph.A.add(e123);
graph.A.add(e213);
//机器4对应的集合
Edge e234 = new Edge();
Edge e324 = new Edge();
e234.head = v10;
e234.tail = v6;
e324.head = v6;
e324.tail = v10;
graph.A.add(e234);
graph.A.add(e324);
//定义集合UJIN和UJOUT
//机器1对应的UJIN和UJOUT
Vertex v01 = new Vertex();
Vertex vM1 = new Vertex();
Edge e011 = new Edge();
Edge e021 = new Edge();
Edge e031 = new Edge();
Edge e1M1 = new Edge();
Edge e2M1 = new Edge();
Edge e3M1 = new Edge();
graph.UJIN.add(v01);
graph.UJOUT.add(vM1);
v01.flowOutEdges.add(e011);
v01.flowOutEdges.add(e021);
v01.flowOutEdges.add(e031);
vM1.flowOutEdges.add(e1M1);
vM1.flowOutEdges.add(e2M1);
vM1.flowOutEdges.add(e3M1);
//机器2对应的UJIN和UJOUT
Vertex v02 = new Vertex();
Vertex vM2 = new Vertex();
Edge e012 = new Edge();
Edge e022 = new Edge();
Edge e032 = new Edge();
Edge e1M2 = new Edge();
Edge e2M2 = new Edge();
Edge e3M2 = new Edge();
graph.UJIN.add(v02);
graph.UJOUT.add(vM2);
v02.flowOutEdges.add(e012);
v02.flowOutEdges.add(e022);
v02.flowOutEdges.add(e032);
vM2.flowOutEdges.add(e1M2);
vM2.flowOutEdges.add(e2M2);
vM2.flowOutEdges.add(e3M2);
//机器3对应的UJIN和UJOUT
Vertex v03 = new Vertex();
Vertex vM3 = new Vertex();
Edge e013 = new Edge();
Edge e023 = new Edge();
Edge e1M3 = new Edge();
Edge e2M3 = new Edge();
graph.UJIN.add(v03);
graph.UJOUT.add(vM3);
v02.flowOutEdges.add(e013);
v02.flowOutEdges.add(e023);
vM2.flowOutEdges.add(e1M3);
vM2.flowOutEdges.add(e2M3);
//机器3对应的UJIN和UJOUT
Vertex v04 = new Vertex();
Vertex vM4 = new Vertex();
Edge e024 = new Edge();
Edge e034 = new Edge();
Edge e2M4 = new Edge();
Edge e3M4 = new Edge();
graph.UJIN.add(v04);
graph.UJOUT.add(vM4);
v02.flowOutEdges.add(e024);
v02.flowOutEdges.add(e034);
vM2.flowOutEdges.add(e2M4);
vM2.flowOutEdges.add(e3M4);
//定义集合UJMid
graph.UJMid.add(v1);
graph.UJMid.add(v2);
graph.UJMid.add(v3);
graph.UJMid.add(v4);
graph.UJMid.add(v5);
graph.UJMid.add(v6);
graph.UJMid.add(v7);
graph.UJMid.add(v8);
graph.UJMid.add(v9);
graph.UJMid.add(v10);
v1.flowOutEdges.add(e121);
v1.flowOutEdges.add(e131);
v1.flowInEdges.add(e211);
v1.flowInEdges.add(e311);
v2.flowOutEdges.add(e122);
v2.flowOutEdges.add(e132);
v2.flowInEdges.add(e212);
v2.flowInEdges.add(e312);
v3.flowOutEdges.add(e123);
v3.flowInEdges.add(e213);
v4.flowOutEdges.add(e212);
v4.flowOutEdges.add(e232);
v4.flowInEdges.add(e122);
v4.flowInEdges.add(e322);
v5.flowOutEdges.add(e211);
v5.flowOutEdges.add(e231);
v5.flowInEdges.add(e121);
v5.flowInEdges.add(e321);
v6.flowOutEdges.add(e234);
v6.flowInEdges.add(e324);
v7.flowOutEdges.add(e213);
v7.flowInEdges.add(e123);
v8.flowOutEdges.add(e321);
v8.flowOutEdges.add(e311);
v8.flowInEdges.add(e231);
v8.flowInEdges.add(e131);
v9.flowOutEdges.add(e312);
v9.flowOutEdges.add(e322);
v9.flowInEdges.add(e132);
v9.flowInEdges.add(e232);
v10.flowOutEdges.add(e324);
v10.flowInEdges.add(e234);
return graph;
}