Compare commits
No commits in common. "250e5f5cad301c7f60a841b8bff3c180cd2375a2" and "17da0c8d161dc63d84042ece662ffda439be9e93" have entirely different histories.
250e5f5cad
...
17da0c8d16
10
Decode.py
10
Decode.py
|
|
@ -2,7 +2,6 @@ import numpy as np
|
||||||
from Job import Job
|
from Job import Job
|
||||||
from Machine import Machine_Time_window
|
from Machine import Machine_Time_window
|
||||||
|
|
||||||
|
|
||||||
class Decode:
|
class Decode:
|
||||||
def __init__(self, J, Processing_time, M_num):
|
def __init__(self, J, Processing_time, M_num):
|
||||||
"""
|
"""
|
||||||
|
|
@ -80,11 +79,11 @@ class Decode:
|
||||||
End_work_time = M_Ealiest + P_t # 当前工件当前工序的结束时间
|
End_work_time = M_Ealiest + P_t # 当前工件当前工序的结束时间
|
||||||
return M_Ealiest, Selected_Machine, P_t, O_num, last_O_end, End_work_time
|
return M_Ealiest, Selected_Machine, P_t, O_num, last_O_end, End_work_time
|
||||||
|
|
||||||
# 解码操作(适配新编码:前半部分为OS,后半部分为MS)
|
# 解码操作
|
||||||
def decode(self, CHS, Len_Chromo):
|
def decode(self, CHS, Len_Chromo):
|
||||||
"""
|
"""
|
||||||
:param CHS: 种群基因
|
:param CHS: 种群基因
|
||||||
:param Len_Chromo: 工序总数(OS和MS各占一半长度)
|
:param Len_Chromo: MS与OS的分解线
|
||||||
:return: 双目标值 [最大加工时间, 负载标准差]
|
:return: 双目标值 [最大加工时间, 负载标准差]
|
||||||
"""
|
"""
|
||||||
# 重置状态
|
# 重置状态
|
||||||
|
|
@ -103,11 +102,10 @@ class Decode:
|
||||||
job.Last_Processing_Machine = None
|
job.Last_Processing_Machine = None
|
||||||
job.Last_Processing_end_time = 0
|
job.Last_Processing_end_time = 0
|
||||||
|
|
||||||
OS = list(CHS[0:Len_Chromo]) # 前半部分为工件排列
|
MS = list(CHS[0:Len_Chromo])
|
||||||
MS = list(CHS[Len_Chromo:2 * Len_Chromo]) # 后半部分为机器索引
|
OS = list(CHS[Len_Chromo:2 * Len_Chromo])
|
||||||
Needed_Matrix = self.Order_Matrix(MS)
|
Needed_Matrix = self.Order_Matrix(MS)
|
||||||
JM, TM = Needed_Matrix[0], Needed_Matrix[1]
|
JM, TM = Needed_Matrix[0], Needed_Matrix[1]
|
||||||
|
|
||||||
for i in OS:
|
for i in OS:
|
||||||
Job = i
|
Job = i
|
||||||
O_num = self.Jobs[Job].Current_Processed() # 现在加工的工序
|
O_num = self.Jobs[Job].Current_Processed() # 现在加工的工序
|
||||||
|
|
|
||||||
142
Encode.py
142
Encode.py
|
|
@ -1,7 +1,6 @@
|
||||||
import random
|
import random
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
class Encode:
|
class Encode:
|
||||||
def __init__(self, Matrix, Pop_size, J, J_num, M_num):
|
def __init__(self, Matrix, Pop_size, J, J_num, M_num):
|
||||||
"""
|
"""
|
||||||
|
|
@ -16,25 +15,24 @@ class Encode:
|
||||||
self.J_num = J_num
|
self.J_num = J_num
|
||||||
self.M_num = M_num
|
self.M_num = M_num
|
||||||
self.CHS = []
|
self.CHS = []
|
||||||
# 调整初始化比例:增加随机选择比例到50%
|
self.GS_num = int(0.6 * Pop_size) # 全局选择初始化
|
||||||
self.GS_num = int(0.3 * Pop_size) # 全局选择初始化
|
|
||||||
self.LS_num = int(0.2 * Pop_size) # 局部选择初始化
|
self.LS_num = int(0.2 * Pop_size) # 局部选择初始化
|
||||||
self.RS_num = int(0.5 * Pop_size) # 随机选择初始化(提高比例)
|
self.RS_num = int(0.2 * Pop_size) # 随机选择初始化
|
||||||
self.Len_Chromo = 0
|
self.Len_Chromo = 0
|
||||||
for i in J.values():
|
for i in J.values():
|
||||||
self.Len_Chromo += i # 遍历字典J的所有值并求和,即工序数之和
|
self.Len_Chromo += i # 遍历字典J的所有值并求和,即工序数之和
|
||||||
|
|
||||||
# 生成工序准备的部分(工件排列,天然满足工序顺序约束)
|
# 生成工序准备的部分
|
||||||
def OS_List(self):
|
def OS_List(self):
|
||||||
OS_list = []
|
OS_list = []
|
||||||
for k, v in self.J.items(): # 遍历字典J的所有键值对,初始化工序矩阵
|
for k, v in self.J.items(): # 遍历字典J的所有键值对,初始化工序矩阵
|
||||||
OS_add = [k - 1 for j in range(v)] # 工件索引从0开始
|
OS_add = [k - 1 for j in range(v)]
|
||||||
OS_list.extend(OS_add)
|
OS_list.extend(OS_add)
|
||||||
return OS_list
|
return OS_list
|
||||||
|
|
||||||
# 生成初始化矩阵
|
# 生成初始化矩阵
|
||||||
def CHS_Matrix(self, C_num):
|
def CHS_Matrix(self, C_num):
|
||||||
return np.zeros([C_num, self.Len_Chromo * 2], dtype=int) # 工件排列+机器索引
|
return np.zeros([C_num, self.Len_Chromo], dtype=int)
|
||||||
|
|
||||||
# 定位每个工件的每道工序的位置,Job第几个工件,Operation第几道工序
|
# 定位每个工件的每道工序的位置,Job第几个工件,Operation第几道工序
|
||||||
def Site(self, Job, Operation):
|
def Site(self, Job, Operation):
|
||||||
|
|
@ -48,74 +46,88 @@ class Encode:
|
||||||
|
|
||||||
# 全局初始化
|
# 全局初始化
|
||||||
def Global_initial(self):
|
def Global_initial(self):
|
||||||
CHS = self.CHS_Matrix(self.GS_num)
|
MS = self.CHS_Matrix(self.GS_num) # 根据GS_num生成种群
|
||||||
OS_list = self.OS_List()
|
OS_list = self.OS_List()
|
||||||
|
OS = self.CHS_Matrix(self.GS_num)
|
||||||
for i in range(self.GS_num):
|
for i in range(self.GS_num):
|
||||||
random.shuffle(OS_list)
|
Machine_time = np.zeros(self.M_num, dtype=int) # 步骤1 生成一个整型数组,长度为机器数,且初始化每个元素为0
|
||||||
OS = OS_list.copy()
|
random.shuffle(OS_list) # 生成工序排序部分
|
||||||
MS = [0] * self.Len_Chromo
|
OS[i] = np.array(OS_list) # 随机打乱后将其赋值给OS的某一行
|
||||||
|
GJ_list = [i_1 for i_1 in range(self.J_num)] # 生成工件集
|
||||||
Machine_time = np.zeros(self.M_num, dtype=int)
|
random.shuffle(GJ_list) # 随机打乱工件集
|
||||||
GJ_list = [i_1 for i_1 in range(self.J_num)]
|
for g in GJ_list: # 选择第一个工件
|
||||||
random.shuffle(GJ_list)
|
h = self.Matrix[g] # h为第一个工件包含的工序对应的时间矩阵
|
||||||
|
for j in range(len(h)): # 从此工件的第一个工序开始
|
||||||
for g in GJ_list:
|
D = h[j] # D为第一个工件的第一个工序对应的时间矩阵
|
||||||
h = self.Matrix[g]
|
List_Machine_weizhi = []
|
||||||
for j in range(len(h)):
|
for k in range(len(D)): # 确定工序可用的机器位于第几个位置
|
||||||
D = h[j]
|
Useing_Machine = D[k]
|
||||||
List_Machine_weizhi = [k for k, val in enumerate(D) if val != 9999]
|
if Useing_Machine != 9999:
|
||||||
Machine_Select = [Machine_time[m] + D[m] for m in List_Machine_weizhi]
|
List_Machine_weizhi.append(k)
|
||||||
Min_time = min(Machine_Select)
|
Machine_Select = []
|
||||||
K = Machine_Select.index(Min_time)
|
for Machine_add in List_Machine_weizhi: # 将机器时间数组对应位置和工序可选机器的时间相加
|
||||||
MS[self.Site(g, j)] = K
|
Machine_Select.append(Machine_time[Machine_add] + D[Machine_add])
|
||||||
Machine_time[List_Machine_weizhi[K]] += D[List_Machine_weizhi[K]]
|
Min_time = min(Machine_Select) # 选出时间最小的机器
|
||||||
|
K = Machine_Select.index(Min_time) # 第一次出现最小时间的位置
|
||||||
CHS[i] = np.hstack((OS, MS)) # 工件排列 + 机器索引(紧凑编码)
|
I = List_Machine_weizhi[K] # 所有机器里的第I个机器
|
||||||
return CHS
|
Machine_time[I] += Min_time # 相应的机器位置加上最小时间
|
||||||
|
site = self.Site(g, j) # 定位每个工件的每道工序的位置
|
||||||
|
MS[i][site] = K # 即将每个工序选择的第K个机器赋值到对应位置
|
||||||
|
CHS1 = np.hstack((MS, OS)) # 将MS和OS整合为一个矩阵
|
||||||
|
return CHS1
|
||||||
|
|
||||||
# 局部初始化
|
# 局部初始化
|
||||||
def Local_initial(self):
|
def Local_initial(self):
|
||||||
CHS = self.CHS_Matrix(self.LS_num)
|
MS = self.CHS_Matrix(self.LS_num) # 根据LS_num生成局部选择的种群大小
|
||||||
OS_list = self.OS_List()
|
OS_list = self.OS_List()
|
||||||
|
OS = self.CHS_Matrix(self.LS_num)
|
||||||
for i in range(self.LS_num):
|
for i in range(self.LS_num):
|
||||||
random.shuffle(OS_list)
|
random.shuffle(OS_list) # (随机打乱)生成工序排序部分
|
||||||
OS = OS_list.copy()
|
OS[i] = np.array(OS_list)
|
||||||
MS = [0] * self.Len_Chromo
|
GJ_List = [i_1 for i_1 in range(self.J_num)] # 生成工件集
|
||||||
|
for g in GJ_List: # 选择第一个工件(注意:不用随机打乱了)
|
||||||
|
Machine_time = np.zeros(self.M_num, dtype=int) # 局部初始化,每个工件重新初始化
|
||||||
|
h = self.Matrix[g] # h为第一个工件包含的工序对应的时间矩阵
|
||||||
|
for j in range(len(h)): # 从选择的工件的第一个工序开始
|
||||||
|
D = h[j] # 此工件第一个工序对应的机器加工时间矩阵
|
||||||
|
List_Machine_weizhi = []
|
||||||
|
for k in range(len(D)): # 确定工序可用的机器位于第几个位置
|
||||||
|
Useing_Machine = D[k]
|
||||||
|
if Useing_Machine != 9999:
|
||||||
|
List_Machine_weizhi.append(k)
|
||||||
|
Machine_Select = []
|
||||||
|
for Machine_add in List_Machine_weizhi: # 将机器时间数组对应位置和工序可选机器的时间相加
|
||||||
|
Machine_Select.append(Machine_time[Machine_add] + D[Machine_add])
|
||||||
|
Min_time = min(Machine_Select) # 选出这些时间里最小的
|
||||||
|
K = Machine_Select.index(Min_time) # 第一次出现最小时间的位置
|
||||||
|
I = List_Machine_weizhi[K] # 所有机器里的第I个机器
|
||||||
|
Machine_time[I] += Min_time
|
||||||
|
site = self.Site(g, j) # 定位每个工件的每道工序的位置
|
||||||
|
MS[i][site] = K # 即将每个工序选择的第K个机器赋值到对应位置
|
||||||
|
CHS1 = np.hstack((MS, OS)) # 将MS和OS整合为一个矩阵
|
||||||
|
return CHS1
|
||||||
|
|
||||||
GJ_List = [i_1 for i_1 in range(self.J_num)]
|
# 随机初始化
|
||||||
for g in GJ_List:
|
|
||||||
Machine_time = np.zeros(self.M_num, dtype=int)
|
|
||||||
h = self.Matrix[g]
|
|
||||||
for j in range(len(h)):
|
|
||||||
D = h[j]
|
|
||||||
List_Machine_weizhi = [k for k, val in enumerate(D) if val != 9999]
|
|
||||||
Machine_Select = [Machine_time[m] + D[m] for m in List_Machine_weizhi]
|
|
||||||
Min_time = min(Machine_Select)
|
|
||||||
K = Machine_Select.index(Min_time)
|
|
||||||
MS[self.Site(g, j)] = K
|
|
||||||
Machine_time[List_Machine_weizhi[K]] += D[List_Machine_weizhi[K]]
|
|
||||||
|
|
||||||
CHS[i] = np.hstack((OS, MS))
|
|
||||||
return CHS
|
|
||||||
|
|
||||||
# 随机初始化(提高比例到50%)
|
|
||||||
def Random_initial(self):
|
def Random_initial(self):
|
||||||
CHS = self.CHS_Matrix(self.RS_num)
|
MS = self.CHS_Matrix(self.RS_num) # 根据RS_num生成随机选择的种群大小
|
||||||
OS_list = self.OS_List()
|
OS_list = self.OS_List()
|
||||||
|
OS = self.CHS_Matrix(self.RS_num)
|
||||||
for i in range(self.RS_num):
|
for i in range(self.RS_num):
|
||||||
random.shuffle(OS_list)
|
random.shuffle(OS_list)
|
||||||
OS = OS_list.copy()
|
OS[i] = np.array(OS_list)
|
||||||
MS = [0] * self.Len_Chromo
|
GJ_List = [i_1 for i_1 in range(self.J_num)] # 生成工件集
|
||||||
|
for g in GJ_List: # 选择第一个工件
|
||||||
for g in range(self.J_num):
|
|
||||||
h = self.Matrix[g]
|
h = self.Matrix[g]
|
||||||
for j in range(len(h)):
|
for j in range(len(h)): # 选择第一个工件的第一个工序
|
||||||
D = h[j]
|
D = h[j] # 此工件第一个工序可加工的机器对应的时间矩阵
|
||||||
List_Machine_weizhi = [k for k, val in enumerate(D) if val != 9999]
|
List_Machine_weizhi = []
|
||||||
# 随机选择可用机器
|
for k in range(len(D)):
|
||||||
selected = random.choice(List_Machine_weizhi)
|
Useing_Machine = D[k]
|
||||||
K = List_Machine_weizhi.index(selected)
|
if Useing_Machine != 9999:
|
||||||
MS[self.Site(g, j)] = K
|
List_Machine_weizhi.append(k)
|
||||||
|
number = random.choice(List_Machine_weizhi) # 从可选择的机器编号中随机选择一个
|
||||||
CHS[i] = np.hstack((OS, MS))
|
K = List_Machine_weizhi.index(number) # 即为该工序可选择的机器里的第K个机器
|
||||||
return CHS
|
site = self.Site(g, j) # 定位每个工件的每道工序的位置
|
||||||
|
MS[i][site] = K # 即将每个工序选择的第K个机器赋值到对应位置
|
||||||
|
CHS1 = np.hstack((MS, OS))
|
||||||
|
return CHS1
|
||||||
215
GA.py
215
GA.py
|
|
@ -11,10 +11,10 @@ class GA():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.Pop_size = 500 # 种群数量
|
self.Pop_size = 500 # 种群数量
|
||||||
self.Pc = 0.8 # 交叉概率
|
self.Pc = 0.8 # 交叉概率
|
||||||
self.Pm = 0.15 # 降低变异概率到0.15
|
self.Pm = 0.3 # 变异概率
|
||||||
self.Pv = 0.5 # 选择何种方式进行交叉的概率阈值
|
self.Pv = 0.5 # 选择何种方式进行交叉的概率阈值
|
||||||
self.Pw = 0.95 # 选择何种方式进行变异的概率阈值
|
self.Pw = 0.95 # 选择何种方式进行变异的概率阈值
|
||||||
self.Max_Itertions = 100
|
self.Max_Itertions = 100 # 最大迭代次数
|
||||||
|
|
||||||
# 适应度
|
# 适应度
|
||||||
def fitness(self, CHS, J, Processing_time, M_num, Len):
|
def fitness(self, CHS, J, Processing_time, M_num, Len):
|
||||||
|
|
@ -24,141 +24,128 @@ class GA():
|
||||||
Fit.append(d.decode(CHS[i], Len))
|
Fit.append(d.decode(CHS[i], Len))
|
||||||
return Fit
|
return Fit
|
||||||
|
|
||||||
def pmx_crossover(self, parent1, parent2, length):
|
|
||||||
if length <= 1:
|
|
||||||
return parent1.copy(), parent2.copy()
|
|
||||||
|
|
||||||
# 确保交叉区间[a, b]有效(a < b)
|
|
||||||
a = random.randint(0, length - 2)
|
|
||||||
b = random.randint(a + 1, length - 1)
|
|
||||||
|
|
||||||
child1 = parent1.copy()
|
|
||||||
child2 = parent2.copy()
|
|
||||||
|
|
||||||
# 建立映射表(parent1[a:b+1]与parent2[a:b+1]的对应关系)
|
|
||||||
map1 = {parent1[i]: parent2[i] for i in range(a, b + 1)} # p1→p2的映射
|
|
||||||
map2 = {parent2[i]: parent1[i] for i in range(a, b + 1)} # p2→p1的映射
|
|
||||||
|
|
||||||
# 处理child1:交叉区间外的元素替换
|
|
||||||
for i in range(length):
|
|
||||||
if i < a or i > b:
|
|
||||||
val = child1[i]
|
|
||||||
# 限制循环次数,避免无限循环(最多循环len(map1)次)
|
|
||||||
loop_count = 0
|
|
||||||
max_loop = len(map1)
|
|
||||||
while val in map1 and map1[val] in parent1[a:b + 1] and loop_count < max_loop:
|
|
||||||
val = map1[val]
|
|
||||||
loop_count += 1
|
|
||||||
child1[i] = map1.get(val, val) # 若超出循环次数,直接使用当前val
|
|
||||||
|
|
||||||
# 处理child2:交叉区间外的元素替换
|
|
||||||
for i in range(length):
|
|
||||||
if i < a or i > b:
|
|
||||||
val = child2[i]
|
|
||||||
loop_count = 0
|
|
||||||
max_loop = len(map2)
|
|
||||||
while val in map2 and map2[val] in parent2[a:b + 1] and loop_count < max_loop:
|
|
||||||
val = map2[val]
|
|
||||||
loop_count += 1
|
|
||||||
child2[i] = map2.get(val, val)
|
|
||||||
|
|
||||||
return child1, child2
|
|
||||||
|
|
||||||
# 机器部分交叉
|
# 机器部分交叉
|
||||||
def machine_cross(self, CHS1, CHS2, T0):
|
def machine_cross(self, CHS1, CHS2, T0):
|
||||||
"""
|
"""
|
||||||
:param CHS1: 基因1
|
:param CHS1: 机器选 择部分的基因1
|
||||||
:param CHS2: 基因2
|
:param CHS2: 机器选择部分的基因2
|
||||||
:param T0: 工序总数
|
:param T0: 工序总数
|
||||||
:return: 交叉后的基因
|
:return: 交叉后的机器选择部分的基因
|
||||||
"""
|
"""
|
||||||
OS1, MS1 = CHS1[:T0], CHS1[T0:]
|
T_r = [j for j in range(T0)]
|
||||||
OS2, MS2 = CHS2[:T0], CHS2[T0:]
|
r = random.randint(1, 10) # 在区间[1,T0]内产生一个整数r
|
||||||
|
random.shuffle(T_r) #直接打乱T_r,原地修改
|
||||||
# 随机选择交叉位置
|
R = T_r[0:r] # 按照随机数r产生r个互不相等的整数
|
||||||
T_r = list(range(T0))
|
OS_1 = CHS1[O_num:2 * T0]
|
||||||
random.shuffle(T_r)
|
OS_2 = CHS2[O_num:2 * T0]
|
||||||
r = random.randint(1, T0 // 2) # 交叉部分长度
|
MS_1 = CHS2[0:T0]
|
||||||
R = T_r[:r]
|
MS_2 = CHS1[0:T0]
|
||||||
|
|
||||||
# 交换机器选择部分
|
|
||||||
for i in R:
|
for i in R:
|
||||||
MS1[i], MS2[i] = MS2[i], MS1[i]
|
K, K_2 = MS_1[i], MS_2[i]
|
||||||
|
MS_1[i], MS_2[i] = K_2, K
|
||||||
return np.hstack((OS1, MS1)), np.hstack((OS2, MS2))
|
CHS1 = np.hstack((MS_1, OS_1))
|
||||||
|
CHS2 = np.hstack((MS_2, OS_2))
|
||||||
# 工序部分交叉(使用PMX交叉)
|
|
||||||
def operation_cross(self, CHS1, CHS2, T0, J_num):
|
|
||||||
OS1 = list(CHS1[T0:2 * T0]) # 确保转换为列表
|
|
||||||
OS2 = list(CHS2[T0:2 * T0])
|
|
||||||
MS1 = CHS1[0:T0].copy()
|
|
||||||
MS2 = CHS2[0:T0].copy()
|
|
||||||
|
|
||||||
# 调用修复后的PMX交叉
|
|
||||||
new_os1, new_os2 = self.pmx_crossover(OS1, OS2, T0)
|
|
||||||
|
|
||||||
CHS1 = np.hstack((MS1, new_os1))
|
|
||||||
CHS2 = np.hstack((MS2, new_os2))
|
|
||||||
return CHS1, CHS2
|
return CHS1, CHS2
|
||||||
|
|
||||||
# 机器部分变异(仅在可用机器集中选择)
|
# 工序部分交叉
|
||||||
|
def operation_cross(self, CHS1, CHS2, T0, J_num):
|
||||||
|
"""
|
||||||
|
:param CHS1: 工序选择部分的基因1
|
||||||
|
:param CHS2: 工序选择部分的基因2
|
||||||
|
:param T0: 工序总数
|
||||||
|
:param J_num: 工件总数
|
||||||
|
:return: 交叉后的工序选择部分的基因
|
||||||
|
"""
|
||||||
|
OS_1 = CHS1[T0:2 * T0]
|
||||||
|
OS_2 = CHS2[T0:2 * T0]
|
||||||
|
MS_1 = CHS1[0:T0]
|
||||||
|
MS_2 = CHS2[0:T0]
|
||||||
|
Job_list = [i for i in range(J_num)]
|
||||||
|
random.shuffle(Job_list)
|
||||||
|
r = random.randint(1, J_num - 1)
|
||||||
|
Set1 = Job_list[0:r]
|
||||||
|
new_os = list(np.zeros(T0, dtype=int))
|
||||||
|
for k, v in enumerate(OS_1):
|
||||||
|
if v in Set1:
|
||||||
|
new_os[k] = v + 1
|
||||||
|
for i in OS_2:
|
||||||
|
if i not in Set1:
|
||||||
|
Site = new_os.index(0)
|
||||||
|
new_os[Site] = i + 1
|
||||||
|
new_os = np.array([j - 1 for j in new_os])
|
||||||
|
CHS1 = np.hstack((MS_1, new_os))
|
||||||
|
CHS2 = np.hstack((MS_2, new_os))
|
||||||
|
return CHS1, CHS2
|
||||||
|
|
||||||
|
# 机器部分变异
|
||||||
def machine_variation(self, CHS, O, T0, J):
|
def machine_variation(self, CHS, O, T0, J):
|
||||||
"""
|
"""
|
||||||
:param CHS: 基因
|
:param CHS: 机器选择部分的基因
|
||||||
:param O: 加工时间矩阵
|
:param O: 加工时间矩阵
|
||||||
:param T0: 工序总数
|
:param T0: 工序总数
|
||||||
:param J: 各工件加工信息
|
:param J: 各工件加工信息
|
||||||
:return: 变异后的基因
|
:return: 变异后的机器选择部分的基因
|
||||||
"""
|
"""
|
||||||
OS = CHS[:T0]
|
Tr = [i_num for i_num in range(T0)]
|
||||||
MS = CHS[T0:]
|
MS = CHS[0:T0]
|
||||||
|
OS = CHS[T0:2 * T0]
|
||||||
# 确定变异位置数量
|
# 机器选择部分
|
||||||
r = random.randint(1, max(1, T0 // 10)) # 最多变异10%的位置
|
r = random.randint(1, T0 - 1) # 在变异染色体中选择r个位置
|
||||||
positions = random.sample(range(T0), r)
|
random.shuffle(Tr)
|
||||||
|
T_r = Tr[0:r]
|
||||||
for pos in positions:
|
for num in T_r:
|
||||||
# 找到该位置对应的工件和工序
|
T_0 = [j for j in range(T0)]
|
||||||
site = 0
|
K = []
|
||||||
job = -1
|
Site = 0
|
||||||
op = -1
|
|
||||||
for k, v in J.items():
|
for k, v in J.items():
|
||||||
if site + v > pos:
|
K.append(T_0[Site:Site + v])
|
||||||
job = k - 1 # 转换为0索引
|
Site += v
|
||||||
op = pos - site
|
for i in range(len(K)):
|
||||||
|
if num in K[i]:
|
||||||
|
O_i = i
|
||||||
|
O_j = K[i].index(num)
|
||||||
break
|
break
|
||||||
site += v
|
Machine_using = O[O_i][O_j]
|
||||||
|
Machine_time = []
|
||||||
# 获取该工序的可用机器
|
for j in Machine_using:
|
||||||
D = O[job][op]
|
if j != 9999:
|
||||||
available_machines = [k for k, val in enumerate(D) if val != 9999]
|
Machine_time.append(j)
|
||||||
if len(available_machines) <= 1:
|
Min_index = Machine_time.index(min(Machine_time))
|
||||||
continue # 只有一个可用机器时不变异
|
MS[num] = Min_index
|
||||||
|
CHS = np.hstack((MS, OS))
|
||||||
# 随机选择一个不同的可用机器
|
return CHS
|
||||||
current_idx = MS[pos]
|
|
||||||
current_machine = available_machines[current_idx]
|
|
||||||
new_machine = random.choice([m for m in available_machines if m != current_machine])
|
|
||||||
MS[pos] = available_machines.index(new_machine)
|
|
||||||
|
|
||||||
return np.hstack((OS, MS))
|
|
||||||
|
|
||||||
# 工序部分变异
|
# 工序部分变异
|
||||||
def operation_variation(self, CHS, T0, J_num, J, O, M_num):
|
def operation_variation(self, CHS, T0, J_num, J, O, M_num):
|
||||||
"""
|
"""
|
||||||
:param CHS: 基因
|
:param CHS: 工序选择部分的基因
|
||||||
:param T0: 工序总数
|
:param T0: 工序总数
|
||||||
:param J_num: 工件总数
|
:param J_num: 工件总数
|
||||||
:param J: 各工件加工信息
|
:param J: 各工件加工信息
|
||||||
:param O: 加工时间矩阵
|
:param O: 加工时间矩阵
|
||||||
:param M_num: 机器总数
|
:param M_num: 机器总数
|
||||||
:return: 变异后的基因
|
:return: 变异后的工序选择部分的基因
|
||||||
"""
|
"""
|
||||||
OS = list(CHS[:T0])
|
MS = CHS[0:T0]
|
||||||
MS = CHS[T0:]
|
OS = list(CHS[T0:2 * T0])
|
||||||
|
r = random.randint(1, J_num - 1)
|
||||||
# 随机选择两个位置交换
|
Tr = [i for i in range(J_num)]
|
||||||
i, j = random.sample(range(T0), 2)
|
random.shuffle(Tr)
|
||||||
OS[i], OS[j] = OS[j], OS[i]
|
Tr = Tr[0:r]
|
||||||
|
J_os = dict(enumerate(OS)) # 随机选择r个不同的基因
|
||||||
return np.hstack((OS, MS))
|
J_os = sorted(J_os.items(), key=lambda d: d[1])
|
||||||
|
Site = []
|
||||||
|
for i in range(r):
|
||||||
|
Site.append(OS.index(Tr[i]))
|
||||||
|
A = list(itertools.permutations(Tr, r))
|
||||||
|
A_CHS = []
|
||||||
|
for i in range(len(A)):
|
||||||
|
for j in range(len(A[i])):
|
||||||
|
OS[Site[j]] = A[i][j]
|
||||||
|
C_I = np.hstack((MS, OS)) # 水平堆叠,即合成
|
||||||
|
A_CHS.append(C_I)
|
||||||
|
Fit = []
|
||||||
|
for i in range(len(A_CHS)):
|
||||||
|
d = Decode(J, O, M_num)
|
||||||
|
Fit.append(d.decode(CHS, T0))
|
||||||
|
return A_CHS[Fit.index(min(Fit))]
|
||||||
|
|
|
||||||
16
NSGA2.py
16
NSGA2.py
|
|
@ -1,5 +1,4 @@
|
||||||
import random
|
import random
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
class NSGA2:
|
class NSGA2:
|
||||||
def __init__(self, pop_size, obj_num):
|
def __init__(self, pop_size, obj_num):
|
||||||
|
|
@ -9,7 +8,7 @@ class NSGA2:
|
||||||
def fast_non_dominated_sort(self, pop_obj):
|
def fast_non_dominated_sort(self, pop_obj):
|
||||||
"""快速非支配排序"""
|
"""快速非支配排序"""
|
||||||
pop_size = len(pop_obj)
|
pop_size = len(pop_obj)
|
||||||
dominated = [[] for _ in range(pop_size)] # 被支配个体列表
|
dominated = [[] for _ in range(pop_size)] # 被支配个体列表,_通常用作占位符变量,表示不关心这个变量的具体值
|
||||||
rank = [0] * pop_size # 个体的非支配等级
|
rank = [0] * pop_size # 个体的非支配等级
|
||||||
n = [0] * pop_size # 支配该个体的个体数量
|
n = [0] * pop_size # 支配该个体的个体数量
|
||||||
|
|
||||||
|
|
@ -51,7 +50,7 @@ class NSGA2:
|
||||||
"""计算拥挤度距离"""
|
"""计算拥挤度距离"""
|
||||||
pop_size = len(pop_obj)
|
pop_size = len(pop_obj)
|
||||||
distance = [0.0] * pop_size
|
distance = [0.0] * pop_size
|
||||||
max_rank = max(rank) if rank else 0
|
max_rank = max(rank)
|
||||||
|
|
||||||
# 对每个等级的个体计算拥挤度
|
# 对每个等级的个体计算拥挤度
|
||||||
for r in range(max_rank + 1):
|
for r in range(max_rank + 1):
|
||||||
|
|
@ -80,7 +79,7 @@ class NSGA2:
|
||||||
return distance
|
return distance
|
||||||
|
|
||||||
def selection(self, pop, pop_obj):
|
def selection(self, pop, pop_obj):
|
||||||
"""选择操作:基于非支配排序和拥挤度的锦标赛选择,惩罚拥挤度为0的个体"""
|
"""选择操作:基于非支配排序和拥挤度的锦标赛选择"""
|
||||||
pop_size = len(pop)
|
pop_size = len(pop)
|
||||||
rank = self.fast_non_dominated_sort(pop_obj)
|
rank = self.fast_non_dominated_sort(pop_obj)
|
||||||
distance = self.crowding_distance(pop_obj, rank)
|
distance = self.crowding_distance(pop_obj, rank)
|
||||||
|
|
@ -96,14 +95,9 @@ class NSGA2:
|
||||||
selected.append(pop[i])
|
selected.append(pop[i])
|
||||||
elif rank[i] > rank[j]:
|
elif rank[i] > rank[j]:
|
||||||
selected.append(pop[j])
|
selected.append(pop[j])
|
||||||
# 等级相同则选择拥挤度大的个体(惩罚拥挤度为0的重复解)
|
# 等级相同则选择拥挤度大的个体
|
||||||
else:
|
else:
|
||||||
# 对拥挤度为0的个体进行惩罚
|
if distance[i] > distance[j]:
|
||||||
if distance[i] == 0 and distance[j] > 0:
|
|
||||||
selected.append(pop[j])
|
|
||||||
elif distance[j] == 0 and distance[i] > 0:
|
|
||||||
selected.append(pop[i])
|
|
||||||
elif distance[i] >= distance[j]:
|
|
||||||
selected.append(pop[i])
|
selected.append(pop[i])
|
||||||
else:
|
else:
|
||||||
selected.append(pop[j])
|
selected.append(pop[j])
|
||||||
|
|
|
||||||
68
main.py
68
main.py
|
|
@ -7,6 +7,7 @@ from GA import GA
|
||||||
from Instance import *
|
from Instance import *
|
||||||
from NSGA2 import NSGA2
|
from NSGA2 import NSGA2
|
||||||
|
|
||||||
|
|
||||||
# 绘制甘特图
|
# 绘制甘特图
|
||||||
def Gantt(Machines):
|
def Gantt(Machines):
|
||||||
M = ['red', 'blue', 'yellow', 'orange', 'green', 'palegoldenrod', 'purple', 'pink', 'Thistle', 'Magenta',
|
M = ['red', 'blue', 'yellow', 'orange', 'green', 'palegoldenrod', 'purple', 'pink', 'Thistle', 'Magenta',
|
||||||
|
|
@ -28,6 +29,7 @@ def Gantt(Machines):
|
||||||
plt.savefig('优化后排程方案的甘特图.png')
|
plt.savefig('优化后排程方案的甘特图.png')
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# 初始化参数
|
# 初始化参数
|
||||||
g = GA()
|
g = GA()
|
||||||
|
|
@ -42,40 +44,18 @@ if __name__ == '__main__':
|
||||||
Optimal_solutions = [] # 存储非支配解
|
Optimal_solutions = [] # 存储非支配解
|
||||||
Optimal_fit_values = [] # 存储非支配解的目标值
|
Optimal_fit_values = [] # 存储非支配解的目标值
|
||||||
|
|
||||||
# 新增:用于存储所有代的所有解
|
|
||||||
all_solutions = [] # 存储所有个体(染色体)
|
|
||||||
all_fitnesses = [] # 存储所有个体的目标值
|
|
||||||
|
|
||||||
# 早停策略参数
|
|
||||||
early_stop_counter = 0
|
|
||||||
max_stagnation = 50 # 连续50代无更新则停止
|
|
||||||
best_fitness_history = []
|
|
||||||
|
|
||||||
for i in range(g.Max_Itertions):
|
for i in range(g.Max_Itertions):
|
||||||
print(f"iter_{i} start!")
|
print(f"iter_{i} start!")
|
||||||
Fit = g.fitness(C, J, Processing_time, M_num, O_num)
|
Fit = g.fitness(C, J, Processing_time, M_num, O_num)
|
||||||
|
|
||||||
# 记录当前代的所有解
|
|
||||||
all_solutions.extend(C)
|
|
||||||
all_fitnesses.extend(Fit)
|
|
||||||
|
|
||||||
# 非支配排序
|
# 非支配排序
|
||||||
rank = nsga2.fast_non_dominated_sort(Fit)
|
rank = nsga2.fast_non_dominated_sort(Fit)
|
||||||
current_non_dominated = [C[j] for j in range(len(C)) if rank[j] == 0]
|
current_non_dominated = [C[j] for j in range(len(C)) if rank[j] == 0]
|
||||||
current_non_dominated_fit = [Fit[j] for j in range(len(C)) if rank[j] == 0]
|
current_non_dominated_fit = [Fit[j] for j in range(len(C)) if rank[j] == 0]
|
||||||
|
|
||||||
# 更新全局非支配解
|
# 更新全局非支配解
|
||||||
new_non_dominated = False
|
Optimal_solutions.extend(current_non_dominated)
|
||||||
for sol, fit in zip(current_non_dominated, current_non_dominated_fit):
|
Optimal_fit_values.extend(current_non_dominated_fit)
|
||||||
is_dominated = False
|
|
||||||
for existing_fit in Optimal_fit_values:
|
|
||||||
if all(existing_fit[d] <= fit[d] for d in range(2)) and any(existing_fit[d] < fit[d] for d in range(2)):
|
|
||||||
is_dominated = True
|
|
||||||
break
|
|
||||||
if not is_dominated:
|
|
||||||
Optimal_solutions.append(sol)
|
|
||||||
Optimal_fit_values.append(fit)
|
|
||||||
new_non_dominated = True
|
|
||||||
|
|
||||||
# 对全局解重新筛选非支配解
|
# 对全局解重新筛选非支配解
|
||||||
if Optimal_solutions:
|
if Optimal_solutions:
|
||||||
|
|
@ -91,20 +71,6 @@ if __name__ == '__main__':
|
||||||
Optimal_solutions = [Optimal_solutions[j] for j in sorted_indices[:g.Pop_size]]
|
Optimal_solutions = [Optimal_solutions[j] for j in sorted_indices[:g.Pop_size]]
|
||||||
Optimal_fit_values = [Optimal_fit_values[j] for j in sorted_indices[:g.Pop_size]]
|
Optimal_fit_values = [Optimal_fit_values[j] for j in sorted_indices[:g.Pop_size]]
|
||||||
|
|
||||||
# 早停策略检查
|
|
||||||
if new_non_dominated:
|
|
||||||
early_stop_counter = 0
|
|
||||||
else:
|
|
||||||
early_stop_counter += 1
|
|
||||||
if early_stop_counter >= max_stagnation:
|
|
||||||
print(f"早停于第{i}代,连续{max_stagnation}代无新的非支配解")
|
|
||||||
break
|
|
||||||
|
|
||||||
# 定期引入随机解(每20代)
|
|
||||||
if i % 20 == 0 and i > 0:
|
|
||||||
random_solutions = e.Random_initial()[:int(g.Pop_size * 0.1)] # 引入10%的随机解
|
|
||||||
C = np.vstack((C[:-len(random_solutions)], random_solutions))
|
|
||||||
|
|
||||||
# 选择操作(基于NSGA-II)
|
# 选择操作(基于NSGA-II)
|
||||||
selected = nsga2.selection(C, Fit)
|
selected = nsga2.selection(C, Fit)
|
||||||
|
|
||||||
|
|
@ -162,23 +128,15 @@ if __name__ == '__main__':
|
||||||
print(f"\n选中解的目标值: (最大完工时间: {final_fitness[0]}, 负载标准差: {final_fitness[1]:.2f})")
|
print(f"\n选中解的目标值: (最大完工时间: {final_fitness[0]}, 负载标准差: {final_fitness[1]:.2f})")
|
||||||
Gantt(d.Machines)
|
Gantt(d.Machines)
|
||||||
|
|
||||||
# 绘制帕累托前沿(非支配解)
|
# 绘制帕累托前沿
|
||||||
if Optimal_fit_values:
|
if Optimal_fit_values:
|
||||||
plt.figure(figsize=(10, 6))
|
plt.figure()
|
||||||
cmax_nd = [fit[0] for fit in Optimal_fit_values]
|
cmax = [fit[0] for fit in Optimal_fit_values]
|
||||||
load_std_nd = [fit[1] for fit in Optimal_fit_values]
|
load_std = [fit[1] for fit in Optimal_fit_values]
|
||||||
plt.scatter(cmax_nd, load_std_nd, color='red', label='Non-dominated solutions', zorder=5)
|
plt.scatter(cmax, load_std, color='red')
|
||||||
|
plt.title('ParetoFront')
|
||||||
# 绘制所有解的点图(所有代的所有个体)
|
plt.xlabel('maxtime')
|
||||||
if all_fitnesses:
|
plt.ylabel('standard deviation')
|
||||||
cmax_all = [fit[0] for fit in all_fitnesses]
|
|
||||||
load_std_all = [fit[1] for fit in all_fitnesses]
|
|
||||||
plt.scatter(cmax_all, load_std_all, color='blue', alpha=0.5, label='All solutions', zorder=1)
|
|
||||||
|
|
||||||
plt.title('All Solutions and Pareto Front')
|
|
||||||
plt.xlabel('Max Completion Time (Cmax)')
|
|
||||||
plt.ylabel('Load Standard Deviation')
|
|
||||||
plt.legend()
|
|
||||||
plt.grid(True)
|
plt.grid(True)
|
||||||
plt.savefig('all_solutions_pareto.png')
|
plt.savefig('pareto.png')
|
||||||
plt.show()
|
plt.show()
|
||||||
BIN
优化后排程方案的甘特图.png
BIN
优化后排程方案的甘特图.png
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Loading…
Reference in New Issue