热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

混合整数非线性规划_OM|二维剪板机下料问题(2DGCSP)的混合整数规划精确求解——数学规划的计算智能特征...

作者:路军编者按混合整数规划(MIP),是指同时包含整数变量和连续变量的数学规划。针对二维剪板机下料问题等大规模组合问题,混合整数规划是一
52882e0134a3ad0100a86cc61fa5bcde.png

作者:路军

编者按

混合整数规划(MIP),是指同时包含整数变量和连续变量的数学规划。针对二维剪板机下料问题等大规模组合问题,混合整数规划是一种速度快,精度高的求解途径,上较其他方法具有明显优势。本文介绍了二维剪板机下料问题(2D-GCSP)的混合整数规划模型并基于BWMV bin packing数据库进行模型的实现,生成排样图,为数据库中的案例提出解决方案。

二维剪板机下料问题(2D-GCSP) 的混合整数规划是最优美的整数规划模型之一。以往很多人认为像2D-GCSP这样的问题由于本质上的递归性,很难建立成混合整数规划模型,但是持这种观点的人忽略了数学规划的智能特征。所谓计算智能是如果将问题描述给机器,则机器可以自行得到问题的结果。例如很多元启发算法都被称为计算智能算法,就是因为他们好像能够通用性地解决一些问题。这也给我们一个启示,就是不怕问题刁钻,只要我们把问题的特征用建模逻辑描述出来,那么它就是合理的数学规划模型,就能为我们工作。

2D-GCSP二维剪板机下料问题

2D-GCSP中的G来自法文Guillotine。约瑟夫·吉约坦( Joseph Guillotin )是法国一名信仰人文主义的医生,他提出认为死刑应以快速及人道的方式执行以降低死刑的痛苦。1792年,吉约坦医生与一位法国刽子手合作,动手制造了一台断头台的原型机,从此新型断头台便正式面世,并以他的姓氏Guillotine命名 。为了纪念这位医生,以后凡是瞬间切断东西的方式都被命名为Guillotine。在航母、飞机、建筑等制造工业上经常会使用一种剪板机对金属板材进行切割下料,每次必须横向或纵向切断整个板材,下料图案十分独特,无论多少次切割,就是你总能找到一刀切位置。下图是剪板机。

3aa9fe48b23212697e5bc1f6fbc59258.png
e590aa077425863e92267fe016ac1492.png

问题数据

附件一是BWMV bin packing数据库中第40个例子。第一行表示例子编号,第二行30,30表示原始板材大小,80表示总计下料需求板材的种类数。第三行开始是需求定义,例如第三行表示需要5X8的小板材1件,第四行表示需要8X8的小板材1件。所有板材必须从WXH=30X30的大板材上G方式剪裁下来,目标是规划下料方式以极小化所需要的大板材数量。对原始数据,用以下符号表示:W-原始板材宽度,H-原始板材高度,n-需求小板材数量,ai, bi-第i类小板材宽度和高度,mi-第i类小板材的需求数量。

整数规划模型

Silva等[1]首次给出G-下料的整数规划模型,此模型描述真实下料时的逻辑。如下图所示,工人在下料时候要依照给出的需求下料,那么下料后的余料被放起来准备之后的小板材下料。于是除了原始板材外,随着下料的进展,待切割板材会堆积成板材库。那么逻辑是要保证某类待切割的板材数量大于从该类下料小板材的数量。

用xij表示第i种需求小板材从第j种余料(包括原始板材)中下料的数量。用0-1数值矩阵aijk表示从j类余料中下料i类小板擦会产生一件余料k,于是根据逻辑(即工人始终能够找到需要的余料,或者说余料的数量大于将要在该类余料进行下料的数量):

4816966ef38c2da0a4eaf0c9ec738f12.png

这里的m是所有的余料种类,j=0表示原始板材(即W×H板材)。

另外,下料需求需要被满足:

562a165009dab205a10f108910ab23a8.png

目标当然是原始板材使用数量最少:

394308e864599f1ee45753e7c0a81038.png

下图是根本逻辑:从余料j(j=0表示原始板材)上下料i会产生两块 新生的余料备用。生成何种余料用三维0-1常数矩阵aijk。约束(1)表示一定要保证有足够的余料可用于需求小板材的下料。

