关于分支定价求解GAP问题的Java实现
没有找到可以参考的列生成的实现方式,自己实现了一下,分享出来,一起学习
- 关于列生成和分支定价的部分可以参考
https://blog.youkuaiyun.com/u014090505/article/details/89019327 - 广义分配(GAP)问题描述和模型参考的文献
高振, 唐立新, 汪定伟. 列生成解大规模NP-hard整数与组合优化问题[J]. 信息与控制, 2005, 32(z1):604-607. - 程序用Java实现,调用CPLEX
- 代码如下:
GAP_BP.java
import java.io.IOException;
import java.util.ArrayList;
import ilog.concert.IloException;
public class GAP_BP
{
public static double RC_EPS = 1.0e-6;
public static int nMachine=5;
public static int nJob=10;
public static double[] b;
public static double[] capacity;
public static double[][] consume;
public static double[][] profit;
static class PAIR
{
public int job_id;
public double profit_weight;
public PAIR(int id, double pw)
{
job_id = id;
profit_weight = pw;
}
}
public static void main(String[] args) throws IloException, IOException
{
long begintime =0;
long endtime=0;
long costTime=0;
begintime = System.currentTimeMillis();
GAP_BP BP=new GAP_BP();
ArrayList<ProblemNode> nodeQueue=new ArrayList<>();
ProblemNode node=new ProblemNode();
node.initNode();
nodeQueue.add(node);
double[] bestS = null;
double bestObj=Double.MIN_VALUE;
ProblemNode problem0=new ProblemNode();
while(nodeQueue.size()>0)
{
ProblemNode problem=nodeQueue.get(nodeQueue.size()-1);
boolean next=false;
while(true)
{
double[] s = null;
if(problem.solve())
s=problem.curSol;
else
{
next=true;
break;
}
double tempObj=problem.curObj;
System.out.println("obj"+tempObj);
if(isIntSol(s)&&tempObj>bestObj)
{
bestObj=tempObj;
bestS=s.clone();
System.out.println("bestOBj: "+bestObj);
problem.outPutBest();
problem0=problem;
}
int[] newPattern=problem.findPattern_CPLEX_IP();
if(newPattern!=null)
{
problem.addColumn(newPattern);
System.out.println("cloumnNum:::"+problem.columns.size());
}
else
break;
}
if(next)//由于不可行跳出循环, 终止节点
{
problem.solver.end();
nodeQueue.remove(nodeQueue.size()-1);
continue;
}
else //由于不可继续加列跳出循环
{
if(problem.curObj<=bestObj||isIntSol(problem.curSol))
{
problem.solver.end();
nodeQueue.remove(nodeQueue.size()-1);
continue;
}
else
{
System.out.println("分支");
//分支,加俩节点,删除当前节点
@SuppressWarnings("unchecked")
ArrayList<Integer> fixedVars=(ArrayList<Integer>) problem.fixedVars.clone();
double min=0.5;
int index=0;
for(int i=0;i<problem.curSol.length;i++)
{
double m=Math.rint(problem.curSol[i]);
if(Math.abs(m-problem.curSol[i])>0)
{
index=i;
break;
}
}
System.out.println("index:: "+index+" var:: "+problem.curSol[index]);
fixedVars.set(index, 0);
ProblemNode node1=new ProblemNode(problem,fixedVars);
fixedVars.set(index, 1);
ProblemNode node2=new ProblemNode(problem,fixedVars);
problem.solver.end();
nodeQueue.remove(nodeQueue.size()-1);
nodeQueue.add(node1);
nodeQueue.add(node2);
}
}
}
System.out.println("bestOBj: "+bestObj);
problem0.outPutBest();
endtime=System.currentTimeMillis();
costTime = (endtime - begintime);
System.out.println("时间消耗:"+costTime/1000.0+"s");
}
public GAP_BP() throws IOException
{
String datafile = "./src/data5063.dat";
readData(datafile);
nMachine = capacity.length;
nJob = consume[0].length;
}
public static void readData(String fileName)throws IOException
{
InputDataReader reader = new InputDataReader(fileName);
try
{
b = reader.readDoubleArray();
capacity = reader.readDoubleArray();
consume = reader.readDoubleArrayArray();
profit = reader.readDoubleArrayArray();
}
catch (InputDataReader.InputDataReaderException exc )
{
System.err.println(exc);
}
}
public static boolean isIntSol(double[] sol)
{
for(int i=0;i<sol.length;i++)
{
if((int)sol[i]!=sol[i])
return false;
}
return true;
}
}
ProblemNode.java
import java.io.IOException;
import java.util.ArrayList;
import ilog.concert.IloColumn;
import ilog.concert.IloException;
import ilog.concert.IloNumExpr;
import ilog.concert.IloNumVar;
import ilog.concert.IloNumVarType;
import ilog.concert.IloObjective;
import ilog.concert.IloRange;
import ilog.cplex.IloCplex;
public class ProblemNode
{
public IloRange[] Fill;
public IloNumVarArray patterns;
public IloObjective totalProfit;
public IloCplex solver;
public double[] curSol;
public double curObj;
public ArrayList<int[]> columns;
public ArrayList<Integer> objs;
public ArrayList<Integer> fixedVars;
public ProblemNode()
{
}
@SuppressWarnings("unchecked")
public ProblemNode(ProblemNode node,ArrayList<Integer> fixedVars) throws IloException
{
this.fixedVars=(ArrayList<Integer>) fixedVars.clone();
this.objs=(ArrayList<Integer>) node.objs.clone();
solver = new IloCplex();
totalProfit = solver.addMaximize();//目标函数
Fill = new IloRange[GAP_BP.b.length];
for (int f = 0; f < GAP_BP.b.length; f++)
Fill[f] = solver.addRange(GAP_BP.b[f],1);
patterns = new IloNumVarArray();
columns =new ArrayList<>();
for(int i=0;i<node.columns.size();i++)
{
addColumn(node.columns.get(i), node.objs.get(i),this.fixedVars.get(i));
}
solver.setOut(null);
}
public void initNode() throws IloException, IOException
{
solver = new IloCplex();
totalProfit = solver.addMaximize();//目标函数
Fill = new IloRange[GAP_BP.b.length];
for (int f = 0; f < GAP_BP.b.length; f++)
Fill[f] = solver.addRange(GAP_BP.b[f],1);
ArrayList<int[]> sol = initSolution();
patterns = new IloNumVarArray();
fixedVars=new ArrayList<>();
objs=new ArrayList<>();
columns=new ArrayList<>();
int[] column=new int[GAP_BP.nMachine+GAP_BP.nJob];
for(int j = 0; j < GAP_BP.nMachine; j++)
{
if(sol.get(j)[0]> 0)
{
IloColumn pattern = solver.column(totalProfit,sol.get(j)[0]);
objs.add(sol.get(j)[0]);
column=new int[GAP_BP.nMachine+GAP_BP.nJob];
for(int i = 0; i < GAP_BP.nJob; i++)
{
int value=sol.get(j)[i+2];
pattern=pattern.and(solver.column(Fill[i],value));
column[i]=value;
}
for(int i = 0; i < GAP_BP.nMachine; i++)
{
int value=0;
if(j==i)
value=1;
pattern=pattern.and(solver.column(Fill[GAP_BP.nJob+i],value));
column[GAP_BP.nJob + i] = value;
}
fixedVars.add(-1);
patterns.add(solver.numVar(pattern,0,1));
columns.add(column);
}
}
solver.setOut(null);
}
public boolean solve() throws IloException
{
if(!solver.solve())
return false;
curSol=new double[patterns.getSize()];
for (int j = 0; j < patterns.getSize(); j++)
curSol[j]=solver.getValue(patterns.getElement(j));
curObj=solver.getObjValue();
return true;
}
public void addColumn(int[] column,int profit,int fixed) throws IloException
{
IloColumn pattern = solver.column(totalProfit,profit);
for(int i=0;i<column.length;i++)
pattern=pattern.and(solver.column(Fill[i],column[i]));
if(fixed==-1)
patterns.add(solver.numVar(pattern,0,1));
else
patterns.add(solver.numVar(pattern,fixed,fixed));
columns.add(column.clone());
}
public void addColumn(int[] newPattern) throws IloException
{
int cur_profit = 0;
int[] column=new int[GAP_BP.nMachine+GAP_BP.nJob];
//生成的新的子问题的解,转化成一列column
IloColumn pattern = solver.column(Fill[0],newPattern[1]);
column[0] =newPattern[1];
cur_profit += newPattern[1] * GAP_BP.profit[newPattern[0]][0];
for(int i = 1; i < GAP_BP.nJob; i++) //设置新的方案,同时计算该方案的收益
{
pattern = pattern.and(solver.column(Fill[i],newPattern[i + 1]));
cur_profit += newPattern[i+1] * GAP_BP.profit[newPattern[0]][i];
column[i] =newPattern[i + 1];
}
for(int i = 0; i < GAP_BP.nMachine; i++)
{
int value=0;
if(i==newPattern[0])
value=1;
pattern = pattern.and(solver.column(Fill[GAP_BP.nJob+i],value));
column[GAP_BP.nJob+i] =value;
}
IloColumn pp = solver.column(totalProfit,cur_profit);
pattern=pp.and(pattern);
patterns.add(solver.numVar(pattern,0,1));//添加新列new parttern
columns.add(column);
fixedVars.add(-1);
objs.add(cur_profit);
}
//输出最优解
public void outPutBest()
{
System.out.println();
for(int i=0;i<curSol.length;i++)
{
if(curSol[i]==1)
{
int j=0;
for(j=GAP_BP.nJob;j<columns.get(i).length;j++)
{
if(columns.get(i)[j]==1)
System.out.print("M"+(j-GAP_BP.nJob)+":");
}
for(j=0;j<GAP_BP.nJob;j++)
{
if(columns.get(i)[j]==1)
System.out.printf("%4s",j);
}
System.out.println();
}
}
}
//生成新的列,求解子问题(背包问题),可以选择合适的方法求解
public int[] findPattern_CPLEX_IP() throws IloException
{
double[] price=solver.getDuals(Fill);
double maxProfit =-1;//tmpProfit = 0;
int[] bestPattern=null;
double[] profit_j;
double[] consume_j;
for(int j = 0; j <GAP_BP.nMachine; j++)
{
IloCplex patSolver=new IloCplex(); patSolver.setOut(null);
profit_j=new double[GAP_BP.nJob];
consume_j=new double[GAP_BP.nJob];
for(int i = 0; i < GAP_BP.nJob; i++)
{
profit_j[i]=GAP_BP.profit[j][i] - price[i];
consume_j[i]=(GAP_BP.consume[j][i]);
}
IloNumVar[] x = patSolver.numVarArray(GAP_BP.nJob, 0, 1, IloNumVarType.Int); patSolver.addMaximize(patSolver.diff(patSolver.scalProd(x, profit_j), price[GAP_BP.nJob+j]));
IloNumExpr expr=patSolver.linearIntExpr();
for(int i=0;i<GAP_BP.nJob;i++) expr=patSolver.sum(expr,patSolver.prod(consume_j[i], x[i]));
patSolver.addLe(expr,GAP_BP.capacity[j]);
patSolver.solve();
if (patSolver.getObjValue()> maxProfit)
{
maxProfit = patSolver.getObjValue();
bestPattern=new int[GAP_BP.nJob+1];
bestPattern[0]=j;//第一个元素记录机器的编号
for(int i = 0; i < GAP_BP.nJob; i++)
bestPattern[i+1]=(int)patSolver.getValue(x[i]);
}
if(maxProfit<GAP_BP.RC_EPS)
bestPattern=null;
patSolver.end();
}
return bestPattern;
}
public static ArrayList<int[]> initSolution()
{
ArrayList<int[]> solutions=new ArrayList<>();
for(int i=0;i<GAP_BP.nMachine;i++)
solutions.add(new int[GAP_BP.nJob+2]);//第0号元素记录累计收益,第1号元素记录累计消耗,其他元素为1表示对应的任务分给了该机器
ArrayList<Integer> jobsID=new ArrayList<>();
int job = -1, machine = -1;
double max_pw = 0; // profit/weight,即单位消耗的收益
for(int i = 0; i <GAP_BP.nJob; i++)
jobsID.add(i);
while(jobsID.size() > 0)
{
max_pw = 0;
for(int i = 0; i < jobsID.size(); i++)
{
for(int j = 0; j < GAP_BP.nMachine; j++)
{
if(solutions.get(j)[1]+GAP_BP.consume[j][jobsID.get(i)] > GAP_BP.capacity[j])
continue; //如果机器能力不满足则跳过该机器
if(GAP_BP.profit[j][jobsID.get(i)] / (double)GAP_BP.consume[j][jobsID.get(i)] > max_pw)
{
max_pw = GAP_BP.profit[j][jobsID.get(i)] / (double)GAP_BP.consume[j][jobsID.get(i)];
job = i;
machine = j;
}
}
}
solutions.get(machine)[jobsID.get(job)+ 2] = 1;
solutions.get(machine)[0] += GAP_BP.profit[machine][jobsID.get(job)];
solutions.get(machine)[1] += GAP_BP.consume[machine][jobsID.get(job)];
jobsID.remove(job);
}
return solutions;
}
}
读文件函数,CPLEX样例程序自带,InputDataReader.java
/* --------------------------------------------------------------------------
* File: InputDataReader.java
* Version 12.6
* --------------------------------------------------------------------------
* Licensed Materials - Property of IBM
* 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5655-Y21
* Copyright IBM Corporation 2001, 2013. All Rights Reserved.
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with
* IBM Corp.
* --------------------------------------------------------------------------
*
* This is a helper class used by several examples to read input data files
* containing arrays in the format [x1, x2, ..., x3]. Up to two-dimensional
* arrays are supported.
*/
import java.io.*;
public class InputDataReader {
public static class InputDataReaderException extends Exception {
private static final long serialVersionUID = 1021L;
InputDataReaderException(String file) {
super("'" + file + "' contains bad data format");
}
}
StreamTokenizer _tokenizer;
Reader _reader;
String _fileName;
public InputDataReader(String fileName) throws IOException {
_reader = new FileReader(fileName);
_fileName = fileName;
_tokenizer = new StreamTokenizer(_reader);
// State the '"', '\'' as white spaces.
_tokenizer.whitespaceChars('"', '"');
_tokenizer.whitespaceChars('\'', '\'');
// State the '[', ']' as normal characters.
_tokenizer.ordinaryChar('[');
_tokenizer.ordinaryChar(']');
_tokenizer.ordinaryChar(',');
}
protected void finalize() throws Throwable {
_reader.close();
}
double readDouble() throws InputDataReaderException,
IOException {
int ntType = _tokenizer.nextToken();
if ( ntType != StreamTokenizer.TT_NUMBER )
throw new InputDataReaderException(_fileName);
return _tokenizer.nval;
}
int readInt() throws InputDataReaderException,
IOException {
int ntType = _tokenizer.nextToken();
if ( ntType != StreamTokenizer.TT_NUMBER )
throw new InputDataReaderException(_fileName);
return (new Double(_tokenizer.nval)).intValue();
}
double[] readDoubleArray() throws InputDataReaderException,
IOException {
int ntType = _tokenizer.nextToken(); // Read the '['
if ( ntType != '[' )
throw new InputDataReaderException(_fileName);
DoubleArray values = new DoubleArray();
ntType = _tokenizer.nextToken();
while (ntType == StreamTokenizer.TT_NUMBER) {
values.add(_tokenizer.nval);
ntType = _tokenizer.nextToken();
if ( ntType == ',' ) {
ntType = _tokenizer.nextToken();
}
else if ( ntType != ']' ) {
throw new InputDataReaderException(_fileName);
}
}
if ( ntType != ']' )
throw new InputDataReaderException(_fileName);
// Allocate and fill the array.
double[] res = new double[values.getSize()];
for (int i = 0; i < values.getSize(); i++) {
res[i] = values.getElement(i);
}
return res;
}
double[][] readDoubleArrayArray() throws InputDataReaderException,
IOException {
int ntType = _tokenizer.nextToken(); // Read the '['
if ( ntType != '[' )
throw new InputDataReaderException(_fileName);
DoubleArrayArray values = new DoubleArrayArray();
ntType = _tokenizer.nextToken();
while (ntType == '[') {
_tokenizer.pushBack();
values.add(readDoubleArray());
ntType = _tokenizer.nextToken();
if ( ntType == ',' ) {
ntType = _tokenizer.nextToken();
}
else if ( ntType != ']' ) {
throw new InputDataReaderException(_fileName);
}
}
if ( ntType != ']' )
throw new InputDataReaderException(_fileName);
// Allocate and fill the array.
double[][] res = new double[values.getSize()][];
for (int i = 0; i < values.getSize(); i++) {
res[i] = new double[values.getSize(i)];
for (int j = 0; j < values.getSize(i); j++) {
res[i][j] = values.getElement(i,j);
}
}
return res;
}
int[] readIntArray() throws InputDataReaderException,
IOException {
int ntType = _tokenizer.nextToken(); // Read the '['
if ( ntType != '[' )
throw new InputDataReaderException(_fileName);
IntArray values = new IntArray();
ntType = _tokenizer.nextToken();
while (ntType == StreamTokenizer.TT_NUMBER) {
values.add(_tokenizer.nval);
ntType = _tokenizer.nextToken();
if ( ntType == ',' ) {
ntType = _tokenizer.nextToken();
}
else if ( ntType != ']' ) {
throw new InputDataReaderException(_fileName);
}
}
if ( ntType != ']' )
throw new InputDataReaderException(_fileName);
// Allocate and fill the array.
int[] res = new int[values.getSize()];
for (int i = 0; i < values.getSize(); i++) {
res[i] = values.getElement(i);
}
return res;
}
int[][] readIntArrayArray() throws InputDataReaderException,
IOException {
int ntType = _tokenizer.nextToken(); // Read the '['
if ( ntType != '[' )
throw new InputDataReaderException(_fileName);
IntArrayArray values = new IntArrayArray();
ntType = _tokenizer.nextToken();
while (ntType == '[') {
_tokenizer.pushBack();
values.add(readIntArray());
ntType = _tokenizer.nextToken();
if ( ntType == ',' ) {
ntType = _tokenizer.nextToken();
}
else if ( ntType != ']' ) {
throw new InputDataReaderException(_fileName);
}
}
if ( ntType != ']' )
throw new InputDataReaderException(_fileName);
// Allocate and fill the array.
int[][] res = new int[values.getSize()][];
for (int i = 0; i < values.getSize(); i++) {
res[i] = new int[values.getSize(i)];
for (int j = 0; j < values.getSize(i); j++) {
res[i][j] = values.getElement(i,j);
}
}
return res;
}
private class DoubleArray {
int _num = 0;
double[] _array = new double[32];
final void add(double dval) {
if ( _num >= _array.length ) {
double[] array = new double[2 * _array.length];
System.arraycopy(_array, 0, array, 0, _num);
_array = array;
}
_array[_num++] = dval;
}
final double getElement(int i) { return _array[i]; }
final int getSize() { return _num; }
}
private class DoubleArrayArray {
int _num = 0;
double[][] _array = new double[32][];
final void add(double[] dray) {
if ( _num >= _array.length ) {
double[][] array = new double[2 * _array.length][];
for (int i = 0; i < _num; i++) {
array[i] = _array[i];
}
_array = array;
}
_array[_num] = new double[dray.length];
System.arraycopy(dray, 0, _array[_num], 0, dray.length);
_num++;
}
final double getElement(int i, int j) { return _array[i][j]; }
final int getSize() { return _num; }
final int getSize(int i) { return _array[i].length; }
}
private class IntArray {
int _num = 0;
int[] _array = new int[32];
final void add(double ival) {
if ( _num >= _array.length ) {
int[] array = new int[2 * _array.length];
System.arraycopy(_array, 0, array, 0, _num);
_array = array;
}
_array[_num++] = (int)Math.round(ival);
}
final int getElement(int i) { return _array[i]; }
final int getSize() { return _num; }
}
private class IntArrayArray {
int _num = 0;
int[][] _array = new int[32][];
final void add(int[] iray) {
if ( _num >= _array.length ) {
int[][] array = new int[2 * _array.length][];
for (int i = 0; i < _num; i++) {
array[i] = _array[i];
}
_array = array;
}
_array[_num] = new int[iray.length];
System.arraycopy(iray, 0, _array[_num], 0, iray.length);
_num++;
}
final int getElement(int i, int j) { return _array[i][j]; }
final int getSize() { return _num; }
final int getSize(int i) { return _array[i].length; }
}
}