Compare commits

..

2 Commits

6 changed files with 253 additions and 202 deletions

View File

@ -2,6 +2,7 @@ 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):
""" """
@ -31,7 +32,7 @@ class Decode:
Site = 0 Site = 0
# 按照基因的MS部分按工件序号划分 # 按照基因的MS部分按工件序号划分
for S_i in self.J.values(): for S_i in self.J.values():
Ms_decompose.append(MS[Site:Site + S_i]) #对MS按照每道工序数进行切分 Ms_decompose.append(MS[Site:Site + S_i]) # 对MS按照每道工序数进行切分
Site += S_i Site += S_i
for i in range(len(Ms_decompose)): # len(Ms_decompose)表示工件数 for i in range(len(Ms_decompose)): # len(Ms_decompose)表示工件数
JM_i = [] JM_i = []
@ -79,11 +80,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: MS与OS的分解线 :param Len_Chromo: 工序总数OS和MS各占一半长度
:return: 双目标值 [最大加工时间, 负载标准差] :return: 双目标值 [最大加工时间, 负载标准差]
""" """
# 重置状态 # 重置状态
@ -102,10 +103,11 @@ class Decode:
job.Last_Processing_Machine = None job.Last_Processing_Machine = None
job.Last_Processing_end_time = 0 job.Last_Processing_end_time = 0
MS = list(CHS[0:Len_Chromo]) OS = list(CHS[0:Len_Chromo]) # 前半部分为工件排列
OS = list(CHS[Len_Chromo:2 * Len_Chromo]) MS = 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
View File

@ -1,6 +1,7 @@
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):
""" """
@ -15,24 +16,25 @@ 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 = []
self.GS_num = int(0.6 * Pop_size) # 全局选择初始化 # 调整初始化比例增加随机选择比例到50%
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.2 * Pop_size) # 随机选择初始化 self.RS_num = int(0.5 * 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)] OS_add = [k - 1 for j in range(v)] # 工件索引从0开始
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], dtype=int) return np.zeros([C_num, self.Len_Chromo * 2], dtype=int) # 工件排列+机器索引
# 定位每个工件的每道工序的位置Job第几个工件Operation第几道工序 # 定位每个工件的每道工序的位置Job第几个工件Operation第几道工序
def Site(self, Job, Operation): def Site(self, Job, Operation):
@ -46,88 +48,74 @@ class Encode:
# 全局初始化 # 全局初始化
def Global_initial(self): def Global_initial(self):
MS = self.CHS_Matrix(self.GS_num) # 根据GS_num生成种群 CHS = self.CHS_Matrix(self.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):
Machine_time = np.zeros(self.M_num, dtype=int) # 步骤1 生成一个整型数组长度为机器数且初始化每个元素为0 random.shuffle(OS_list)
random.shuffle(OS_list) # 生成工序排序部分 OS = OS_list.copy()
OS[i] = np.array(OS_list) # 随机打乱后将其赋值给OS的某一行 MS = [0] * self.Len_Chromo
GJ_list = [i_1 for i_1 in range(self.J_num)] # 生成工件集
random.shuffle(GJ_list) # 随机打乱工件集 Machine_time = np.zeros(self.M_num, dtype=int)
for g in GJ_list: # 选择第一个工件 GJ_list = [i_1 for i_1 in range(self.J_num)]
h = self.Matrix[g] # h为第一个工件包含的工序对应的时间矩阵 random.shuffle(GJ_list)
for j in range(len(h)): # 从此工件的第一个工序开始
D = h[j] # D为第一个工件的第一个工序对应的时间矩阵 for g in GJ_list:
List_Machine_weizhi = [] h = self.Matrix[g]
for k in range(len(D)): # 确定工序可用的机器位于第几个位置 for j in range(len(h)):
Useing_Machine = D[k] D = h[j]
if Useing_Machine != 9999: List_Machine_weizhi = [k for k, val in enumerate(D) if val != 9999]
List_Machine_weizhi.append(k) Machine_Select = [Machine_time[m] + D[m] for m in List_Machine_weizhi]
Machine_Select = [] Min_time = min(Machine_Select)
for Machine_add in List_Machine_weizhi: # 将机器时间数组对应位置和工序可选机器的时间相加 K = Machine_Select.index(Min_time)
Machine_Select.append(Machine_time[Machine_add] + D[Machine_add]) MS[self.Site(g, j)] = K
Min_time = min(Machine_Select) # 选出时间最小的机器 Machine_time[List_Machine_weizhi[K]] += D[List_Machine_weizhi[K]]
K = Machine_Select.index(Min_time) # 第一次出现最小时间的位置
I = List_Machine_weizhi[K] # 所有机器里的第I个机器 CHS[i] = np.hstack((OS, MS)) # 工件排列 + 机器索引(紧凑编码)
Machine_time[I] += Min_time # 相应的机器位置加上最小时间 return CHS
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):
MS = self.CHS_Matrix(self.LS_num) # 根据LS_num生成局部选择的种群大小 CHS = self.CHS_Matrix(self.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[i] = np.array(OS_list) OS = OS_list.copy()
GJ_List = [i_1 for i_1 in range(self.J_num)] # 生成工件集 MS = [0] * self.Len_Chromo
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):
MS = self.CHS_Matrix(self.RS_num) # 根据RS_num生成随机选择的种群大小 CHS = self.CHS_Matrix(self.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[i] = np.array(OS_list) OS = OS_list.copy()
GJ_List = [i_1 for i_1 in range(self.J_num)] # 生成工件集 MS = [0] * self.Len_Chromo
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 = [] List_Machine_weizhi = [k for k, val in enumerate(D) if val != 9999]
for k in range(len(D)): # 随机选择可用机器
Useing_Machine = D[k] selected = random.choice(List_Machine_weizhi)
if Useing_Machine != 9999: K = List_Machine_weizhi.index(selected)
List_Machine_weizhi.append(k) MS[self.Site(g, j)] = K
number = random.choice(List_Machine_weizhi) # 从可选择的机器编号中随机选择一个
K = List_Machine_weizhi.index(number) # 即为该工序可选择的机器里的第K个机器 CHS[i] = np.hstack((OS, MS))
site = self.Site(g, j) # 定位每个工件的每道工序的位置 return CHS
MS[i][site] = K # 即将每个工序选择的第K个机器赋值到对应位置
CHS1 = np.hstack((MS, OS))
return CHS1

