禁忌搜索算法求解作业车间调度问题(附Java代码)

作业车间调度问题

问题描述

一个加工系统共有m台机器,需要加工n个加工顺序不同的工件。

已知:
(1) P={p1,p2,,pn},pii,i=1,2,,n,工件集P= { p_1,p_2,…,p_n } ,其中p_i 为第i个工件,i=1,2,…,n,;
(2) M={m1,m2,,mm},mjj,j=1,2,,m机器集M={m_1,m_2,…,m_m},m_j为第j号机器,j=1,2,…,m;
(3) OP={op1,op2,,opn},opi={opi1,opi2,,opim}piopikik使,opik=0pik,k=1,2,,m;工序集OP={op_1,op_2,…,op_n},op_i={op_{i1},op_{i2},…,op_{im}}为工件p_i的工序序列。op_{ik}为第i个工件的第k道工序使用的机器号,op_{ik}=0表示工件p_i在第k道工序不加工,k=1,2,…,m;
(4) 使T={tij},tijipi使jtij=0pi使j每个工件使用每台机器的时间矩阵T={t_{ij}},t_{ij}为第i个工件p_i使用第j台机器的时间。t_{ij}=0表示工件p_i不使用机器j。

JSP需要满足下列约束条件:
(1) 每个工件使用每台机器不多于1次;
(2) 每个工件利用每台机器的顺序可以不同,即可以有opiopd(id)op_i≠op_d(i≠d);
(3) 每个工件的工序必须依次加工,后工序不能先于前工序;
(4) 任何工件没有抢先加工的优先权,应服从任何生产顺序;
(5) 工件加工过程中没有新工件加入,也不临时取消工件的加工。

调度目标通常是最小化最大完工时间,即min{max{ci}},i=1,2,,nmin{max{c_i}},i=1,2,…,n。式中cic_i表示工件ii的完工时间。
对于以上描述的调度问题,调度算法的任务就是在许可的计算时间内得到最优或是较优的加工顺序。

问题模型

(i,j)(i,j)表示作业ii的第jj个工序。SijS_{ij}TijT_{ij}分别表示(i,j)(i,j)的加工起始时刻和加工时间。ZijkZ_{ijk}表示(i,j)(i,j)是否在第kk台机器上加工:如果(i,j)(i,j)在第kk台机器上加工,Zijk=1Z_{ijk}=1;否则,Zijk=0Z_{ijk}=0CkC_k为第kk台机器的完工时间,则问题的数学模型如下:
在这里插入图片描述
公式(1)为目标函数,即优化目标,系统中使用总加工时间最短为优化目标。公式(2)表示1个作业只能在加工完成前一道工序后才可以加工后一道工序。公式(3)表示1个作业的第1道工序的起始加工时刻大于或等于0。公式(4)表示在1台机床上不会同时加工1个以上的作业。

举个栗子

假如此时有3个工件需要再3台机器上加工,不同工件所需的加工工序及加工时间可以用以下公式表示:
Job0=[(0,3),(1,2),(2,2)]Job_0=[(0,3),(1,2),(2,2)]
Job1=[(0,2),(2,1),(1,4)]Job_1=[(0,2),(2,1),(1,4)]
Job2=[(1,4),(2,3)]Job_2=[(1,4),(2,3)]
在这个例子中,作业job0job_0有3道工序:它的第1道工序上标注有(0,3),表示第1道工序必须在第0台机器上进行加工,且需要3个单位的加工时间;它的第2道工序上标注有(1,2),其表示第2道工序必须在第1台机器上进行加工,且需要2个单位的加工时间;余下的同理。总的来说,这个实例中共有8道工序。
下图用甘特图表示了一种可行解:
在这里插入图片描述
该解所需的最长时间为11工时。

禁忌搜索与JSP

有关禁忌搜索算法的内容,公众号内有详细教程:
干货 |【算法】禁忌搜索算法(Tabu Search,TS)超详细通俗解析附C++代码实例
干货 | 到底是什么算法,能让人们如此绝望?
禁忌搜索算法求解带时间窗的车辆路径规划问题详解(附Java代码)
大家可以点击超链接回顾相关知识。

一般而言,用禁忌搜索算法解决问题时,需要注意的点无非就是以下几个:
初始解的生成;
禁忌对象的选择;
邻域动作算子的选择。

我们简单介绍代码中使用的算子:
在这里插入图片描述
Neighbor1类:对同一机器上的两道相邻工序o1,o2o_1,o_2,交换两道工序的前后顺序。例如交换图例中machine0machine_0job0job_0job1job_1的顺序。交换后,machine0machine_0上的加工顺序为job0,job1{job_0,job_1}
NeighborA类:对同一机器上的三道不同的工序o1,o2,o3o_1,o_2,o_3,满足o1o_1o2o_2相邻,或o2o_2o3o_3相邻。找到最早开始加工的工件的位置kk,按o3,o2,o1o_3,o_2,o_1的顺序加入kk处。例如图例中machine1machine_1的三道工序,交换后的加工顺序为job0,job1,job2{job_0,job_1,job_2}
禁忌表为工序总数*工序总数大小的二维表。对Neighbor1,禁忌边o1,o2(o_1,o_2);对NeighborA,禁忌边(o1,o3),(o2,o3)(o_1,o_3),(o_2,o_3)
如果所有边都被禁忌,随机选择某一组工序进行变换。
若执行邻域动作后的新解优于历史最优解,则不会被禁忌表禁忌。

代码展示

代码是github上的开源代码,作者是Thiebout Dewitte。我们简单介绍一下代码,感兴趣的朋友可以进入公众号下载代码(附小编的详细注解哦!)。

输入部分

输入算例格式如下:
在这里插入图片描述
第一行为注释部分。
第二行数字分别为工件数、机器数。

输出部分

运行代码时,可以多种运行方式:
在这里插入图片描述
在Main.java文件内选择所需运行模式,算例设置也在同一文件中。

测试单一算例:使用opendeurdagKulak()方法。将测试算例路径放入Main.java中:在这里插入图片描述
测试算例附带在代码内。
结果生成在编译器内部:
在这里插入图片描述
前三行按照机器顺序排列,cost表示总耗时,最后一行表示最长耗时的加工顺序。

测试多个算例,分别生成table1、2:
在这里插入图片描述
在这里插入图片描述
在上方输入算例所在文件夹,下方输入输出部分文件名。
table输出可放置在LaTeX环境中,在此就不展示了。

代码下载

具体代码比较长,就不在此展示了,有兴趣的朋友可以在公众号【程序猿声】内输入【JSPTS】不带【】,即可下载对应Java代码。
欢迎关注公众号,收看更多干货运筹学算法知识及有趣内容!
在这里插入图片描述

原文地址:https://www.cnblogs.com/zll-hust/p/13288823.html