C++用户的Cplex使用指南(四)——Cutting stock problem与Column Generation

1 Cutting stock problem

1.1 模型改进

符号定义:
x j x_j xj:第 j j j种切法使用的次数(即按这种切法切割的钢卷的数量);
a i j a_{ij} aij:在第 j j j种切法中切割出长度为 w i w_i wi的钢卷的数目。

例如,标准钢卷长度为 W = 100 W=100 W=100,需要的钢卷长度 w i = 25 , 35 , 45 w_i=25,35,45 wi=25,35,45,每种长度钢卷的需求量 n i = 100 , 200 , 300 , ( i = 1 , … , 3 ) n_i=100,200,300,(i=1,\dots,3) ni=100,200,300,(i=1,,3)
则可以有以下几种切法:
切法1:将1个标准长度的钢卷切成4个 w 1 = 25 w_1=25 w1=25的,则 a 11 = 4 , a 21 = 0 , a 31 = 0 a_{11}=4,a_{21}=0,a_{31}=0 a11=4,a21=0,a31=0
切法2:将1个标准长度的钢卷切成1个 w 1 = 25 w_1=25 w1=25的和2个 w 2 = 35 w_2=35 w2=35的,则 a 12 = 1 , a 22 = 2 , a 32 = 0 a_{12}=1,a_{22}=2,a_{32}=0 a12=1,a22=2,a32=0
切法3:将1个标准长度的钢卷切成2个 w 3 = 45 w_3=45 w3=45的,则 a 13 = 0 , a 23 = 0 , a 33 = 2 a_{13}=0,a_{23}=0,a_{33}=2 a13=0,a23=0,a33=2
……

则数学模型( P 2 P_2 P2)如下:

min ⁡ ∑ j = 1 n x j s.t. ∑ j = 1 n a i j x j ≥ n i , i = 1 , … , m (需求数量约束) x j ∈ Z + , j = 1 , … , n \min\quad \sum^n_{j=1}x_j\\ \text{s.t.}\quad \sum_{j=1}^na_{ij}x_j\ge n_i,i=1,\dots,m\quad\text{(需求数量约束)}\\ x_j\in\mathbb{Z}_+,j=1,\dots,n minj=1nxjs.t.j=1naijxjni,i=1,,m(需求数量约束)xjZ+,j=1,,n

其中 n n n为所有切法的数目,同时满足 ∑ i = 1 m w i a i j ≤ W \sum^m_{i=1}w_ia_{ij}\le W i=1mwiaijW

A = [ a 11 a 12 a 13 ⋯ a 21 a 22 a 23 ⋯ ⋮ ⋮ ⋮ ⋮ a m 1 a m 2 a m 3 ⋯ ] A= \begin{bmatrix} a_{11}&a_{12}&a_{13}&\cdots\\ a_{21}&a_{22}&a_{23}&\cdots\\ \vdots&\vdots&\vdots&\vdots\\ a_{m1}&a_{m2}&a_{m3}&\cdots\\ \end{bmatrix} A=a11a21am1a12a22am2a13a23am3

A中的每一列代表一种切法。

1.2 切法数量的估计

C m k ˉ = m ! k ˉ ! ( m − k ˉ ) ! C^{\bar{k}}_m=\frac{m!}{\bar{k}!(m-\bar{k})!} Cmkˉ=kˉ!(mkˉ)!m!

其中, k ˉ \bar{k} kˉ表示每种切法中切割出不同长度钢卷数目的平均值,可以看出当 m m m增大时,切法的数目会呈指数级增大。

虽然依然能用单纯形法求解上述模型 P 1 , P 2 P_1,P_2 P1,P2。先不谈单纯形法求解该模型的效率,仅从需要枚举所有的切割方案 x j x_j xj这一操作,就能知道上述求解方法一定不是最优的。而且上述枚举法存在以下缺点:

  • 当原材料长度增加时,切割方案呈指数式增长,我们无法枚举所有的 x j x_j xj

  • 枚举出来的切割方案可能会剩余过多的木料,这种方案通常不会被选择。

单纯形法要求我们列出所有的变量( x j x_j xj)。回忆一下,单纯形法在每次迭代的过程中,只能用一个非基变量替换一个基变量,而最终的基变量个数仅与约束条件的数量有关。这么看来,有大部分的 x j x_j xj是不会被用到的(也就是说这些方案都不够好)。因此,我们不需要,也没必要列出所有的 x j x_j xj;再者,对于大规模的问题,我们无法列出满足条件的所有变量。基于此,有学者就提出了求解大规模线性规划的高效算法 —— 列生成法,主要适用于**变量数目(列)大于约束条件个数(行)**的线性规划问题。

2 Column Generation

2.1 列生成基本思想

列生成算法的主要思想是对那些无法列出所有变量的原问题(master problem,MP),先考虑有限变量的主问题(restricted master problem ,RMP),其余变量在有需要的时候,再添加到RMP中。什么变量是需要被加入到RMP中的呢?在单纯形法中,我们根据非基变量检验数的正负来选择进基变量;在列生成中,通过子问题(subproblem)来寻找满足reduced cost条件的变量,如果找到的话,就将它加入到RMP中,直至找不到为止,那么MP就求得了最优解。

2.2 引例

2.2.1 Model

Master Problem

x j x_j xj:将要使用模式 j j j的次数;

A i j A_{ij} Aij:满足需求 d i d_i di所需的每个模式 j j j的项数 i i i
min ⁡ ∑ j x j s.t. ∑ i , j A i j x j ≥ d i ∀ i ∈ I x j ≥ 0 ∀ j ∈ J \min \sum_j x_j\\ \text{s.t.}\quad \sum_{i,j}A_{ij}x_j\ge d_i\quad\forall i\in I\\ x_j\ge0\quad\forall j\in J minjxjs.t.i,jAijxjdiiIxj0jJ

Subproblem

π \pi π:master problem当前解的对偶变量向量, π i \pi_i πi即master problem的对偶变量;

W W W:卷材宽度, W i W_i Wi是从卷材上切下的模式中可使用的带材 i i i的宽度;

A i A_i Ai:该subproblem的建模变量,即在此subproblem中尺寸 i i i的切割次数。
min ⁡ 1 − ∑ i π i A i s.t. ∑ i W i A i ≤ W A i ≥ 0 ∀ i ∈ I \min\quad 1-\sum_i\pi_iA_i\\ \text{s.t.}\quad \sum_iW_iA_i\le W\\ A_i\ge0\quad\forall i\in I min1iπiAis.t.iWiAiWAi0iI
Subproblem的目标函数是新列的Reduced Cost。

2.2.2 Codes

/*cplex header file*/
#include <ilcplex/ilocplex.h>

/*using name space for cplex program*/
ILOSTLBEGIN

#define RC_EPS 1.0e-6

static void readData(const char* filename, IloNum& rollWidth, IloNumArray& size, IloNumArray& amount){
    ifstream in(filename);
    if(in){
        in >> rollWidth;//input the standard width of the steel roll
        in >> size;//input the size demand of cilents
        in >> amount;//input the amount demand of cilents
    }
    else{//in case file dose not exist
        cerr << "No such file: " << filename << endl;
        throw(1);
    }
}

/*report for the master problem*/
static void report1(IloCplex& cutSolver, IloNumVarArray Cut, IloRangeArray Fill){
    cout << endl;
    cout << "Using " << cutSolver.getObjective() << " rolls" << endl;//output the total number of used rolls
    cout << endl;

    /*output the number of each pattern, aka x_j*/
    for (IloInt j = 0; j < Cut.getSize();j++){
        cout << " Cut" << j << " = " << cutSolver.getValue(Cut[j]) << endl;
    }
    cout << endl;
    /*output the shadow price of each pattern*/
    for (IloInt i = 0;i<Fill.getSize();i++){
        cout << " Fill" << i << " = " << cutSolver.getDual(Fill[i]) << endl;
    }
    cout << endl;
}

/*report for the subproblems*/
static void report2(IloAlgorithm& patSolver, IloNumVarArray Use, IloObjective obj){
    cout << endl;
    cout << "Reduced cost is " << patSolver.getValue(obj) << endl;//output the reduced cost of the pattern
    cout << endl;

    /*output the cofficients of each size (aka A_i) in the pattern*/
    if(patSolver.getValue(obj)<=-RC_EPS){
        for (IloInt i = 0; i < Use.getSize(); i++) {
            cout << " Use " << i << " = " << patSolver.getValue(Use[i]) << endl;
        }
        cout << endl;
    }
}