213
GA.py
View File

@ -11,141 +11,154 @@ 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.3 # 变异概率 self.Pm = 0.15 # 降低变异概率到0.15
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):
Fit = [] Fit = []
for i in range(len(CHS)): for i in range(len(CHS)):
d = Decode(J, Processing_time, M_num) #实例化一个解码器,传入问题参数 d = Decode(J, Processing_time, M_num) # 实例化一个解码器,传入问题参数
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: 交叉后的基因
""" """
T_r = [j for j in range(T0)] OS1, MS1 = CHS1[:T0], CHS1[T0:]
r = random.randint(1, 10) # 在区间[1,T0]内产生一个整数r OS2, MS2 = CHS2[:T0], CHS2[T0:]
random.shuffle(T_r) #直接打乱T_r原地修改
R = T_r[0:r] # 按照随机数r产生r个互不相等的整数 # 随机选择交叉位置
OS_1 = CHS1[O_num:2 * T0] T_r = list(range(T0))
OS_2 = CHS2[O_num:2 * T0] random.shuffle(T_r)
MS_1 = CHS2[0:T0] r = random.randint(1, T0 // 2) # 交叉部分长度
MS_2 = CHS1[0:T0] R = T_r[:r]
# 交换机器选择部分
for i in R: for i in R:
K, K_2 = MS_1[i], MS_2[i] MS1[i], MS2[i] = MS2[i], MS1[i]
MS_1[i], MS_2[i] = K_2, K
CHS1 = np.hstack((MS_1, OS_1))
CHS2 = np.hstack((MS_2, OS_2))
return CHS1, CHS2
# 工序部分交叉 return np.hstack((OS1, MS1)), np.hstack((OS2, MS2))
# 工序部分交叉使用PMX交叉
def operation_cross(self, CHS1, CHS2, T0, J_num): def operation_cross(self, CHS1, CHS2, T0, J_num):
""" OS1 = list(CHS1[T0:2 * T0]) # 确保转换为列表
:param CHS1: 工序选择部分的基因1 OS2 = list(CHS2[T0:2 * T0])
:param CHS2: 工序选择部分的基因2 MS1 = CHS1[0:T0].copy()
:param T0: 工序总数 MS2 = CHS2[0:T0].copy()
:param J_num: 工件总数
:return: 交叉后的工序选择部分的基因 # 调用修复后的PMX交叉
""" new_os1, new_os2 = self.pmx_crossover(OS1, OS2, T0)
OS_1 = CHS1[T0:2 * T0]
OS_2 = CHS2[T0:2 * T0] CHS1 = np.hstack((MS1, new_os1))
MS_1 = CHS1[0:T0] CHS2 = np.hstack((MS2, new_os2))
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 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: 变异后的基因
""" """
Tr = [i_num for i_num in range(T0)] OS = CHS[:T0]
MS = CHS[0:T0] MS = CHS[T0:]
OS = CHS[T0:2 * T0]
# 机器选择部分 # 确定变异位置数量
r = random.randint(1, T0 - 1) # 在变异染色体中选择r个位置 r = random.randint(1, max(1, T0 // 10)) # 最多变异10%的位置
random.shuffle(Tr) positions = random.sample(range(T0), r)
T_r = Tr[0:r]
for num in T_r: for pos in positions:
T_0 = [j for j in range(T0)] # 找到该位置对应的工件和工序
K = [] site = 0
Site = 0 job = -1
op = -1
for k, v in J.items(): for k, v in J.items():
K.append(T_0[Site:Site + v]) if site + v > pos:
Site += v job = k - 1 # 转换为0索引
for i in range(len(K)): op = pos - site
if num in K[i]:
O_i = i
O_j = K[i].index(num)
break break
Machine_using = O[O_i][O_j] site += v
Machine_time = []
for j in Machine_using: # 获取该工序的可用机器
if j != 9999: D = O[job][op]
Machine_time.append(j) available_machines = [k for k, val in enumerate(D) if val != 9999]
Min_index = Machine_time.index(min(Machine_time)) if len(available_machines) <= 1:
MS[num] = Min_index continue # 只有一个可用机器时不变异
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: 变异后的基因
""" """
MS = CHS[0:T0] OS = list(CHS[:T0])
OS = list(CHS[T0:2 * T0]) MS = CHS[T0:]
r = random.randint(1, J_num - 1)
Tr = [i for i in range(J_num)] # 随机选择两个位置交换
random.shuffle(Tr) i, j = random.sample(range(T0), 2)
Tr = Tr[0:r] OS[i], OS[j] = OS[j], OS[i]
J_os = dict(enumerate(OS)) # 随机选择r个不同的基因
J_os = sorted(J_os.items(), key=lambda d: d[1]) return np.hstack((OS, MS))
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))]