f3f2bf21882bd5a935e44844b3315362.png

至于如何生成aijk, Silva等[1]建议首先做枚举。本文以下直接将这个枚举写进模型里!

模型实现

上面的模型(1)-(3)比较抽象,现在把它具体化,并把对aijk的枚举写进模型。

首先,对余料从0到M的编码抽象性太强,这里把他们具体化为板材的长度和宽度,即使用x[w][h][i]表示从大小为wXh余料上切割第i类小板材的数量。于是目标是极小化从WxH板材(原始板材)上切割出所有小板材的数量,即:

6c69df1de2e41223025402f3a0496061.png

也就是使用(4)代替上面的(3)。

其次必须满足所有需求:

90b8b18a8561a7a1b501b34d721c6f59.png

也就是用(5)取代上面的(2)。

最后用下面的(6)取代上面的(1):

846e52375499b8e1d6fdab38ed2d1965.png

这个看起来复杂,仔细分析会发现跟上面的(1)是一丘之貉,不过好处是扔掉了aijk。

整体模型看起来是这个样子的(数据区采用小案例,节省空间):

min sum{i=1,...,n}x[W][H][i] //(4)
subject tosum{w&#61;1,...,W;h&#61;1,...,H;w>&#61;a[i];h>&#61;b[i]}x[w][h][i] >&#61;m[i]|i&#61;1,...,n //(5)sum{w&#61;1,...,W;h&#61;1,...,H;i&#61;1,...,n;w>&#61;a[i];h>&#61;b[i];w&#61;&#61;(a[i]&#43;w1);b[i]&#61;&#61;h1}x[w][h][i]&#43;-->sum{w&#61;1,...,W;h&#61;1,...,H;i&#61;1,...,n;w>&#61;a[i];h>&#61;b[i];h&#61;&#61;(b[i]&#43;h1);w1&#61;&#61;w}x[w][h][i]-->>&#61;sum{i&#61;1,...,n;w1>&#61;a[i];h1>&#61;b[i]}x[w1][h1][i]-->| w1&#61;1,...,W;h1&#61;1,...H;(h1&#43;w1)<(H&#43;W) //(6)
wheren is an integerW,H are numbersdt is a set of numbera[i],b[i],m[i] are numbers|i&#61;1,...,nx[w][h][i] is a variable of nonnegative integer|w&#61;1,...,W;h&#61;1,...,H;i&#61;1,...,n
data_relationn&#61;_$(dt)/3a[i]&#61;dt[3(i-1)&#43;1] | i&#61;1,...,nb[i]&#61;dt[3(i-1)&#43;2] | i&#61;1,...,nm[i]&#61;dt[3i] | i&#61;1,...,n
dataW&#61;10H&#61;10dt&#61;{
2 1
6 1
10 1
1 1
8 1
3 1
1 1
1 1
6 1
1 1
4 1
9 1
1 1
9 1
4 1
2 1
3 1
9 1
4 1
9 1}

上面的模型用&#43;Leapms解析并用cplex命令调用cplex求解即可秒解。

下料排样图的生成

上面的模型只给出x[w][h][i]的最优值&#xff0c;即从wxh余料中切割第i类小板材的具体数量&#xff0c;由此信息得到排样图是另外一个挑战。首先看结果样子&#xff08;以上面80小板材数据为例&#xff09;&#xff1a;

f6a4084151ad52c44c66c0cb3ed81943.png

而后看技术。这里技术包括用&#43;Leampms编程接口&#xff0c;还有细致的逻辑思考&#xff0c;这里不再赘述&#xff0c;只贴出c&#43;&#43;代码&#xff0c;此源码能够一次求出文件中500个案例&#xff1a;