/*report for the final master problem*/
static void report3(IloCplex& cutSolver, IloNumVarArray Cut){
    cout << endl;
    cout << "Best integer solution uses " << cutSolver.getObjValue() << " rolls" << endl;//output the final total number of used rolls
    cout << endl;

    /*output the number of each pattern, aka x_j*/
    for (IloInt j = 0; j < Cut.getSize();j++){
        cout << " Cut" << j << " = " << cutSolver.getValue(Cut[j]) << endl;
    }
}

/*main program*/

int main(){
    IloEnv env;//declare the cplex environment

    /*try to establish the model and slove it*/
    try{
        IloInt i, j;

        IloNum rollWidth;//the standard width of steel roll (there is no need to add "(env)" for IloNum/IloInt)
        IloNumArray amount(env);//the wanted amount of cilents
        IloNumArray size(env);

        readData("cutstock.dat", rollWidth, size, amount);//input the data of the problem instance
        //system("pause");
        /*cutting-optimization problem, aka master problem*/
        IloModel cutOpt(env);//declare the model for the master problem

        IloObjective RollsUsed = IloAdd(cutOpt, IloMinimize(env));//add objective to model cutOpt
        IloRangeArray Fill = IloAdd(cutOpt, IloRangeArray(env, amount, IloInfinity));//add constraints to model cutOpt, amount is the lower bound and Ilofinity is the upper bound

        IloNumVarArray Cut(env); //declare decision variables for the master problem, aka x_j; Note that the variables are initialized with numVar

        IloInt nWdth = size.getSize();//the number of different size
        for (j = 0; j < nWdth;j++){
            /*
            add the coefficients of variables by column;
            "RollsUsed(1)"" means the coefficient of the varibale is 1;
            "Fill[j](int(rollWidth / size[j]))" means the coefficient of the variable in the j-th constraint is int(rollWidth / size[j]);
            this program cuts a standard roll with single size as initial solution. 
            */
            Cut.add(IloNumVar(RollsUsed(1) + Fill[j](int(rollWidth / size[j]))));
        }

        IloCplex cutSolver(cutOpt);//declare the solver for the master problem

        /*pattern-generation problem, aka subproblem*/
        IloModel patGen(env);//declare the model for subproblems

        IloObjective ReducedCost = IloAdd(patGen, IloMinimize(env, 1));//"IloMinimize(env, 1)" means the objective function initialize with a constant 1, 1 is the coefficient of the variable in the objective function
        IloNumVarArray Use(env, nWdth, 0.0, IloInfinity, ILOINT);//declare integer decision variables for the subproblems, aka A_i
        patGen.add(IloScalProd(size, Use) <= rollWidth);//(product) add constraints

        IloCplex patSolver(patGen);//declare the solver for subproblems

        /*column-generation procedure*/
        IloNumArray price(env, nWdth);//declare container for the shadow price, aka the cofficients of objective function for subproblems
        IloNumArray newPatt(env, nWdth);//declare the container for new column

        for (;;){
            /*optimize the current pattern, aka solve the current master problem*/
            cutSolver.solve();//solve the master proble
            cutSolver.exportModel("MP.lp");//export the model as lp file
            report1(cutSolver, Cut, Fill);
            /*find and add a new pattern*/
            for (i = 0; i < nWdth;i++){
                price[i] = -cutSolver.getDual(Fill[i]);
            }
            ReducedCost.setLinearCoefs(Use, price);//set the linear coefficients of objective function for subproblems

            patSolver.solve();//solve the subproblem
            patSolver.exportModel("SP.lp");
            report2(patSolver, Use, ReducedCost);

            /*if the redeced cost of the pattern is postive, stop searching for columns*/
            if(patSolver.getValue(ReducedCost)>-RC_EPS){
                break;
            }

            patSolver.getValues(newPatt, Use);//copy the values of "Use" to "newPatt", aka let newPattern be the new column
            Cut.add(IloNumVar(RollsUsed(1) + Fill(newPatt)));//add new column to the master problem
            system("pause");//pause to check out the lp files
        }

        cutOpt.add(IloConversion(env, Cut, ILOINT));//transfer numVar "Cut" into intVar

        cutSolver.solve();//solve the last master problem
        cout << "Solution status: " << cutSolver.getStatus() << endl;//output the solving status
        report3(cutSolver, Cut);
    }
    catch(IloException& ex){
        cerr << "Error: " << ex << endl;
    }
    catch(...){
        cerr << "Error" << endl;
    }

    env.end();
    return 0;
}
2.2.2.1 Analysis