View File

@ -1,4 +1,5 @@
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):
@ -8,7 +9,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 # 支配该个体的个体数量
@ -50,7 +51,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) max_rank = max(rank) if rank else 0
# 对每个等级的个体计算拥挤度 # 对每个等级的个体计算拥挤度
for r in range(max_rank + 1): for r in range(max_rank + 1):
@ -79,7 +80,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)
@ -95,9 +96,14 @@ 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:
if distance[i] > distance[j]: # 对拥挤度为0的个体进行惩罚
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
View File

@ -7,7 +7,6 @@ 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',
@ -29,7 +28,6 @@ def Gantt(Machines):
plt.savefig('优化后排程方案的甘特图.png') plt.savefig('优化后排程方案的甘特图.png')
plt.show() plt.show()
if __name__ == '__main__': if __name__ == '__main__':
# 初始化参数 # 初始化参数
g = GA() g = GA()
@ -44,18 +42,40 @@ 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]
# 更新全局非支配解 # 更新全局非支配解
Optimal_solutions.extend(current_non_dominated) new_non_dominated = False
Optimal_fit_values.extend(current_non_dominated_fit) for sol, fit in zip(current_non_dominated, 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:
@ -71,6 +91,20 @@ 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)
@ -128,15 +162,23 @@ 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() plt.figure(figsize=(10, 6))
cmax = [fit[0] for fit in Optimal_fit_values] cmax_nd = [fit[0] for fit in Optimal_fit_values]
load_std = [fit[1] for fit in Optimal_fit_values] load_std_nd = [fit[1] for fit in Optimal_fit_values]
plt.scatter(cmax, load_std, color='red') plt.scatter(cmax_nd, load_std_nd, color='red', label='Non-dominated solutions', zorder=5)
plt.title('ParetoFront')
plt.xlabel('maxtime') # 绘制所有解的点图(所有代的所有个体)
plt.ylabel('standard deviation') if all_fitnesses:
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('pareto.png') plt.savefig('all_solutions_pareto.png')
plt.show() plt.show()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB