柔性车间作业调度(Flexible Job-shop Scheduling Problem, FJSP)
1. 前言
1.1 车间作业调度问题描述
调度问题的一般性定义为:在一定的约束条件下,把有限的资源在时间上分配给若干个任务,以满足或优化一个或多个性能指标。
n
n
n 个工件
[
J
1
,
J
2
,
.
.
.
,
J
n
]
[J_1,J_2,...,J_n]
[J1,J2,...,Jn] 要在
m
m
m 台机器
[
M
1
,
M
2
,
.
.
.
,
M
m
]
[M_1,M_2,...,M_m]
[M1,M2,...,Mm] 上加工。每个工件包含一道或多道工序,工序顺序是预先确定的,每道工序可以在多台不同加工机器上进行加工,工序的加工时间随加工机器的不同而不同。
调度目标是为每道工序选择最合适的机器、确定每台机器上各个工序的最佳加工顺序以及开工时间,使整个系统的某些性能指标达到最优。
1.2 车间调度问题的分类
- 单机调度问题 加工系统中只有一台机床,代加工的工件有且仅有一道工序,所有工件都在该机床上进行加工。
- 并行机调度问题 加工系统中有多个完全相同的机床,每个工件只有一道工序,工件可以在任意一台机床上加工。
- 开放车间调度问题 每个工件的工序之间的加工顺序是任意的。工件的加工路线可以在任何一道工序开始,在任何一道工序结束。工件的加工没有特定的技术路线约束,各个工件之间没有先后关系约束。
- 流水车间调度 加工系统中有一组功能不同的机床,待加工的工件包含多道工序,每道工序在一台机床上加工,所有工件的加工路线相同,每个工件的工序之间有先后顺序约束。
- 作业车间调度问题 加工系统中有一组功能不同的机床,待加工的工件包含多道工序,每道工序在一台机床上加工,工件的加工路线互不相同,每个工件的工序之间有先后顺序约束。
柔性作业车间调度问题也是本文介绍的重点内容。
柔性,指的是供应链根据需求的变化,快速做出反应的能力。
1.3 调度问题的解决算法
2. 柔性作业车间调度问题(FJSP)描述
柔性作业车间调度问题包含两个子问题:
- 确定各工件的加工机器机器选择子问题
- 确定各个机器上的加工先后顺序工序排序子问题
柔性作业车间调度问题根据资源选择条件的不同,柔性程度可以分为完全柔性作业车间调度问题(T-FJSP,工序可选择的加工机器为所有候选机器)和部分柔性作业车间调度问题(P-FJSP,工序可选择的加工机器为部分候选机器)。具体示例如下:
2.1 问题背景
柔性作业车间调度问题的描述如下:
一个加工系统有 m m m 台机器,要加工 n n n 种工件。每个工件包含一道或多道工序,工件的工序顺序是预先确定的;每道工序可以在多台不同的机床上加工,工序的加工时间随机床的性能不同而变化。调度目标是为每道工序选择最合适的机器、确定每台机器上各工件工序的最佳加工顺序及开工时间,使系统的某些性能指标达到最优。
2.2 问题假设
- 同一台机器同一时刻只能加工一个工件;
- 同一工件的同一道工序在同一时刻只能被一台机器加工;
- 每个工件的每道工序一旦开始加工不能中断;
- 不同工件之间具有相同的优先级;
- 不同工件的工序之间没有先后约束;
- 同一工件的工序之间有先后约束;
- 所有工件在零时刻都可以被加工。
2.3 问题模型
为了后文描述方便,定义以下符号:
符号 | 说明 |
---|---|
n n n | 工件总数 |
m m m | 机器总数 |
Ω \Omega Ω | 总的机器集 |
i , e i,e i,e | 机器序号, i , e = 1 , 2 , 3 , . . , m i,e=1,2,3,..,m i,e=1,2,3,..,m |
j , k j,k j,k | 工件序号, j , k = 1 , 2 , 3 , . . . n j,k= 1,2,3,...n j,k=1,2,3,...n |
h j h_j hj | 工件 j j j 的工序总数 |
h , l h,l h,l | 工序序号, h = 1 , 2 , 3 , . . . h j h=1,2,3,...h_j h=1,2,3,...hj |
Ω j h \Omega _{jh} Ωjh | 工件 j j j 的第 h h h 道工序的可选加工机器集 |
m j h m_{jh} mjh | 工件 j j j 的第 h h h 道工序的可选加工机器数 |
O j h O_{jh} Ojh | 工件 j j j 的第 h h h 道工序 |
M i j h M_{ijh} Mijh | 工件 j j j 的第 h h h 道工序在机器 i i i 上加工 |
p i j h p_{ijh} pijh | 工件 j j j 的第 h h h 道工序在机器 i i i 上的加工时间 |
s j h s_{jh} sjh | 工件 j j j 的第 h h h 道工序加工开始时间 |
c j h c_{jh} cjh | 工件 j j j 的第 h h h 道工序加工完成时间 |
L L L | 一个足够大的正数 |
d j d_{j} dj | 工件 j j j 的交货期 |
C j C_{j} Cj | 每个工件的完成时间 |
C max C_{\text{max}} Cmax | 最大完工时间 |
T o T_o To | ∑ j = 1 n h j {{\sum\limits_{j=1}^{n}h_j}} j=1∑nhj,所有工件工序总数 |
x i j h = { 1 , 如果工序 O j h 选择机器 i ; 0 , 否则; x_{ijh}=\left\{ \begin{aligned} & 1,\text{如果工序}O_{jh}\text{选择机器}i ;\\ & 0,\text{否则;} \end{aligned} \right. xijh={1,如果工序Ojh选择机器i;0,否则;
y i j h k l = { 1 , 如果 O i j h 先于 O i k l 加工; 0 , 否则; y_{ijhkl}=\left\{ \begin{aligned} & 1,\text{如果}O_{ijh}\text{先于}O_{ikl}\text{加工;} \\ & 0,\text{否则;} \end{aligned} \right. yijhkl={1,如果Oijh先于Oikl加工;0,否则;
如果
Ω
j
h
=
Ω
\Omega _{jh}=\Omega
Ωjh=Ω,
∀
j
∈
[
1
,
n
]
,
∀
h
∈
[
1
,
h
j
]
\forall j\in [1,n],\forall h\in [1,{{h}_{j}}]
∀j∈[1,n],∀h∈[1,hj],则是T-FJSP;
如果
Ω
j
h
∈
Ω
\Omega _{jh}∈\Omega
Ωjh∈Ω,
∃
j
∈
[
1
,
n
]
,
∃
h
∈
[
1
,
h
j
]
\exists j\in [1,n],\exists h\in [1,{{h}_{j}}]
∃j∈[1,n],∃h∈[1,hj],则是P-FJSP;
2.4 模型约束
一般的FJSP受到下列约束:
s
j
h
+
x
i
j
h
×
p
i
j
h
≤
c
j
h
.
\begin{equation} s_{jh}+x_{ijh}×p_{ijh}≤c_{jh}. \end{equation}
sjh+xijh×pijh≤cjh.
其中:
i
=
1
,
2
,
3
,
.
.
,
m
i=1,2,3,..,m
i=1,2,3,..,m;
j
=
1
,
2
,
3
,
.
.
.
,
n
j= 1,2,3,...,n
j=1,2,3,...,n;
h
=
1
,
2
,
3
,
.
.
.
,
h
j
h=1,2,3,...,h_j
h=1,2,3,...,hj
工件
j
j
j 的第
h
h
h 道工序加工开始时间+在机器
i
i
i 上的加工时间≤工序加工完成时间
c
j
h
≤
s
j
(
h
+
1
)
.
\begin{equation} c_{jh}≤s_{j(h+1)}. \end{equation}
cjh≤sj(h+1).
其中:
j
=
1
,
2
,
3
,
.
.
.
n
j= 1,2,3,...n
j=1,2,3,...n;
h
=
1
,
2
,
3
,
.
.
.
,
h
j
−
1
h=1,2,3,...,h_j-1
h=1,2,3,...,hj−1
工件
j
j
j 的第
h
h
h 道工序加工完成时间≤工件
j
j
j 的第
h
+
1
h+1
h+1 道工序加工开始时间
c
j
h
j
≤
C
max
.
\begin{equation} c_{jh_{j}}≤C_{\text{max}}. \end{equation}
cjhj≤Cmax.
其中:
j
=
1
,
2
,
3
,
.
.
.
n
j= 1,2,3,...n
j=1,2,3,...n
工件
j
j
j 共有
h
j
h_j
hj 道工序,工序总时间不能超过最大完工时间
C
max
C_\text{max}
Cmax
s
j
h
+
p
i
j
h
≤
s
k
l
+
L
(
1
−
y
i
j
h
k
l
)
.
\begin{equation} s_{jh}+p_{ijh}≤s_{kl}+L(1-y_{ijhkl}). \end{equation}
sjh+pijh≤skl+L(1−yijhkl).
其中:
j
=
0
,
1
,
2
,
.
.
.
n
j= 0,1,2,...n
j=0,1,2,...n;
k
=
1
,
2
,
3
,
.
.
.
n
k= 1,2,3,...n
k=1,2,3,...n;
h
=
1
,
2
,
3
,
.
.
.
,
h
j
h=1,2,3,...,h_j
h=1,2,3,...,hj;
l
=
1
,
2
,
3
,
.
.
.
,
h
k
l=1,2,3,...,h_k
l=1,2,3,...,hk;
i
=
1
,
2
,
.
.
,
m
i=1,2,..,m
i=1,2,..,m
工件
j
j
j 的第
h
h
h 道工序加工开始时间+在第
i
i
i机器的加工时间≤工件
k
k
k 的第
l
l
l 道工序加工开始时间+
Y
Y
Y
Y
Y
Y=如果
O
i
j
h
O_{ijh}
Oijh先于
O
i
k
l
O_{ikl}
Oikl加工时间 =0
Y
Y
Y=如果
O
i
j
h
O_{ijh}
Oijh晚于
O
i
k
l
O_{ikl}
Oikl加工时间 =无穷大
c
j
h
≤
s
i
(
h
+
1
)
+
L
(
1
−
y
i
k
l
j
(
h
+
1
)
)
.
\begin{equation} c_{jh}≤s_{i(h+1)}+L(1-y_{iklj(h+1)}). \end{equation}
cjh≤si(h+1)+L(1−yiklj(h+1)).
其中:
j
=
1
,
2
,
.
.
.
n
j= 1,2,...n
j=1,2,...n;
k
=
0
,
1
,
2
,
.
.
.
n
k= 0,1,2,...n
k=0,1,2,...n;
h
=
1
,
2
,
3
,
.
.
.
,
h
j
−
1
h=1,2,3,...,h_j-1
h=1,2,3,...,hj−1;
l
=
1
,
2
,
3
,
.
.
.
,
h
k
l=1,2,3,...,h_k
l=1,2,3,...,hk;
i
=
1
,
2
,
.
.
,
m
i=1,2,..,m
i=1,2,..,m
工件
j
j
j 共有
h
j
h_j
hj 道工序总时间不能超过工件
j
j
j 的第
h
+
1
h+1
h+1 道工序加工开始时间+
Y
Y
YY
YY
Y
Y
YY
YY=如果
O
i
k
l
O_{ikl}
Oikl先于
O
i
j
(
h
+
1
)
O_{ij(h+1)}
Oij(h+1)加工时间 =0
Y
Y
YY
YY=如果
O
i
k
l
O_{ikl}
Oikl晚于
O
i
j
(
h
+
1
)
O_{ij(h+1)}
Oij(h+1)加工时间 =无穷大
∑
i
=
1
m
j
h
x
i
j
h
=
1.
\begin{equation} {{\sum\limits_{i=1}^{m_{jh}}x_{ijh}}}=1. \end{equation}
i=1∑mjhxijh=1.
其中:
j
=
1
,
2
,
3
,
.
.
.
n
j= 1,2,3,...n
j=1,2,3,...n;
h
=
1
,
2
,
3
,
.
.
.
,
h
j
h=1,2,3,...,h_j
h=1,2,3,...,hj
如果工序
O
j
h
O_{jh}
Ojh只能在可选的机器中选择一个
∑
j
=
1
n
∑
h
=
1
h
j
y
i
j
h
k
l
=
x
i
k
l
.
\begin{equation} {{\sum\limits_{j=1}^{n}{{\sum\limits_{h=1}^{h_{j}}y_{ijhkl}}}}}=x_{ikl}. \end{equation}
j=1∑nh=1∑hjyijhkl=xikl.
其中:
k
=
1
,
2
,
3
,
.
.
.
n
k= 1,2,3,...n
k=1,2,3,...n;
l
=
1
,
2
,
3
,
.
.
.
,
h
k
l=1,2,3,...,h_k
l=1,2,3,...,hk;
i
=
1
,
2
,
.
.
,
m
i=1,2,..,m
i=1,2,..,m
对于第
i
i
i号机器,早于
O
k
l
O_{kl}
Okl的其他全部工件的全部工序 的集合个数=
Y
Y
Y
YYY
YYY
如果工序
O
k
l
O_{kl}
Okl选择i机器 ——
Y
Y
Y
=
1
YYY=1
YYY=1
如果工序
O
k
l
O_{kl}
Okl不选择i机器 ——
Y
Y
Y
=
0
YYY=0
YYY=0
∑
k
=
1
n
∑
l
=
1
h
j
y
i
j
h
k
l
=
x
i
j
h
.
\begin{equation} {{\sum\limits_{k=1}^{n}{{\sum\limits_{l=1}^{h_{j}}y_{ijhkl}}}}}=x_{ijh}. \end{equation}
k=1∑nl=1∑hjyijhkl=xijh.
其中:
k
=
1
,
2
,
3
,
.
.
.
n
k= 1,2,3,...n
k=1,2,3,...n;
h
=
1
,
2
,
3
,
.
.
.
,
h
k
h=1,2,3,...,h_k
h=1,2,3,...,hk;
i
=
1
,
2
,
.
.
,
m
i=1,2,..,m
i=1,2,..,m
对于第
i
i
i号机器,晚于
O
j
h
O_{jh}
Ojh的其他全部工件的全部工序 的集合个数=
Y
Y
Y
YYY
YYY
如果工序
O
j
h
O_{jh}
Ojh选择i机器 ——
Y
Y
Y
=
1
YYY=1
YYY=1
如果工序
O
j
h
O_{jh}
Ojh不选择i机器 ——
Y
Y
Y
=
0
YYY=0
YYY=0
s
j
h
≥
0
;
c
j
h
≥
0.
\begin{equation} s_{jh}≥0 ;c_{jh}≥0. \end{equation}
sjh≥0;cjh≥0.
其中:
j
=
0
,
1
,
2
,
3
,
.
.
.
n
j= 0,1,2,3,...n
j=0,1,2,3,...n;
h
=
1
,
2
,
3
,
.
.
.
,
h
j
h=1,2,3,...,h_j
h=1,2,3,...,hj
各个参数变量必须是正数
2.4.1 模型约束小总结
- 式(1) 和式(2) 表示每一个工件的工序先后顺序约束;
- 式(3)表示工件的完工时间的约束,即每一个工件的完工时间不可能超过总的完工时间;
- 式(4)和式(5) 表示同一时刻同一台机器只能加工一道工序;
- 式(6) 表示机器约束,即同一时刻同一道工序只能且仅能被一台机器加工;
- 式(7)和式(8)表示存在每一台机器上可以存在循环操作;
- 式(9)表示各个参数变量必须是正数。
2.4.2 传统方法
目前已经提出了很多近似算法来求解FJSP问题,包括启发式、元启发式和机器学习技术。
相比较于精确算法,近似算法可以在求解效率和求解质量上实现较好的均衡。特别是群体智能(SI)和进化算法(EA),例如遗传(GA)、粒子群(PSO)、蚁群优化(ACO)、人工蜂群(ABC)等,在求解FJSP时都表现出了较强的实力。
3. FJSP评价指标
在FJSP的求解过程中,调度方案优劣的评价需要通过一定的目标函数来判断,目标函数也就是常用的评价指标。FJSP中不仅包括了经典JSP常用的评价指标,而且还有其他的评价指标。下面列出文献中较为常用的几个评价指标。
- 最大完工时间最小完工时间是每个工件最后一道工序完成的时间,其中最大的那个时间就是最大完工时间(makespan)。
它是衡量调度方案的最根本指标,主要体现车间的生产效率,也是FJSP研究中应用最广泛的评价指标之一。
可表示为:
f 1 = min ( max 1 ≤ j ≤ n ( C j ) ) . \begin{equation} f_1=\text{min}(\underset{1\le j\le n}{\mathop{\max }}\,({{C}_{j}})). \end{equation} f1=min(1≤j≤nmax(Cj)).
- 机器最大负荷最小在FJSP求解中,存在选择机器的过程,各台机器的负荷随着不同的调度方案而不同。负荷最大的机器就是瓶颈设备。要提高每台机器的利用率,必须使得各台机器的负荷尽量小且平衡,可表示为
f 2 = min ( max 1 ≤ j ≤ m ( ∑ j = 1 n ∑ h = 1 h j p i j h x i j h ) ) . \begin{equation} f_2=\text{min}(\underset{1\le j\le m}{\mathop{\max }}\,({{\sum\limits_{j=1}^{n}{{\sum\limits_{h=1}^{h_{j}}p_{ijh}x_{ijh}}}}})). \end{equation} f2=min(1≤j≤mmax(j=1∑nh=1∑hjpijhxijh)).
- 总机器负荷最小工序在不同机器上的加工时间是不同的,那么总的机器负荷随着不同的调度方案而不同。尽量使最大完工时间一样的情况下,减少所有机器的总消耗,可表示为
f 3 = min ( ∑ i = 1 m ∑ j = 1 n ∑ h = 1 h j p i j h x i j h ) . \begin{equation} f_3=\text{min}({{\sum\limits_{i=1}^{m}{{\sum\limits_{j=1}^{n}{\sum\limits_{h=1}^{h_{j}}p_{ijh}x_{ijh}}}}}}). \end{equation} f3=min(i=1∑mj=1∑nh=1∑hjpijhxijh).
- 提前/拖期最小准时制的生产必须考虑交货期问题,工件完工时间越接近交货期,表明其交货期性能越好。一般用最大提前时间指标 E j E_j Ej 表示工件 j j j 的交货期 d j d_j dj 与其完成时间 C j C_j Cj 的非负差值,即
E j = max ( d j − C j , 0 ) . \begin{equation} E_j=\text{max}(d_j-C_j,0). \end{equation} Ej=max(dj−Cj,0).
- 用最大拖期时间指标 T j T_j Tj表示工件 j j j 的完成时间 C j C_j Cj 与交货期时间 d j d_j dj 的非负差值,即
T j = max ( C j − d j , 0 ) . \begin{equation} T_j=\text{max}(C_j-d_j,0). \end{equation} Tj=max(Cj−dj,0).
- 最大提前时间最小和最大拖期时间最小分别可表示为
f
4
=
min
(
max
1
≤
j
≤
n
(
E
j
)
)
.
\begin{equation} f_4=\text{min}(\underset{1\le j\le n}{\mathop{\max }}\,(E_j)). \end{equation}
f4=min(1≤j≤nmax(Ej)).
f
5
=
min
(
max
1
≤
j
≤
n
(
T
j
)
)
.
\begin{equation} f_5=\text{min}(\underset{1\le j\le n}{\mathop{\max }}\,(T_j)). \end{equation}
f5=min(1≤j≤nmax(Tj)).
以上几种性能评价指标较为常用。还有其他如考虑工件安装时间的性能评价指标或更加贴近生产成本的一些成本指标等。其中如果性能评价指标函数是完工时间的非减函数,则称为正规性能指标(regular measure),如 f 1 , f 2 , f 3 , f 4 , f 5 f_1,f_2,f_3,f_4,f_5 f1,f2,f3,f4,f5;否则称为非正规性能指标,如 E / T E/T E/T 惩罚代价最小等
4. 举例说明
4.1 加工过程论述方法
假如此时有3个工件
[
J
1
,
J
2
,
J
3
]
[J_1,J_2,J_3]
[J1,J2,J3]需要在3台机器
[
M
1
,
M
2
,
M
3
]
[M_1,M_2,M_3]
[M1,M2,M3] 上加工
n
=
3
m
=
3
n=3\\ m=3
n=3m=3
不同工件所需的加工工序及加工时间可以用以下公式表示:
J 1 = [ ( 1 , 3 ) , ( 2 , 2 ) , ( 3 , 2 ) ] J_1=[(1,3),(2,2),(3,2)] J1=[(1,3),(2,2),(3,2)]
J 2 = [ ( 1 , 2 ) , ( 3 , 1 ) , ( 2 , 4 ) ] J_2=[(1,2),(3,1),(2,4)] J2=[(1,2),(3,1),(2,4)]
J 3 = [ ( 2 , 4 ) , ( 3 , 3 ) ] J_3=[(2,4),(3,3)] J3=[(2,4),(3,3)]
在这个例子中,作业 J 1 J_1 J1有3道工序:
- 它的第1道工序上标注有(1,3),表示第1道工序必须在 M 1 M_1 M1上进行加工,且需要3个单位的加工时间;
- 它的第2道工序上标注有(2,2),表示第2道工序必须在 M 2 M_2 M2上进行加工,且需要2个单位的加工时间;
- 余下的同理。总的来说,这个实例中共有8道工序。
当然,对于代码 来讲 起始做索引0开始比较好,即 J 0 J_0 J0和 J 0 J_0 J0。
4.2 加工过程论述图表
例如, 一个包括 3 个工件、5 台机器的柔性作业车间调度加工时间表。柔性作业车间调度问题的求解过程包括两部分:选择各工序的加工机器和确定每台机器上工件的先后顺序。
调度的目标就是确定每个机器上工序的加工顺序和每个工序的开工时间,使得最大完工时间
C
m
a
x
C_{max}
Cmax最小或者其他指标达到最优
5. 利用遗传算法求解柔性车间调度问题
5.1 遗传算法编码
编码与解码是指染色体和调度解之间进行相互转换,是遗传算法成功实施优化的首要和关键问题。对于传统的作业车间调度问题,大多数研究采用基于工序的编码。
但是柔性作业车间调度问题不仅要确定工序的加工顺序,还需为每道工序选择一台合适的机器,仅采用基于工序的编码方法不能得到问题的解。因此,对于柔性作业车间调度问题,遗传算法的编码由两部分组成,第一部分为基于工序的编码,用来确定工序的加工先后顺序;第二部分为基于机器分配的编码,用来选择每道工序的加工机器。融合这两种编码方法,即可得到柔性作业车间调度问题的一个可行解。结合两种编码方式的染色体如下图所示。
function [Population] = Coding(T,PopSize)
%% INPUT:
% T--input matrix:
% For example:
% A partial flexible scheduling problem in which 8 jobs are processed
% on 8 machines, in which the number of available machining machines per
% step of job is less than or equal to the total number of machines
% J1 ={[5 3 5 3 3 0 10 9];[10 0 5 8 3 9 9 6];[0 10 0 5 6 2 4 5]};
% J2 ={[5 7 3 9 8 0 9 0];[0 8 5 2 6 7 10 9];[0 10 0 5 6 4 1 7];[10 8 9 6 4 7 0 0]};
% J3 ={[10 0 0 7 6 5 2 4];[0 10 6 4 8 9 10 0];[1 4 5 6 0 10 0 7]};
% J4 ={[3 1 6 5 9 7 8 4];[12 11 7 8 10 5 6 9];[4 6 2 10 3 9 5 7]};
% J5 ={[3 6 7 8 9 0 10 0];[10 0 7 4 9 8 6 0];[0 9 8 7 4 2 7 0];[11 9 0 6 7 5 3 6]};
% J6 ={[6 7 1 4 6 9 0 10];[11 0 9 9 9 7 6 4];[10 5 9 10 11 0 10 0]};
% J7 ={[5 4 2 6 7 0 10 0];[0 9 0 9 11 9 10 5];[0 8 9 3 8 6 0 10]};
% J8 ={[2 8 5 9 0 4 0 10];[7 4 7 8 9 0 10 0];[9 9 0 8 5 6 7 1];[9 0 3 7 1 5 8 0]};
% T={J1;J2;J3;J4;J5;J6;J7;J8}; 8*1 cell
% Popsize- Population size in genetic algorithms,2*PopSize+1
%% OUTPUT:
% Population-Popsize*1 cell
% chromosome-2*1 cell
%% variable declaration
num_of_jobs = length(T); %number of jobs
num_of_machines = length(T{1}{1}); %number of machines
steps_of_job =[];
machine_of_job=cell(num_of_jobs,1);
% calculate the length of chromosome
for i = 1:num_of_jobs
steps_of_job=[steps_of_job;length(T{i})];
end
len_of_chromosome = sum(steps_of_job);
% calculate the machine set for each steps of each job
for i=1:num_of_jobs
steps=cell(steps_of_job(i),1);
for j = 1:steps_of_job(i)
machineset=[];
for k=1:length(T{i}{j})
if T{i}{j}(k)~=0
machineset=[machineset k];
end
end
steps{j}=machineset;
end
machine_of_job{i}=steps;
end
%steps chromosome
%Coding is based on the step of the job
step_chromsome=[];
for i = 1:num_of_jobs
for j = 1:steps_of_job(i)
step_chromsome=[step_chromsome i];
end
end
step_population =[];
%Generate population with chromosome containing random genes
for i = 1:PopSize
step_population(i,:)=step_chromsome(randperm(length(step_chromsome(:))));
end
%
%machine chromosome
%In each steps, the machine is randomly selected for two machines,
%and the machine that selects the short processing time is the gene with
%propability
machine_population =[];
for index = 1:PopSize
machine_chromsome=[];
for i=1:num_of_jobs
for j=1:steps_of_job(i)
pos= randperm(length(machine_of_job{i}{j}),2);
machine1=machine_of_job{i}{j}(pos(1));
machine2=machine_of_job{i}{j}(pos(2));
if (rand(1)<0.8) && (T{i}{j}(machine1)>T{i}{j}(machine2))
machine = machine2;
else
machine = machine1;
end
machine_chromsome=[machine_chromsome machine];
end
end
machine_population(index,:) = machine_chromsome;
end
Population=cell(PopSize,1);
for i=1:PopSize
Population{i} =[step_population(i,:);machine_population(i,:)];
end
end
其中基于工序编码的基因串确定工序加工的先后顺序,基于机器编码的基因串确定每个工件所有工序的加工机器。图中所示的染色体表示的工序和机器序列为
(
O
11
,
M
1
)
(O_{11},M_1 )
(O11,M1),
(
O
21
,
M
1
)
(O_{21},M_1)
(O21,M1),
(
O
12
,
M
3
)
(O_{12},M_3 )
(O12,M3),
(
O
22
,
M
4
O
)
(O_{22},M_4O )
(O22,M4O),
(
O
13
,
M
4
)
(O_{13},M_4 )
(O13,M4),
(
O
31
,
M
3
)
(O_{31},M_3 )
(O31,M3),
(
O
23
,
M
5
)
(O_{23},M_5 )
(O23,M5),
(
O
32
,
M
2
)
(O_{32},M_2)
(O32,M2),
(
O
33
,
M
2
)
(O_{33},M_2)
(O33,M2)
编码代码如下:
解码过程和编码过程相反,解码分为一般解码或者称为半主动解码、主动解码和全主动解码。本文使用半主动解码方式进行染色体解码。
5.2 遗传算法解码
(1)活动调度: 在不推迟其他操作或破坏优先顺序的条件下,其中没有一个操作可以提前加工。图b所示。
(2)半活动调度: 在不改变机器上加工顺序的条件下,其中没有操作可以提前。图a所示。
(3)非延迟调度: 至少存在一个工件等待加工时,对应不存在相应处于空闲的机器。
解码代码如下
%% Flexible Job-shop scheduling problem based on genetic algorithm with POX selection
% Author:Eric.Wan
% Date:2019-12-8
% Add.:ShenYang,China
% Email:970301442@qq.com
% Version: v1.0
function [Jobs,Cmax,MachineList,ST,PT] = SemiActiveDecoding(T,Chromosome)
%% INPUT:
% T--input matrix:
% For example:
% A partial flexible scheduling problem in which 8 jobs are processed
% on 8 machines, in which the number of available machining machines per
% step of job is less than or equal to the total number of machines
% J1 ={[5 3 5 3 3 0 10 9];[10 0 5 8 3 9 9 6];[0 10 0 5 6 2 4 5]};
% J2 ={[5 7 3 9 8 0 9 0];[0 8 5 2 6 7 10 9];[0 10 0 5 6 4 1 7];[10 8 9 6 4 7 0 0]};
% J3 ={[10 0 0 7 6 5 2 4];[0 10 6 4 8 9 10 0];[1 4 5 6 0 10 0 7]};
% J4 ={[3 1 6 5 9 7 8 4];[12 11 7 8 10 5 6 9];[4 6 2 10 3 9 5 7]};
% J5 ={[3 6 7 8 9 0 10 0];[10 0 7 4 9 8 6 0];[0 9 8 7 4 2 7 0];[11 9 0 6 7 5 3 6]};
% J6 ={[6 7 1 4 6 9 0 10];[11 0 9 9 9 7 6 4];[10 5 9 10 11 0 10 0]};
% J7 ={[5 4 2 6 7 0 10 0];[0 9 0 9 11 9 10 5];[0 8 9 3 8 6 0 10]};
% J8 ={[2 8 5 9 0 4 0 10];[7 4 7 8 9 0 10 0];[9 9 0 8 5 6 7 1];[9 0 3 7 1 5 8 0]};
% T={J1;J2;J3;J4;J5;J6;J7;J8}; 8*1 cell
%Chromosome -- A chromosome to be decoded
%% OUTPUT:
%JobList-- job sequences
%Cmax --the max makespan
%MachineList--The machine sequences corresponding to chromosome
%ST --the start time for each job step in chromosome
%PT --The operation time for each job step in chromome
%% start
num_of_jobs = length(T); %number of jobs
num_of_machines = length(T{1}{1}); %number of machines
len_of_chromosome = length(Chromosome);
StepList = []; %steps for all genes
MachineList = zeros(1,len_of_chromosome);
ST = zeros(1,len_of_chromosome);
PT = zeros(1,len_of_chromosome);
DecodedGenes =[];
steps_of_job =[];
for i = 1:num_of_jobs
steps_of_job=[steps_of_job;length(T{i})];
end
%% Caculate MachineList and PT
for index = 1:len_of_chromosome
DecodedGenes=[DecodedGenes Chromosome(1,index)];
postion = length(find(DecodedGenes==Chromosome(1,index)));
StepList = [StepList postion];
MachineList(index)=Chromosome(2,sum(steps_of_job(1:(Chromosome(1,index)-1)))+postion);
PT(index)=T{Chromosome(1,index)}{postion}(MachineList(index));
end
%% Caculate ST
Machines = unique(MachineList);
Jobs = unique(Chromosome(1,:));
job_start_time = cell(num_of_jobs,1);
job_end_time = cell(num_of_jobs,1);
machine_start_time = cell(num_of_machines,1);
machine_end_time = cell(num_of_machines,1);
machine_state = zeros(1,num_of_machines); %0--FirstWork;1--NotFirst
for index = 1:len_of_chromosome
job = Chromosome(1,index);
machine = MachineList(index);
pt = PT(index);
step = StepList(index);
pos_m = find(Machines==machine);
pos_j = find(Jobs==job);
if step==1 %first step without considering the constrains between steps of same job
if machine_state(pos_m)==0 % The machine is first used
job_start_time{pos_j}=[0,pos_m];
job_end_time{pos_j}=[job_start_time{pos_j}(1)+pt,pos_m];
else
job_start_time{pos_j}=[machine_end_time{pos_m}(1),pos_m];
job_end_time{pos_j}=[job_start_time{pos_j}(1)+pt,pos_m];
end
else
if machine_state(pos_m)==0 % The machine is first used
job_start_time{pos_j}=[job_end_time{pos_j}(1),pos_m];
job_end_time{pos_j}=[job_start_time{pos_j}(1)+pt,pos_m];
else
job_start_time{pos_j}=[max(machine_end_time{pos_m}(1),job_end_time{pos_j}(1)),pos_m];
job_end_time{pos_j}=[job_start_time{pos_j}(1)+pt,pos_m];
end
end
machine_start_time{pos_m}= job_start_time{pos_j}(1);
machine_end_time{pos_m} = job_end_time{pos_j}(1);
machine_state(pos_m)=1;
ST(index)=job_start_time{pos_j}(1);
end
%% Caculate Cmax
end_time=cell2mat(job_end_time);
Cmax = max(end_time(:,1));
end
5.3 遗传算法选择
在遗传算法中,适应度是个体对生存环境的适应程度,适应度高的个体将获得更多的生存机会。选择算子根据适应度的值选择个体遗传到下一代群体中。选择操作采用最佳个体保存(Elitist model)和锦标选择(Tournament selection)两种方法。在本章的改进遗传算法中,最佳个体保存方法是将父代群体中最优的 一个个体直接复制到下一代中。锦标选择是从种群中随机选择两个个体,如果随机值(在 0~1 之间随机产生)小于给定概率值 r(概率值 r是一个参数,一般设置为 0.8),则选择优的一个,否则就选择另一个。被选择的个体放回到种群,可以重新作为一个父染色体参与选择。
选择代码如下
BestFitness=min(FITNESS); % find the best chromosome
position=find(FITNESS==BestFitness); % and the position,may be more than one
BestChromosome=Population{position(1)}; % choose one chromosome from population
%% Selection :Elitism and 2-tournament selection are used
Parent = cell(PopSize,1);
Pr =0.8;
for index =1:PopSize-1
pos = randperm(PopSize,2);
chromosome1 = Population{pos(1)};
chromosome2 = Population{pos(2)};
if (rand(1)<Pr) && FITNESS(pos(1))>FITNESS(pos(2))
chromosome = chromosome1;
else
chromosome = chromosome2;
end
Parent{index} = chromosome;
end
Parent{PopSize}=BestChromosome;
5.4 遗传算法交叉
交叉操作是将种群中两个个体随机地交换某些基因,产生新的基因组合,期望将有益的基因组合在一起。染色体中两部分基因串的交叉分别进行,其中第一部分基于工序编码基因串的交叉操作采用IPOX 交叉算子,第二部分基于机器分配编码基因串的交叉采用多点交叉MPX的方法。
5.4.1 IPOX交叉因子
IPOX操作是在POX操作基础上经改进而形成的(POX不了解的可以参考博文基于遗传算法的传统作业车间调度的问题求解),它仅仅交叉父代染色体中工序的加工序列,保留工件中工序分配的机器到子代。IPOX的具体操作过程如下图所示,其中 P 1 P_1 P1和 P 2 P_2 P2 为调度问题的两个父代染色体,交叉产生子代 C 1 C_1 C1 和 C 2 C_2 C2IPOX交叉操作过程为:
1)把所有的工件随机分成两个集合
G
1
G_1
G1 和
G
2
G_2
G2 ;
2) 复制
P
1
P_1
P1 包含在
G
1
G_1
G1 中的工件到
C
1
C_1
C1,复制
P
2
P_2
P2 包含在
G
2
G_2
G2 中的工件到
C
2
C_2
C2 ,保留它们的位置;
3)复制
P
1
P_1
P1 包含在
G
1
G_1
G1 中的工件到
C
2
C_2
C2 ,复制$ P_2$ 包含在$ G_2$ 中的工件到
C
1
C_1
C1 ,保留它们的顺序。
5.4.2 MPX交叉因子
MPX交叉父代染色体中工序选定的机器,工序的加工顺序保留到子代。多点交叉操作的过程如下图所示。对于某调度问题, P 1 P_1 P1和 P 2 P_2 P2为调度问题的两个父代染色体,交叉产生子代 C 1 C_1 C1和 C 2 C_2 C2MPX交叉操作过程为:
1)首先随机产生一个有整数0、1组成的与染色体长度相等的
r
a
n
d
[
0
,
1
]
rand[0,1]
rand[0,1]集;
2)依次在
P
2
P_2
P2中选出
P
1
P_1
P1与
r
a
n
d
[
0
,
1
]
rand[0,1]
rand[0,1]集中1的位置对应相同的工序,交换他们分配的机器,
P
1
P_1
P1 和
P
2
P_2
P2中其他机器的加工顺序保留到子代,这样分别产生子代
C
1
C_1
C1 和
C
2
C_2
C2 。因为是同一工序的加工机器交换,生成的子代染色体必为可行解。
交叉操作的代码如下
%% Crossover: IPOX for steps, MPX for machine
Children_group1=cell(PopSize,1);
for i=1:(PopSize-1)
%Parent individuals are selected for crossover operation:
%Parent1 is selected sequentially and Parent2 is selected randomly.
index_parent2 = randi([1,(PopSize-1)]);
Parent1=Parent{i};
Parent2=Parent{index_parent2};
Children1=zeros(2,len_of_chromosome);
Children2=zeros(2,len_of_chromosome);
if rand(1)<=Pc %Use the probability to determine if crossover is required
%% Part1: IPX for step
%Randomly divide the set of jobs {1,2,3...,n} into two non-empty sub-sets J1 and J2.
num_J1 = randi([1,num_of_jobs]);
if num_J1==num_of_jobs
num_J1 = fix(num_of_jobs/2);
end
J = randperm(num_of_jobs);
J1 =J(1:num_J1);
J2 =J(num_J1+1:num_of_jobs);
% Copy the jobs that Parent1 contains in J1 to Children1,
% and Parent2 contains in J2 to Children2, and keep them in place.
for index = 1:num_J1 % look for jobs that Parent1 are included in J1
job = J1(index);
for j = 1:len_of_chromosome
if job == Parent1(1,j)
Children1(1,j)=Parent1(1,j);
end
end
end
for index = 1:num_of_jobs-num_J1 % look for jobs that Parent2 are included in J2
job = J2(index);
for j = 1:len_of_chromosome
if job == Parent2(1,j)
Children2(1,j)=Parent2(1,j);
end
end
end
%Copy the jobs that Parent1 contains in J1 to Children2,
%and Parent2 contains in J2 to Children1 in their order.
for index = 1:len_of_chromosome % look for jobs that Parent1 are included in J1
job = Parent1(1,index);
if ~isempty(find(J1==job, 1))
for gene = 1:len_of_chromosome
if Children2(1,gene)==0
Children2(1,gene)=job;
break;
end
end
end
end
for index = 1:len_of_chromosome % look for jobs that Parent1 are included in J1
job = Parent2(1,index);
if ~isempty(find(J2==job, 1))
for gene = 1:len_of_chromosome
if Children1(1,gene)==0
Children1(1,gene)=job;
break;
end
end
end
end
%%IPOX cross operation completed
%% Part 2 MPX for machine
%The process of crossover operation is as follows: firstly, a set rand0_1
%composed of 0 and 1 that is equal to the length of chromosome is randomly
%generated, and then the genes at the same position of 1 in the set of rand0_1
%in the two parental generations are interchanged, and two offspring are
%obtained after the crossover
rand0_1 = zeros(1,len_of_chromosome);
for gene = 1:len_of_chromosome
if rand(1)>0.5
rand0_1(gene)=1;
end
end
for gene = 1:len_of_chromosome
if rand0_1(gene)==1
Children1(2,gene) = Parent2(2,gene);
Children2(2,gene) = Parent1(2,gene);
else
Children1(2,gene) = Parent1(2,gene);
Children2(2,gene) = Parent2(2,gene);
end
end
%MOX cross operation completed
else
Children1 = Parent1;
Children2 = Parent2;
end
%% Select the Fitness value best retained to the next generation
[Parent1_FitnessValue] = FitnessCalculator(T,Parent1);
[Parent2_FitnessValue] = FitnessCalculator(T,Parent2);
[Children1_FitnessValue] = FitnessCalculator(T,Children1);
[Children2_FitnessValue] = FitnessCalculator(T,Children2);
[~, pos] = max([Parent1_FitnessValue Parent2_FitnessValue Children1_FitnessValue Children2_FitnessValue]);
temp_group ={Parent1 Parent2 Children1 Children2};
end
Children_group1{PopSize}= BestChromosome;
5.5 遗传算法变异
变异操作的目的是改善算法的局部搜索能力和维持群体多样性,同时防止出现早
熟现象。基于工序编码和基于机器分配编码的变异分别设计如下:
- 基于工序编码的变异
对于这部分基因实施插入变异,即从染色体中随机选择一个基因,然后将之插入到一个随机的位置。 - 基于机器分配编码的变异
由于每道工序都可以由多台机器完成,所以随机选择两道工序,然后在执行这两道工序的机器集合中选择一台机器(采用比例选择策略,加工时间短的优先选择),并将选择的机器号置入对应的基于机器分配编码的基因串中,这样得出的解能确保是可行解。
交叉操作的代码如下
%% Mutation
Children_group2=cell(PopSize,1);
for i=1:(PopSize-1)
temp_chromsome = Children_group1{i};
%% Mutation for steps
if rand(1)<Pm
for j=1:4
pos1=randi([1,len_of_chromosome]); % Choose the sequence number of a gene to be mutated
pos2=randi([1,len_of_chromosome]); % Choose another the sequence number of a gene to be mutated
Gene=temp_chromsome(1,pos1);
temp_chromsome(1,pos1)=temp_chromsome(1,pos2);
temp_chromsome(1,pos2)=Gene;
end
end
%% Mutation for machine
if rand(1)<Pm
for k=1:4
job = randi([1,num_of_jobs]) ; %random choose job
tempstep = randi([1,steps_of_job(job)]); %random choose step
newmachine = machine_of_job{job}{tempstep}(randi([1,length(machine_of_job{job}{tempstep})]));%random choose machine
temp_chromsome(2,sum(steps_of_job(1:job-1))+1)= newmachine; %replace the old one
end
end
Children_group2{i} = temp_chromsome;
end
Children_group2{PopSize}= BestChromosome;
% complete mutation
6. 实验验证
这里使用两个基准实例,即部分柔性实例 T 8 ∗ 8 T_{8*8} T8∗8 和完全柔性实例 T 10 ∗ 10 T_{10*10} T10∗10
%% T8x8
% A partial flexible scheduling problem in which 8 jobs are processed
% on 8 machines, in which the number of available machining machines per
% step of job is less than or equal to the total number of machines
J1 ={[5 3 5 3 3 0 10 9];[10 0 5 8 3 9 9 6];[0 10 0 5 6 2 4 5]};
J2 ={[5 7 3 9 8 0 9 0];[0 8 5 2 6 7 10 9];[0 10 0 5 6 4 1 7];[10 8 9 6 4 7 0 0]};
J3 ={[10 0 0 7 6 5 2 4];[0 10 6 4 8 9 10 0];[1 4 5 6 0 10 0 7]};
J4 ={[3 1 6 5 9 7 8 4];[12 11 7 8 10 5 6 9];[4 6 2 10 3 9 5 7]};
J5 ={[3 6 7 8 9 0 10 0];[10 0 7 4 9 8 6 0];[0 9 8 7 4 2 7 0];[11 9 0 6 7 5 3 6]};
J6 ={[6 7 1 4 6 9 0 10];[11 0 9 9 9 7 6 4];[10 5 9 10 11 0 10 0]};
J7 ={[5 4 2 6 7 0 10 0];[0 9 0 9 11 9 10 5];[0 8 9 3 8 6 0 10]};
J8 ={[2 8 5 9 0 4 0 10];[7 4 7 8 9 0 10 0];[9 9 0 8 5 6 7 1];[9 0 3 7 1 5 8 0]};
T8x8={J1;J2;J3;J4;J5;J6;J7;J8};
%% T10x10
% For all flexible scheduling problems of 10 jobs and 10 machines,
% the number of available processing machines in each step of job is equal to
% the total number of machines
G1={[1 4 6 9 3 5 2 8 9 5];[4 1 1 3 4 8 10 4 11 4];[3 2 5 1 5 6 9 5 10 3]};
G2={[2 10 4 5 9 8 4 15 8 4];[4 8 7 1 9 6 1 10 7 1];[6 11 2 7 5 3 5 14 9 2]};
G3={[8 5 8 9 4 3 5 3 8 1];[9 3 6 1 2 6 4 1 7 2];[7 1 8 5 4 9 1 2 3 4]};
G4={[5 10 6 4 9 5 1 7 1 6];[4 2 3 8 7 4 6 9 8 4];[7 3 12 1 6 5 8 3 5 2]};
G5={[7 10 4 5 6 3 5 15 2 6];[5 6 3 9 8 2 8 6 1 7];[6 1 4 1 10 4 3 11 13 9]};
G6={[8 9 10 8 4 2 7 8 3 10];[7 3 12 5 4 3 6 9 2 15];[4 7 3 6 3 4 1 5 1 11]};
G7={[1 7 8 3 4 9 4 13 10 7];[3 8 1 2 3 6 11 2 13 3];[5 4 2 1 2 1 8 14 5 7]};
G8={[5 7 11 3 2 9 8 5 12 8];[8 3 10 7 5 13 4 6 8 4];[6 2 13 5 4 3 5 7 9 5]};
G9={[3 9 1 3 8 1 6 7 5 4];[4 6 2 5 7 3 1 9 6 7];[8 5 4 8 6 1 2 3 10 12]};
G10={[4 3 1 6 7 1 2 6 20 6];[3 1 8 1 9 4 1 4 17 15];[9 2 4 2 3 5 2 4 10 23]};
T10x10 = {G1;G2;G3;G4;G5;G6;G7;G8;G9;G10};
这里贴上运行8*8实例的运行脚本程序入如下:
clear;
clc;
T = Tmatrix('8x8');
Iterations=100;
PopSize=50;
Pc=0.8;
Pm=0.5;
result=cell(1000,1);
[Chromosome] = POX_GA(T,Iterations,PopSize,Pc,Pm);
[Jobs,Cmax,MachineList,ST,PT] = SemiActiveDecoding(T,Chromosome);
GanntGraf(Jobs,Chromosome(1,:),MachineList,ST,PT,Cmax,'FJSP') ;
7. 参考文献
https://blog.csdn.net/tauyangdao/article/details/103444415
https://blog.csdn.net/tauyangdao/article/details/103827189
https://zhuanlan.zhihu.com/p/383594362
https://zhuanlan.zhihu.com/p/558800456