代码来源于IBM CPLEX安装文件夹中的cutstock.cpp示例文件,根据自己的代码习惯和理解做了修改添加了注释。

该问题中,卷材只有一种标准长度,客户需求的长度可以有多种,数量不定。

2.2.2.2 Structure
Meaningcode
卷材标准长度rollWidth
需求数量amount
需求尺寸size
Master ProblemSubproblem
元素code类型code类型
模型cutOptIloModelpatGenIloModel
目标函数RollsUsedIloObjectiveReducedCostIloObjective
约束FillIloRangeArray直接添加
变量CutIloNumVarArray
IloIntVarArray
UseIloNumVarArray
求解算法cutSolverIloCplexpatSolverIloCplex

2.2.3 Problem Instance

rollWidth=115, size=[25, 40, 50, 55, 70], amount=[50, 36, 24, 8, 30].

Iteration 1

MP1
min ⁡ x 1 + x 2 + x 3 + x 4 + x 5 s.t. 4 x 1 > = 50 2 x 2 > = 36 2 x 3 > = 24 2 x 4 > = 8 x 5 > = 30 x i ≥ 0 i = { 1 , … , 5 } \min\quad x_1 + x_2 + x_3 + x_4 + x_5\\ \text{s.t.}\quad 4 x_1 >= 50\\ 2 x_2 >= 36\\ 2 x_3 >= 24\\ 2 x_4 >= 8\\ x_5 >= 30\\ x_i{\color{red}\ge0}\quad i=\{1,\dots,5\} minx1+x2+x3+x4+x5s.t.4x1>=502x2>=362x3>=242x4>=8x5>=30xi0i={1,,5}

Cut: [12.5, 18, 12, 4, 30]

dual(Fill): [0.25, 0.5, 0.5, 0.5, 1]

SP1
min ⁡ − 0.25 x 1 − 0.5 x 2 − 0.5 x 3 − 0.5 x 4 − x 5 + 1 s.t. 25 x 1 + 40 x 2 + 50 x 3 + 55 x 4 + 70 x 5 < = 115 x i ≥ 0 i = { 1 , … , 5 } \min\quad - 0.25 x_1 - 0.5 x_2 - 0.5 x_3 - 0.5 x_4 - x_5 + 1\\ \text{s.t.}\quad 25 x_1 + 40 x_2 + 50 x_3 + 55 x_4 + 70 x_5 <= 115\\ x_i\ge0 \quad i=\{1,\dots,5\} min0.25x10.5x20.5x30.5x4x5+1s.t.25x1+40x2+50x3+55x4+70x5<=115xi0i={1,,5}
Reduced cost = -0.5

Use: [0, 1, 0, 0, 1]

Iteration 2

MP2
min ⁡ x 1 + x 2 + x 3 + x 4 + x 5 + x 6 s.t. 4 x 1 > = 50 2 x 2 + x 6 > = 36 2 x 3 > = 24 2 x 4 > = 8 x 5 + x 6 > = 30 x i ≥ 0 i = { 1 , … , 6 } \min\quad x_1 + x_2 + x_3 + x_4 + x_5 + x_6\\ \text{s.t.}\quad 4 x_1 >= 50\\ 2 x_2 + x_6 >= 36\\ 2 x_3 >= 24\\ 2 x_4 >= 8\\ x_5 + x_6 >= 30\\ x_i\ge0\quad i=\{1,\dots,6\} minx1+x2+x3+x4+x5+x6s.t.4x1>=502x2+x6>=362x3>=242x4>=8x5+x6>=30xi0i={1,,6}
Cut: [12.5, 3, 12, 4, 0, 30]

dual(Fill): [0.25, 0.5, 0.5, 0.5, 0.5]