#include
#include
using namespace std;extern int W,H,n;
extern int w[1000],h[1000],m[1000];
extern ofstream ofcad;
extern int nInstance;class c_sheet{
public:int w,h; //尺寸int n,nl; //数量&#xff0c;已定位的数量int x[1000],y[1000]; //坐标int item[1000]; //下料bool good;c_sheet(){w&#61;W;h&#61;H;n&#61;nl&#61;0;}c_sheet(int w,int h){this->w&#61;w;this->h&#61;h;n&#61;0;good&#61;false;}void reset(){w&#61;h&#61;n&#61;nl&#61;0;good&#61;false;}void addItem(int item);void print();void draw(ofstream &off);bool cut();bool cut(int x, int y);
};int prepare(int instance&#61;0);//准备模型
int findSheet(int w, int h);
extern int nSheet;
extern c_sheet sheet[1000];
#include
#include
using namespace std;extern int W,H,n;
extern int w[1000],h[1000],m[1000];
extern ofstream ofcad;
extern int nInstance;class c_sheet{
public:int w,h; //尺寸int n,nl; //数量&#xff0c;已定位的数量int x[1000],y[1000]; //坐标int item[1000]; //下料bool good;c_sheet(){w&#61;W;h&#61;H;n&#61;nl&#61;0;}c_sheet(int w,int h){this->w&#61;w;this->h&#61;h;n&#61;0;good&#61;false;}void reset(){w&#61;h&#61;n&#61;nl&#61;0;good&#61;false;}void addItem(int item);void print();void draw(ofstream &off);bool cut();bool cut(int x, int y);
};int prepare(int instance&#61;0);//准备模型
int findSheet(int w, int h);
extern int nSheet;
extern c_sheet s

#include
#include
#include "leap.h"
#include "Guillotine.h"
using namespace std;int W,H,n;
int w[1000],h[1000],m[1000];
ofstream ofcad;void addSheet(int w, int h, int item, int ns);
int dealInstance();//主函数&#xff0c;循环计算所有500案例
int main(){while(1){if(dealInstance()<0)break;}return 0;
}int dealInstance(){//设置时间char time[]&#61;"600";//准备模型if(prepare()<0)return -1;//if(nInstance<41)return 0;//求解模型c_leap lp;lp.loadLeap("current.leap");lp.cplex("",time,"");//生成板材int ns;for(int w&#61;W;w>0;w--){for(int h&#61;H;h>0;h--){for(int i&#61;0;i0){addSheet(w,h,i,ns);}}}}//剪切板材if(nSheet>1)sheet[0].cut();//输出板材图形char cadfname[64];if(lp.getState()!&#61;IP_OPTIMAL)sprintf_s(cadfname,64,"graphic(%d)(%d_%d)!.scr",nInstance,(int)((nInstance-1)/50)&#43;1,nInstance-(int)((nInstance-1)/50)*50);else sprintf_s(cadfname,64,"graphic(%d)(%d_%d).scr",nInstance,(int)((nInstance-1)/50)&#43;1,nInstance-(int)((nInstance-1)/50)*50);ofcad.open(cadfname);ofcad<<"erase all "<}void addSheet(int w, int h, int item, int ns){int sht&#61;findSheet(w,h);if(sht<0){sht&#61;nSheet&#43;&#43;;sheet[sht].w&#61;w;sheet[sht].h&#61;h;}for(int k&#61;0;k}

#include
#include
#include "leap.h"
#include "Guillotine.h"
using namespace std;ifstream iffd;//原始数据文件句柄
bool iffdOpen&#61;false;
int nInstance&#61;0;//案例编号int prepare(int instance){ //准备模型if(nInstance>500)return -1;//如果没有打开原始数据文件&#xff0c;打开之&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;const char iffdname[]&#61;"BWMV bin packing instances.txt";if(!iffdOpen){int ntmp;iffd.open(iffdname);iffd>>ntmp;cout<<"ntmp&#61;"<>dummy;iffd>>W>>H>>n;off<<"tW&#61;"<>w[i]>>h[i]>>m[i];off<<"tt"<}
#include
#include
#include "Guillotine.h"int nSheet&#61;0;
c_sheet sheet[1000];int findSheet(int w,int h){for(int i&#61;0;i}void c_sheet::addItem(int item){this->item[n]&#61;item;n&#43;&#43;;
}void c_sheet::print(){cout<<"Sheet"<}bool c_sheet::cut(){bool rt&#61;true;int w1,h1,w2,h2,s1,s2;if(w&#61;&#61;W&&h&#61;&#61;H){nl&#61;n;for(int i&#61;0;i<&#61;n;i&#43;&#43;){x[i]&#61;(int)(i*1.2*W&#43;.5);y[i]&#61;0;w1&#61;w-::w[item[i]];h1&#61;::h[item[i]];s1&#61;findSheet(w1,h1);if(s1>&#61;0)sheet[s1].cut(x[i]&#43;::w[item[i]],y[i]);w2&#61;w;h2&#61;h-::h[item[i]];s2&#61;findSheet(w2,h2);if(s2>&#61;0)sheet[s2].cut(x[i],y[i]&#43;::h[item[i]]);}}return rt;
}bool c_sheet::cut(int xx, int yy){if(nl&#61;&#61;n)return false;int w1,h1,w2,h2,s1,s2;x[nl]&#61;xx;y[nl]&#61;yy;w1&#61;w-::w[item[nl]];h1&#61;::h[item[nl]];s1&#61;findSheet(w1,h1);if(s1>&#61;0)sheet[s1].cut(xx&#43;::w[item[nl]],yy);w2&#61;w;h2&#61;h-::h[item[nl]];s2&#61;findSheet(w2,h2);if(s2>&#61;0)sheet[s2].cut(xx,yy&#43;::h[item[nl]]);nl&#43;&#43;;return true;
}void c_sheet::draw(ofstream &off){bool hangle&#61;true;for(int i&#61;0;i}

原始数据文件见附件二

附件二.txt

参考文献&#xff1a;

[1] Silva E , Alvelos F , J.M. Valério de Carvalho. An integer programming model for two- and three-stage two-dimensional cutting stock problems[J]. European Journal of Operational Research, 2010, 205(3):



推荐阅读
  • 题目描述:给定n个半开区间[a, b),要求使用两个互不重叠的记录器,求最多可以记录多少个区间。解决方案采用贪心算法,通过排序和遍历实现最优解。 ... [详细]
  • 题目Link题目学习link1题目学习link2题目学习link3%%%受益匪浅!-----&# ... [详细]
  • 本文探讨了《魔兽世界》中红蓝两方阵营在备战阶段的策略与实现方法,通过代码展示了双方如何根据资源和兵种特性进行战士生产。 ... [详细]
  • 文件描述符、文件句柄与打开文件之间的关联解析
    本文详细探讨了文件描述符、文件句柄和打开文件之间的关系,通过具体示例解释了它们在操作系统中的作用及其相互影响。 ... [详细]
  • Codeforces Round #566 (Div. 2) A~F个人题解
    Dashboard-CodeforcesRound#566(Div.2)-CodeforcesA.FillingShapes题意:给你一个的表格,你 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • C++实现经典排序算法
    本文详细介绍了七种经典的排序算法及其性能分析。每种算法的平均、最坏和最好情况的时间复杂度、辅助空间需求以及稳定性都被列出,帮助读者全面了解这些排序方法的特点。 ... [详细]
  • C++: 实现基于类的四面体体积计算
    本文介绍如何使用C++编程语言,通过定义类和方法来计算由四个三维坐标点构成的四面体体积。文中详细解释了四面体体积的数学公式,并提供了两种不同的实现方式。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • This document outlines the recommended naming conventions for HTML attributes in Fast Components, focusing on readability and consistency with existing standards. ... [详细]
  • 本文探讨了 C++ 中普通数组和标准库类型 vector 的初始化方法。普通数组具有固定长度,而 vector 是一种可扩展的容器,允许动态调整大小。文章详细介绍了不同初始化方式及其应用场景,并提供了代码示例以加深理解。 ... [详细]
  • 本文详细探讨了VxWorks操作系统中双向链表和环形缓冲区的实现原理及使用方法,通过具体示例代码加深理解。 ... [详细]
  • 本题涉及一棵由N个节点组成的树(共有N-1条边),初始时所有节点均为白色。题目要求处理两种操作:一是改变某个节点的颜色(从白变黑或从黑变白);二是查询从根节点到指定节点路径上的第一个黑色节点,若无则输出-1。 ... [详细]
  • Linux设备驱动程序:异步时间操作与调度机制
    本文介绍了Linux内核中的几种异步延迟操作方法,包括内核定时器、tasklet机制和工作队列。这些机制允许在未来的某个时间点执行任务,而无需阻塞当前线程,从而提高系统的响应性和效率。 ... [详细]
author-avatar
Mr_ZERO0000000
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有