SP2
min ⁡ − 0.25 x 1 − 0.5 x 2 − 0.5 x 3 − 0.5 x 4 − 0.5 x 5 + 1 s.t. 25 x 1 + 40 x 2 + 50 x 3 + 55 x 4 + 70 x 5 < = 115 x i ≥ 0 i = { 1 , … , 5 } \min\quad - 0.25 x_1 - 0.5 x_2 - 0.5 x_3 - 0.5 x_4 - 0.5 x_5 + 1\\ \text{s.t.}\quad 25 x_1 + 40 x_2 + 50 x_3 + 55 x_4 + 70 x_5 <= 115\\ x_i\ge0 \quad i=\{1,\dots,5\} min0.25x10.5x20.5x30.5x40.5x5+1s.t.25x1+40x2+50x3+55x4+70x5<=115xi0i={1,,5}
Reduced cost = -0.25

Use: [1, 2, 0, 0, 0]

Iteration 3

MP3
min ⁡ x 1 + x 2 + x 3 + x 4 + x 5 + x 6 + x 7 s.t. 4 x 1 + x 7 > = 50 2 x 2 + x 6 + 2 x 7 > = 36 2 x 3 > = 24 2 x 4 > = 8 x 5 + x 6 > = 30 x i ≥ 0 i = { 1 , … , 7 } \min\quad x_1 + x_2 + x_3 + x_4 + x_5 + x_6 + x_7\\ \text{s.t.}\quad 4 x_1 +x_7 >= 50\\ 2 x_2 + x_6 + 2 x_7 >= 36\\ 2 x_3 >= 24\\ 2 x_4 >= 8\\ x_5 + x_6 >= 30\\ x_i\ge0\quad i=\{1,\dots,7\} minx1+x2+x3+x4+x5+x6+x7s.t.4x1+x7>=502x2+x6+2x7>=362x3>=242x4>=8x5+x6>=30xi0i={1,,7}
Cut: [11.75, 0, 12, 4, 0, 30, 3]

dual(Fill): [0.25, 0.375, 0.5, 0.5, 0.625]

SP3
min ⁡ − 0.25 x 1 − 0.375 x 2 − 0.5 x 3 − 0.5 x 4 − 0.625 x 5 + 1 s.t. 25 x 1 + 40 x 2 + 50 x 3 + 55 x 4 + 70 x 5 < = 115 x i ≥ 0 i = { 1 , … , 5 } \min\quad - 0.25 x_1 - 0.375 x_2 - 0.5 x_3 - 0.5 x_4 - 0.625 x_5 + 1\\ \text{s.t.}\quad 25 x_1 + 40 x_2 + 50 x_3 + 55 x_4 + 70 x_5 <= 115\\ x_i\ge0 \quad i=\{1,\dots,5\} min0.25x10.375x20.5x30.5x40.625x5+1s.t.25x1+40x2+50x3+55x4+70x5<=115xi0i={1,,5}
Reduced cost = -0.125

Use: [3, 1, 0, 0, 0]

Iteration 4

MP4
min ⁡ x 1 + x 2 + x 3 + x 4 + x 5 + x 6 + x 7 + x 8 s.t. 4 x 1 + x 7 + 3 x 8 > = 50 2 x 2 + x 6 + 2 x 7 + x 8 > = 36 2 x 3 > = 24 2 x 4 > = 8 x 5 + x 6 > = 30 x i ≥ 0 i = { 1 , … , 8 } \min\quad x_1 + x_2 + x_3 + x_4 + x_5 + x_6 + x_7 + x_8\\ \text{s.t.}\quad 4 x_1 +x_7 + 3 x_8 >= 50\\ 2 x_2 + x_6 + 2 x_7 + x_8 >= 36\\ 2 x_3 >= 24\\ 2 x_4 >= 8\\ x_5 + x_6 >= 30\\ x_i\ge0\quad i=\{1,\dots,8\} minx1+x2+x3+x4+x5+x6+x7+x8s.t.4x1+x7+3x8>=502x2+x6+2x7+x8>=362x3>=242x4>=8x5+x6>=30xi0i={1,,8}
Cut: [8, 0, 12, 4, 0, 30, 0, 6]

2.3 Multiple standard width

2.3.1 Codes

/*cplex header file*/
#include <ilcplex/ilocplex.h>

/*using name space for cplex program*/
ILOSTLBEGIN

typedef IloArray<IloModel> IloModelArray; // model array container
typedef IloArray<IloObjective> IloObjectiveArray; // objective array container
typedef IloArray<IloCplex> IloCplexArray; // Cplex array container
typedef IloArray<IloNumVarArray> NumVarMatrix2; // 2-d float variables container

#define RC_EPS 1.0e-6

static void readData(const char* filename, IloNumArray& rollWidth, IloNumArray& rollCost, IloNumArray& size, IloNumArray& amount){
    ifstream in(filename);
    if(in){
        in >> rollWidth;//input the standard width of the steel roll
        in >> rollCost;//input the costs of rolls
        in >> size;//input the size demand of cilents
        in >> amount;//input the amount demand of cilents
    }
    else{//in case file dose not exist
        cerr << "No such file: " << filename << endl;
        exit(0);
    }
}

/*report for the master problem*/
static void report_MP(IloCplex& costSolver, IloNumVarArray Cut, IloRangeArray Fill){
    cout << endl;
    cout << "The total costs of used rolls: " << costSolver.getObjective() << endl; //output the total costs of used rolls
    cout << endl;

    /*output the number of each pattern, aka x_j*/
    for (IloInt j = 0; j < Cut.getSize();j++){
        cout << " Cut" << j << " = " << costSolver.getValue(Cut[j]) << endl;
    }
    cout << endl;
    /*output the shadow price of each pattern*/
    for (IloInt i = 0;i<Fill.getSize();i++){
        cout << " Fill" << i << " = " << costSolver.getDual(Fill[i]) << endl;
    }
    cout << endl;
}

/*report for the subproblems*/
static void report_SP(IloAlgorithm& patSolver, IloNumVarArray Use, IloObjective obj){
    cout << endl;
    cout << "Reduced cost is " << patSolver.getValue(obj) << endl;//output the reduced cost of the pattern
    cout << endl;

    /*output the cofficients of each size (aka A_i) in the pattern*/
    if(patSolver.getValue(obj) >= -RC_EPS){
        for (IloInt i = 0; i < Use.getSize(); i++) {
            cout << " Use " << i << " = " << patSolver.getValue(Use[i]) << endl;
        }
        cout << endl;
    }
}

/*report for the final master problem*/
static void report_FP(IloCplex& cutSolver, IloNumVarArray Cut){
    cout << endl;
    cout << "Best integer solution costs: " << cutSolver.getObjValue() << endl;//output the final total number of used rolls
    cout << endl;

    /*output the number of each pattern, aka x_j*/
    for (IloInt j = 0; j < Cut.getSize();j++){
        cout << " Cut" << j << " = " << cutSolver.getValue(Cut[j]) << endl;
    }
}

/*main program*/
int main(){
    /*---------------------------------Timer Starter---------------------------------*/
    clock_t startTime, endTime;
    startTime = clock();
    IloEnv env;//declare the cplex environment

    /*try to establish the model and slove it*/
    try{
        IloInt i, j;//i for size and j for cutting pattern

        IloNumArray rollWidth(env);//the standard width of rolls
        IloNumArray rollCost(env);//the standard cost of rolls
        IloNumArray amount(env);//the wanted amount of cilents
        IloNumArray size(env);//the wanted size of cilents

        readData("mcutstock.dat", rollWidth, rollCost, size, amount);//input the data of the problem instance
        //system("pause");//check out the data

        /*cutting-optimization problem, aka master problem*/
        IloModel costOpt(env);//declare the model for the master problem
        IloObjective RollsUsed = IloAdd(costOpt, IloMinimize(env));//add objective to model costOpt
        IloRangeArray Fill = IloAdd(costOpt, IloRangeArray(env, amount, IloInfinity));//add constraints to model costOpt, amount is the lower bound and Ilofinity is the upper bound
        IloNumVarArray Cut(env); //declare decision variables for the master problem, aka x_j; Note that the variables are initialized with numVar
        /*initial cutting pattern*/
        for (j = 0; j < size.getSize();j++){
            /*
            add the coefficients of variables by column;
            "RollsUsed(rollCost[j])"" means the coefficient of the varibale is rollCost[i];
            "Fill[j](int(rollWidth / size[j]))" means the coefficient of the variable in the j-th constraint is int(rollWidth / size[j]);
            this program cuts a standard roll with single size as initial solution. 
            */
            Cut.add(IloNumVar(RollsUsed(rollCost[0]) + Fill[j](int(rollWidth[0] / size[j]))));
        }
        IloCplex costSolver(costOpt);//declare the solver for the master problem

        /*pattern-generation problems, aka subproblems*/
        IloModelArray patGen(env, rollWidth.getSize());
        IloObjectiveArray ReducedCost(env, rollWidth.getSize());
        IloCplexArray patSolver(env, rollWidth.getSize());
        NumVarMatrix2 Use(env, rollWidth.getSize());

        for (i = 0; i < rollWidth.getSize();i++){
            patGen[i] = IloModel(env);//declare the model for subproblems
            ReducedCost[i] = IloAdd(patGen[i], IloMaximize(env, -rollCost[i]));//"IloMinimize(env, -rollCost[i])" means the objective function initialize with a constant -rollCost[i], -rollCost[i] is the coefficient of the variable in the objective function
            Use[i] = IloNumVarArray(env, size.getSize());
            for (j = 0; j < size.getSize();j++){
                Use[i][j]=IloNumVar(env, 0.0, IloInfinity, ILOINT); //declare integer decision variables for the subproblems, aka A_i
            }
            patGen[i].add(IloScalProd(size, Use[i]) <= rollWidth[i]);//(product) add constraints
            patSolver[i] = IloCplex(patGen[i]);//declare the solver for subproblems
        }

        /*column-generation procedure*/
        IloNumArray price(env, size.getSize());//declare container for the shadow price, aka the cofficients of objective function for subproblems
        IloNumArray newPatt(env, size.getSize());//declare the container for new column
        IloBool Z;
        IloInt iter = 0;
        IloInt index;
        IloNum max;

        for (;;){
            Z = 0;
            iter++;
            cout << "Iteration " << iter << "......" << endl;
            /*optimize the current pattern, aka solve the current master problem*/
            cout << "Solving Master Problem..." << endl;
            costSolver.solve();//solve the master proble
            costSolver.exportModel("MP.lp");//export the model as lp file
            report_MP(costSolver, Cut, Fill);

            /*find and add a new pattern*/
            for (i = 0; i < size.getSize(); i++)
            {
                price[i] = costSolver.getDual(Fill[i]);
            }

            for (i = 0; i < rollWidth.getSize();i++){
            ReducedCost[i].setLinearCoefs(Use[i], price);//set the linear coefficients of objective function for subproblems

            cout << "Solving Subproblem " << i + 1 << "..." << endl;
            patSolver[i].solve();//solve the subproblem
            patSolver[i].exportModel("SP.lp");
            report_SP(patSolver[i], Use[i], ReducedCost[i]);
            //system("pause");//pause to check out the lp files
            }

            /*if no redeced cost of the patterns is postive, stop searching for columns*/
            for (i = 0; i < rollWidth.getSize();i++){
                if(patSolver[i].getValue(ReducedCost[i]) >= RC_EPS){
                    Z = 1;
                }
            }

            index = 0;
            max = patSolver[0].getValue(ReducedCost[0]);
            if(rollWidth.getSize() > 1){
            for (i = 1; i < rollWidth.getSize();i++){
                if(patSolver[i].getValue(ReducedCost[i])>max){
                    max = patSolver[i].getValue(ReducedCost[i]);
                    index = i;
                }
            }
            }

            if (!Z){
                break;
            }

            patSolver[index].getValues(newPatt, Use[index]);//copy the values of "Use" to "newPatt", aka let newPattern be the new column
            Cut.add(IloNumVar(RollsUsed(rollCost[index]) + Fill(newPatt)));//add new column to the master problem
            //system("pause");//pause to check out the lp files
        }

        costOpt.add(IloConversion(env, Cut, ILOINT));//transfer numVar "Cut" into intVar
        costSolver.solve();//solve the last master problem
        cout << "Solution status: " << costSolver.getStatus() << endl;//output the solving status
        report_FP(costSolver, Cut);

    }
    catch(IloException& ex){
        cerr << "Error: " << ex << endl;
    }
    catch(...){
        cerr << "Error" << endl;
    }

    env.end();

    /*---------------------------------Timer end---------------------------------*/
    endTime = clock();
    cout << endl;
    cout << "Totle Time : " << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
    
    return 0;
}

2.3.2 Analysis

该问题中,卷材只有多种标准长度,客户需求的长度可以有多种,数量不定。目标函数为所有卷材的成本(卷材单位成本为rollCost,要计算所用卷材数量的话,可将所有卷材成本设为1)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aidenlazz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值