├── .gitignore ├── Decode.py ├── Encode.py ├── GA.py ├── Instance.py ├── Job.py ├── LICENSE ├── Machine.py ├── README.md ├── README_CN.md ├── assets ├── img1.png ├── img10.png ├── img11.png ├── img12.png ├── img13.png ├── img14.png ├── img2.png ├── img3.png ├── img4.png ├── img5.png ├── img6.png ├── img7.png ├── img8.png └── img9.png └── main.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .git/ 3 | __pycache__/ -------------------------------------------------------------------------------- /Decode.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from Job import Job 4 | from Machine import Machine_Time_window 5 | 6 | 7 | class Decode: 8 | def __init__(self, J, Processing_time, M_num): 9 | """ 10 | :param J: 各工件对应的工序数字典 11 | :param Processing_time: 各工件的加工时间矩阵 12 | :param M_num: 加工机器数 13 | """ 14 | self.Processing_time = Processing_time 15 | self.M_num = M_num 16 | self.J = J 17 | self.Machines = [] # 存储机器类 18 | self.Scheduled = [] # 已经排产过的工序 19 | self.fitness = 0 # 适应度 20 | self.Machine_State = np.zeros(M_num, dtype=int) # 在机器上加工的工件是哪个 21 | self.Jobs = [] # 存储工件类 22 | for j in range(M_num): 23 | self.Machines.append(Machine_Time_window(j)) 24 | for k, v in J.items(): 25 | self.Jobs.append(Job(k, v)) 26 | 27 | # 时间顺序矩阵和机器顺序矩阵,根据基因的MS部分转换 28 | def Order_Matrix(self, MS): 29 | JM = [] 30 | T = [] 31 | Ms_decompose = [] 32 | Site = 0 33 | # 按照基因的MS部分按工件序号划分 34 | for S_i in self.J.values(): 35 | Ms_decompose.append(MS[Site:Site + S_i]) 36 | Site += S_i 37 | for i in range(len(Ms_decompose)): # len(Ms_decompose)表示工件数 38 | JM_i = [] 39 | T_i = [] 40 | for j in range(len(Ms_decompose[i])): # len(Ms_decompose[i])表示每一个工件对应的工序数 41 | O_j = self.Processing_time[i][j] # 工件i的工序j可选择的加工时间列表 42 | M_ij = [] 43 | T_ij = [] 44 | for Mac_num in range(len(O_j)): # 寻找MS对应部分的机器时间和机器顺序 45 | if O_j[Mac_num] != 9999: 46 | M_ij.append(Mac_num) 47 | T_ij.append(O_j[Mac_num]) 48 | else: 49 | continue 50 | JM_i.append(M_ij[Ms_decompose[i][j]]) 51 | T_i.append(T_ij[Ms_decompose[i][j]]) 52 | JM.append(JM_i) 53 | T.append(T_i) 54 | return JM, T 55 | 56 | # 确定工序的最早加工时间 57 | def Earliest_Start(self, Job, O_num, Machine): 58 | P_t = self.Processing_time[Job][O_num][Machine] 59 | last_O_end = self.Jobs[Job].Last_Processing_end_time # 上道工序结束时间 60 | Selected_Machine = Machine 61 | M_window = self.Machines[Selected_Machine].Empty_time_window() # 当前机器的空格时间 62 | M_Tstart = M_window[0] 63 | M_Tend = M_window[1] 64 | M_Tlen = M_window[2] 65 | Machine_end_time = self.Machines[Selected_Machine].End_time 66 | ealiest_start = max(last_O_end, Machine_end_time) 67 | if M_Tlen is not None: # 此处为全插入时窗 68 | for le_i in range(len(M_Tlen)): 69 | # 当前空格时间比加工时间大可插入 70 | if M_Tlen[le_i] >= P_t: 71 | # 当前空格开始时间比该工件上一工序结束时间大可插入该空格,以空格开始时间为这一工序开始 72 | if M_Tstart[le_i] >= last_O_end: 73 | ealiest_start = M_Tstart[le_i] 74 | break 75 | # 当前空格开始时间比该工件上一工序结束时间小但空格可满足插入该工序,以该工序的上一工序的结束为开始 76 | if M_Tstart[le_i] < last_O_end and M_Tend[le_i] - last_O_end >= P_t: 77 | ealiest_start = last_O_end 78 | break 79 | M_Ealiest = ealiest_start # 当前工件当前工序的最早开始时间 80 | End_work_time = M_Ealiest + P_t # 当前工件当前工序的结束时间 81 | return M_Ealiest, Selected_Machine, P_t, O_num, last_O_end, End_work_time 82 | 83 | # 解码操作 84 | def decode(self, CHS, Len_Chromo): 85 | """ 86 | :param CHS: 种群基因 87 | :param Len_Chromo: MS与OS的分解线 88 | :return: 适应度,即最大加工时间 89 | """ 90 | MS = list(CHS[0:Len_Chromo]) 91 | OS = list(CHS[Len_Chromo:2 * Len_Chromo]) 92 | Needed_Matrix = self.Order_Matrix(MS) 93 | JM = Needed_Matrix[0] 94 | for i in OS: 95 | Job = i 96 | O_num = self.Jobs[Job].Current_Processed() # 现在加工的工序 97 | Machine = JM[Job][O_num] # 用基因的OS部分的工件序号以及工序序号索引机器顺序矩阵的机器序号 98 | Para = self.Earliest_Start(Job, O_num, Machine) 99 | self.Jobs[Job]._Input(Para[0], Para[5], Para[1]) # 工件完成该工序 100 | if Para[5] > self.fitness: 101 | self.fitness = Para[5] 102 | self.Machines[Machine]._Input(Job, Para[0], Para[2], Para[3]) # 机器完成该工件该工序 103 | return self.fitness 104 | -------------------------------------------------------------------------------- /Encode.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | import numpy as np 4 | 5 | 6 | class Encode: 7 | def __init__(self, Matrix, Pop_size, J, J_num, M_num): 8 | """ 9 | :param Matrix: 机器加工时间矩阵 10 | :param Pop_size: 种群数量 11 | :param J: 各工件对应的工序数 12 | :param J_num: 工件数 13 | :param M_num: 机器数 14 | """ 15 | self.Matrix = Matrix 16 | self.J = J 17 | self.J_num = J_num 18 | self.M_num = M_num 19 | self.CHS = [] 20 | self.GS_num = int(0.6 * Pop_size) # 全局选择初始化 21 | self.LS_num = int(0.2 * Pop_size) # 局部选择初始化 22 | self.RS_num = int(0.2 * Pop_size) # 随机选择初始化 23 | self.Len_Chromo = 0 24 | for i in J.values(): 25 | self.Len_Chromo += i 26 | 27 | # 生成工序准备的部分 28 | def OS_List(self): 29 | OS_list = [] 30 | for k, v in self.J.items(): 31 | OS_add = [k - 1 for j in range(v)] 32 | OS_list.extend(OS_add) 33 | return OS_list 34 | 35 | # 生成初始化矩阵 36 | def CHS_Matrix(self, C_num): 37 | return np.zeros([C_num, self.Len_Chromo], dtype=int) 38 | 39 | # 定位每个工件的每道工序的位置 40 | def Site(self, Job, Operation): 41 | O_num = 0 42 | for i in range(len(self.J)): 43 | if i == Job: 44 | return O_num + Operation 45 | else: 46 | O_num = O_num + self.J[i + 1] 47 | return O_num 48 | 49 | # 全局初始化 50 | def Global_initial(self): 51 | MS = self.CHS_Matrix(self.GS_num) # 根据GS_num生成种群 52 | OS_list = self.OS_List() 53 | OS = self.CHS_Matrix(self.GS_num) 54 | for i in range(self.GS_num): 55 | Machine_time = np.zeros(self.M_num, dtype=int) # 步骤1 生成一个整型数组,长度为机器数,且初始化每个元素为0 56 | random.shuffle(OS_list) # 生成工序排序部分 57 | OS[i] = np.array(OS_list) # 随机打乱后将其赋值给OS的某一行(因为有一个种群,第i则是赋值在OS的第i行,以此生成完整的OS) 58 | GJ_list = [i_1 for i_1 in range(self.J_num)] # 生成工件集 59 | random.shuffle(GJ_list) # 随机打乱工件集,为的是下一步可以随机抽出第一个工件 60 | for g in GJ_list: # 选择第一个工件(由于上一步已经打乱工件集,抽出第一个也是“随机”) 61 | h = self.Matrix[g] # h为第一个工件包含的工序对应的时间矩阵 62 | for j in range(len(h)): # 从此工件的第一个工序开始 63 | D = h[j] # D为第一个工件的第一个工序对应的时间矩阵 64 | List_Machine_weizhi = [] 65 | for k in range(len(D)): # 确定工序可用的机器位于第几个位置 66 | Useing_Machine = D[k] 67 | if Useing_Machine != 9999: 68 | List_Machine_weizhi.append(k) 69 | Machine_Select = [] 70 | for Machine_add in List_Machine_weizhi: # 将机器时间数组对应位置和工序可选机器的时间相加 71 | Machine_Select.append(Machine_time[Machine_add] + D[Machine_add]) 72 | Min_time = min(Machine_Select) # 选出时间最小的机器 73 | K = Machine_Select.index(Min_time) # 第一次出现最小时间的位置,确定最小负荷为哪个机器,即为该工序可选择的机器里的第K个机器,并非Mk 74 | I = List_Machine_weizhi[K] # 所有机器里的第I个机器,即Mi 75 | Machine_time[I] += Min_time # 相应的机器位置加上最小时间 76 | site = self.Site(g, j) # 定位每个工件的每道工序的位置 77 | MS[i][site] = K # 即将每个工序选择的第K个机器赋值到每个工件的每道工序的位置上去 即生成MS的染色体 78 | CHS1 = np.hstack((MS, OS)) # 将MS和OS整合为一个矩阵 79 | return CHS1 80 | 81 | # 局部初始化 82 | def Local_initial(self): 83 | MS = self.CHS_Matrix(self.LS_num) # 根据LS_num生成局部选择的种群大小 84 | OS_list = self.OS_List() 85 | OS = self.CHS_Matrix(self.LS_num) 86 | for i in range(self.LS_num): 87 | random.shuffle(OS_list) # 生成工序排序部分 88 | OS[i] = np.array(OS_list) # 随机打乱后将其赋值给OS的某一行(因为有一个种群,第i则是赋值在OS的第i行,以此生成完整的OS) 89 | GJ_List = [i_1 for i_1 in range(self.J_num)] # 生成工件集 90 | for g in GJ_List: # 选择第一个工件(注意:不用随机打乱了) 91 | Machine_time = np.zeros(self.M_num, 92 | dtype=int) # 设置一个整型数组 并初始化每一个元素为0,由于局部初始化,每个工件的所有工序结束后都要重新初始化,所以和全局初始化不同,此步骤应放在此处 93 | h = self.Matrix[g] # h为第一个工件包含的工序对应的时间矩阵 94 | for j in range(len(h)): # 从选择的工件的第一个工序开始 95 | D = h[j] # 此工件第一个工序对应的机器加工时间矩阵 96 | List_Machine_weizhi = [] 97 | for k in range(len(D)): # 确定工序可用的机器位于第几个位置 98 | Useing_Machine = D[k] 99 | if Useing_Machine != 9999: 100 | List_Machine_weizhi.append(k) 101 | Machine_Select = [] 102 | for Machine_add in List_Machine_weizhi: # 将机器时间数组对应位置和工序可选机器的时间相加 103 | Machine_Select.append(Machine_time[Machine_add] + D[Machine_add]) 104 | Min_time = min(Machine_Select) # 选出这些时间里最小的 105 | K = Machine_Select.index(Min_time) # 第一次出现最小时间的位置,确定最小负荷为哪个机器,即为该工序可选择的机器里的第K个机器,并非Mk 106 | I = List_Machine_weizhi[K] # 所有机器里的第I个机器,即Mi 107 | Machine_time[I] += Min_time 108 | site = self.Site(g, j) # 定位每个工件的每道工序的位置 109 | MS[i][site] = K # 即将每个工序选择的第K个机器赋值到每个工件的每道工序的位置上去 110 | CHS1 = np.hstack((MS, OS)) # 将MS和OS整合为一个矩阵 111 | return CHS1 112 | 113 | # 随机初始化 114 | def Random_initial(self): 115 | MS = self.CHS_Matrix(self.RS_num) # 根据RS_num生成随机选择的种群大小 116 | OS_list = self.OS_List() 117 | OS = self.CHS_Matrix(self.RS_num) 118 | for i in range(self.RS_num): 119 | random.shuffle(OS_list) 120 | OS[i] = np.array(OS_list) 121 | GJ_List = [i_1 for i_1 in range(self.J_num)] # 生成工件集 122 | for g in GJ_List: # 选择第一个工件 123 | h = self.Matrix[g] 124 | for j in range(len(h)): # 选择第一个工件的第一个工序 125 | D = h[j] # 此工件第一个工序可加工的机器对应的时间矩阵 126 | List_Machine_weizhi = [] 127 | for k in range(len(D)): 128 | Useing_Machine = D[k] 129 | if Useing_Machine != 9999: 130 | List_Machine_weizhi.append(k) 131 | number = random.choice(List_Machine_weizhi) # 从可选择的机器编号中随机选择一个(此编号就是机器编号) 132 | K = List_Machine_weizhi.index(number) # 即为该工序可选择的机器里的第K个机器,并非Mk 133 | site = self.Site(g, j) # 定位每个工件的每道工序的位置 134 | MS[i][site] = K # 即将每个工序选择的第K个机器赋值到每个工件的每道工序的位置上去 135 | CHS1 = np.hstack((MS, OS)) 136 | return CHS1 137 | -------------------------------------------------------------------------------- /GA.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import random 3 | 4 | import numpy as np 5 | 6 | from Decode import Decode 7 | from Instance import * 8 | 9 | 10 | class GA(): 11 | def __init__(self): 12 | self.Pop_size = 400 # 种群数量 13 | self.Pc = 0.8 # 交叉概率 14 | self.Pm = 0.3 # 变异概率 15 | self.Pv = 0.5 # 选择何种方式进行交叉的概率阈值 16 | self.Pw = 0.95 # 选择何种方式进行变异的概率阈值 17 | self.Max_Itertions = 100 # 最大迭代次数 18 | 19 | # 适应度 20 | def fitness(self, CHS, J, Processing_time, M_num, Len): 21 | Fit = [] 22 | for i in range(len(CHS)): 23 | d = Decode(J, Processing_time, M_num) 24 | Fit.append(d.decode(CHS[i], Len)) 25 | return Fit 26 | 27 | # 机器部分交叉 28 | def machine_cross(self, CHS1, CHS2, T0): 29 | """ 30 | :param CHS1: 机器选择部分的基因1 31 | :param CHS2: 机器选择部分的基因2 32 | :param T0: 工序总数 33 | :return: 交叉后的机器选择部分的基因 34 | """ 35 | T_r = [j for j in range(T0)] 36 | r = random.randint(1, 10) # 在区间[1,T0]内产生一个整数r 37 | random.shuffle(T_r) 38 | R = T_r[0:r] # 按照随机数r产生r个互不相等的整数 39 | OS_1 = CHS1[O_num:2 * T0] 40 | OS_2 = CHS2[O_num:2 * T0] 41 | MS_1 = CHS2[0:T0] 42 | MS_2 = CHS1[0:T0] 43 | for i in R: 44 | K, K_2 = MS_1[i], MS_2[i] 45 | MS_1[i], MS_2[i] = K_2, K 46 | CHS1 = np.hstack((MS_1, OS_1)) 47 | CHS2 = np.hstack((MS_2, OS_2)) 48 | return CHS1, CHS2 49 | 50 | # 工序部分交叉 51 | def operation_cross(self, CHS1, CHS2, T0, J_num): 52 | """ 53 | :param CHS1: 工序选择部分的基因1 54 | :param CHS2: 工序选择部分的基因2 55 | :param T0: 工序总数 56 | :param J_num: 工件总数 57 | :return: 交叉后的工序选择部分的基因 58 | """ 59 | OS_1 = CHS1[T0:2 * T0] 60 | OS_2 = CHS2[T0:2 * T0] 61 | MS_1 = CHS1[0:T0] 62 | MS_2 = CHS2[0:T0] 63 | Job_list = [i for i in range(J_num)] 64 | random.shuffle(Job_list) 65 | r = random.randint(1, J_num - 1) 66 | Set1 = Job_list[0:r] 67 | new_os = list(np.zeros(T0, dtype=int)) 68 | for k, v in enumerate(OS_1): 69 | if v in Set1: 70 | new_os[k] = v + 1 71 | for i in OS_2: 72 | if i not in Set1: 73 | Site = new_os.index(0) 74 | new_os[Site] = i + 1 75 | new_os = np.array([j - 1 for j in new_os]) 76 | CHS1 = np.hstack((MS_1, new_os)) 77 | CHS2 = np.hstack((MS_2, new_os)) 78 | return CHS1, CHS2 79 | 80 | # 机器部分变异 81 | def machine_variation(self, CHS, O, T0, J): 82 | """ 83 | :param CHS: 机器选择部分的基因 84 | :param O: 加工时间矩阵 85 | :param T0: 工序总数 86 | :param J: 各工件加工信息 87 | :return: 变异后的机器选择部分的基因 88 | """ 89 | Tr = [i_num for i_num in range(T0)] 90 | MS = CHS[0:T0] 91 | OS = CHS[T0:2 * T0] 92 | # 机器选择部分 93 | r = random.randint(1, T0 - 1) # 在变异染色体中选择r个位置 94 | random.shuffle(Tr) 95 | T_r = Tr[0:r] 96 | for num in T_r: 97 | T_0 = [j for j in range(T0)] 98 | K = [] 99 | Site = 0 100 | for k, v in J.items(): 101 | K.append(T_0[Site:Site + v]) 102 | Site += v 103 | for i in range(len(K)): 104 | if num in K[i]: 105 | O_i = i 106 | O_j = K[i].index(num) 107 | break 108 | Machine_using = O[O_i][O_j] 109 | Machine_time = [] 110 | for j in Machine_using: 111 | if j != 9999: 112 | Machine_time.append(j) 113 | Min_index = Machine_time.index(min(Machine_time)) 114 | MS[num] = Min_index 115 | CHS = np.hstack((MS, OS)) 116 | return CHS 117 | 118 | # 工序部分变异 119 | def operation_variation(self, CHS, T0, J_num, J, O, M_num): 120 | """ 121 | :param CHS: 工序选择部分的基因 122 | :param T0: 工序总数 123 | :param J_num: 工件总数 124 | :param J: 各工件加工信息 125 | :param O: 加工时间矩阵 126 | :param M_num: 机器总数 127 | :return: 变异后的工序选择部分的基因 128 | """ 129 | MS = CHS[0:T0] 130 | OS = list(CHS[T0:2 * T0]) 131 | r = random.randint(1, J_num - 1) 132 | Tr = [i for i in range(J_num)] 133 | random.shuffle(Tr) 134 | Tr = Tr[0:r] 135 | J_os = dict(enumerate(OS)) # 随机选择r个不同的基因 136 | J_os = sorted(J_os.items(), key=lambda d: d[1]) 137 | Site = [] 138 | for i in range(r): 139 | Site.append(OS.index(Tr[i])) 140 | A = list(itertools.permutations(Tr, r)) 141 | A_CHS = [] 142 | for i in range(len(A)): 143 | for j in range(len(A[i])): 144 | OS[Site[j]] = A[i][j] 145 | C_I = np.hstack((MS, OS)) 146 | A_CHS.append(C_I) 147 | Fit = [] 148 | for i in range(len(A_CHS)): 149 | d = Decode(J, O, M_num) 150 | Fit.append(d.decode(CHS, T0)) 151 | return A_CHS[Fit.index(min(Fit))] 152 | -------------------------------------------------------------------------------- /Instance.py: -------------------------------------------------------------------------------- 1 | """ 2 | Processing_time:工件各工序对应各机器加工时间矩阵 3 | J:各工件对应的工序数字典 4 | M_num:加工机器数 5 | O_num:加工工序数 6 | J_num:工件个数 7 | """ 8 | 9 | CKS201 = [[10, 9, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999], 10 | [9999, 9999, 14, 16, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999], 11 | [9999, 9999, 9999, 9999, 15, 25, 21, 9999, 9999, 9999, 9999, 9999], 12 | [9999, 9999, 9999, 9999, 9999, 9999, 9999, 9, 13, 15, 24, 9999], 13 | [9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 10]] 14 | CKS301 = [[12, 9, 10, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999], 15 | [9999, 9999, 9999, 16, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999], 16 | [9999, 9999, 9999, 9999, 15, 9999, 9999, 9999, 9999, 9999, 9999, 9999], 17 | [9999, 9999, 9999, 9999, 9999, 27, 22, 9999, 9999, 9999, 9999, 9999], 18 | [9999, 9999, 9999, 9999, 9999, 9999, 9999, 21, 17, 9999, 9999, 9999], 19 | [9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 19, 9999, 9999], 20 | [9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 17, 9999], 21 | [9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 18]] 22 | Processing_time = [CKS201] * 3 + [CKS301] * 4 23 | J = {i: (5 if i < 4 else 8) for i in range(1, 8)} 24 | M_num = 12 25 | O_num = 3 * 5 + 4 * 8 26 | J_num = 7 27 | -------------------------------------------------------------------------------- /Job.py: -------------------------------------------------------------------------------- 1 | class Job: 2 | def __init__(self, Job_index, Operation_num): 3 | """ 4 | :param Job_index: 工件序号 5 | :param Operation_num: 工序数 6 | """ 7 | self.Job_index = Job_index 8 | self.Operation_num = Operation_num 9 | self.Processed = [] # 记录工件工序的加工进度 10 | self.J_start = [] # 记录工件工序的开始时间 11 | self.J_end = [] # 记录工件工序的结束时间 12 | self.J_machine = [] # 记录工件工序选择的机器 13 | self.Last_Processing_Machine = None # 工件当前工序的加工机器 14 | self.Last_Processing_end_time = 0 # 工件当前工序的结束时间 15 | 16 | # 工件已经加工的工序数 17 | def Current_Processed(self): 18 | return len(self.Processed) 19 | 20 | # 工件某工序开始加工 21 | def _Input(self, W_Eailiest, End_time, Machine): 22 | """ 23 | :param W_Eailiest: 工件当前工序的开始时间 24 | :param End_time: 工件当前工序的结束时间 25 | :param Machine: 工件当前工序选择的加工机器 26 | :return: 27 | """ 28 | self.Last_Processing_Machine = Machine 29 | self.Last_Processing_end_time = End_time 30 | self.Processed.append(1) 31 | self.J_start.append(W_Eailiest) 32 | self.J_end.append(End_time) 33 | self.J_machine.append(Machine) 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Incalos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Machine.py: -------------------------------------------------------------------------------- 1 | class Machine_Time_window: 2 | def __init__(self, Machine_index): 3 | """ 4 | :param Machine_index: 加工机器序号 5 | """ 6 | self.Machine_index = Machine_index 7 | self.assigned_task = [] # 机器分配的任务记录,包括工件序号以及工序序号 8 | self.O_start = [] # 各任务工序的开始时间记录 9 | self.O_end = [] # 各任务工序的结束时间记录 10 | self.End_time = 0 11 | 12 | # 机器的哪些时间窗是空的,此处只考虑内部封闭的时间窗,类似甘特图每一行往后叠加 13 | def Empty_time_window(self): 14 | """ 15 | :return: 空格时间的开始、结束、时长 16 | """ 17 | time_window_start = [] 18 | time_window_end = [] 19 | len_time_window = [] 20 | if self.O_end is None: 21 | pass 22 | elif len(self.O_end) == 1: 23 | if self.O_start[0] != 0: 24 | time_window_start = [0] 25 | time_window_end = [self.O_start[0]] 26 | elif len(self.O_end) > 1: 27 | if self.O_start[0] != 0: 28 | time_window_start.append(0) 29 | time_window_end.append(self.O_start[0]) 30 | time_window_start.extend(self.O_end[:-1]) # 因为使用时间窗的结束点就是空时间窗的开始点 31 | time_window_end.extend(self.O_start[1:]) 32 | if time_window_end is not None: 33 | len_time_window = [time_window_end[i] - time_window_start[i] for i in range(len(time_window_end))] 34 | return time_window_start, time_window_end, len_time_window 35 | 36 | # 机器投入新一轮加工 37 | def _Input(self, Job, M_Ealiest, P_t, O_num): 38 | if self.O_end != []: 39 | # 如果当前机器加工的最早开始时间比记录的大,则依次往后排任务,否则将任务插入中间的分配任务记录 40 | if self.O_start[-1] > M_Ealiest: 41 | for i in range(len(self.O_end)): 42 | if self.O_start[i] >= M_Ealiest: 43 | self.assigned_task.insert(i, [Job + 1, O_num + 1]) 44 | break 45 | else: 46 | self.assigned_task.append([Job + 1, O_num + 1]) 47 | else: 48 | self.assigned_task.append([Job + 1, O_num + 1]) 49 | self.O_start.append(M_Ealiest) 50 | self.O_start.sort() 51 | self.O_end.append(M_Ealiest + P_t) 52 | self.O_end.sort() 53 | self.End_time = self.O_end[-1] 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flexible Job-shop Scheduling Problem With Genetic Algorithm 2 | 3 | This project involves using Genetic Algorithm to solve the Flexible Job-shop Scheduling Problem. 4 | 5 | ## 1. Create Instance.py based on actual problems 6 | 7 | For example, the following is the processing schedule for each certain workpiece. 8 | 9 | 10 | | | Machine1 | Machine2 | Machine3 | Machine4 | Machine5 | Machine6 | Machine7 | Machine8 | Machine9 | Machine10 | Machine11 | 11 | | :--------: | :------: | :------: | :------: | :------: | :------: | :------: | :------: | :------: | :------: | :-------: | :-------: | 12 | | Operation1 | 10 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 13 | | Operation2 | 0 | 9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 14 | | Operation3 | 0 | 0 | 14 | 16 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 15 | | Operation4 | 0 | 0 | 0 | 0 | 15 | 25 | 21 | 0 | 0 | 0 | 0 | 16 | | Operation5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 9 | 13 | 25 | 14 | 17 | 18 | If the number of workpieces is **5**, the **Instance.py** will be written in the following format. 19 | 20 | ```python 21 | Processing_time = [[10, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999], 22 | [9999, 9, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999], 23 | [9999, 9999, 14, 16, 9999, 9999, 9999, 9999, 9999, 9999, 9999], 24 | [9999, 9999, 9999, 9999, 15, 25, 21, 9999, 9999, 9999, 9999], 25 | [9999, 9999, 9999, 9999, 9999, 9999, 9999, 9, 13, 25, 24]] 26 | J = {1: 5, 2: 5, 3: 5, 4: 5, 5: 5} 27 | J_num = 5 28 | M_num = 11 29 | O_num = 25 30 | ``` 31 | 32 | The following is an introduction to variable names. 33 | 34 | * **Processing_time** : `the processing schedule of every workpiece written in the list format` 35 | * In the table, the row index represents the sequence number of the operation, the column index represents the sequence number of the machine, and the numerical value represents the corresponding processing time. 36 | * If a machine is not selected in the operation, the corresponding value is represented by **9999**. 37 | * **J** : `the index of each workpiece and the total number of corresponding operations written in the dictionary format` 38 | * **J_num** : `the number of workpieces` 39 | * **M_num** : `the number of machines` 40 | * **O_num** : `the number of operations for all workpieces` 41 | 42 | ## 2. Set the parameters of Genetic Algorithm 43 | 44 | Set the parameters of the genetic algorithm in **GA.py**. 45 | 46 | ```python 47 | class GA(): 48 | def __init__(self): 49 | self.Pop_size = 400 50 | self.Pc = 0.8 51 | self.Pm = 0.3 52 | self.Pv = 0.5 53 | self.Pw = 0.95 54 | self.Max_Itertions = 100 55 | ``` 56 | 57 | The following is an introduction to variable names. 58 | 59 | * **Pop_size** : `the size of population` 60 | * **Pc** : `the probability of performing the crossover operation` 61 | * **Pm** : `the probability of performing the variational operation` 62 | * **Pv** : `the probability of choosing which way to perform the crossover operation` 63 | * **Pw** : `the probability of choosing which way to perform the variational operation` 64 | * **Max_Itertions** : `the maximum number of evolutionary generations` 65 | 66 | ## 3. Run main.py 67 | 68 | After the code runs, the following two results will appear. 69 | 70 | * **Result1** : processing schedule of all the workpieces showed in gantt chart 71 | 72 | ![](assets/img1.png) 73 | 74 | * **Result2** : the maximum completion time of each iteration 75 | 76 | ![](assets/img2.png) 77 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # 基于遗传算法的柔性车间生产调度问题 2 | 3 | 本项目主要涉及使用遗传算法解决柔性车间生产调度问题。 4 | 5 | ## 1. 根据实际问题创建 Instance.py 6 | 7 | 例如,以下是每一工件的生产加工时间表。 8 | 9 | | | 机器1 | 机器2 | 机器3 | 机器4 | 机器5 | 机器6 | 机器7 | 机器8 | 机器9 | 机器10 | 机器11 | 10 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :----: | :----: | 11 | | 工序1 | 10 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 12 | | 工序2 | 0 | 9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 13 | | 工序3 | 0 | 0 | 14 | 16 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 14 | | 工序4 | 0 | 0 | 0 | 0 | 15 | 25 | 21 | 0 | 0 | 0 | 0 | 15 | | 工序5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 9 | 13 | 25 | 14 | 16 | 17 | 假设某一车间共生产 **5** 个上述的工件,对应的 **Instance.py** 应该写成下面的格式。 18 | 19 | ```python 20 | Processing_time = [[10, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999], 21 | [9999, 9, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999], 22 | [9999, 9999, 14, 16, 9999, 9999, 9999, 9999, 9999, 9999, 9999], 23 | [9999, 9999, 9999, 9999, 15, 25, 21, 9999, 9999, 9999, 9999], 24 | [9999, 9999, 9999, 9999, 9999, 9999, 9999, 9, 13, 25, 24]] 25 | J = {1: 5, 2: 5, 3: 5, 4: 5, 5: 5} 26 | J_num = 5 27 | M_num = 11 28 | O_num = 25 29 | ``` 30 | 31 | 各参数的具体含义如下: 32 | 33 | * **Processing_time** : `每一个工件的加工时间列表` 34 | * 在加工时间表中,行索引代表的是各工序的序号,列索引代表的是各加工机器的序号,表中的数值代表的是工件的每一工序对应选择加工机器的加工时间。 35 | * 如果在某一工序中不能使用该机器加工,对应的加工时间应该在列表中用 **9999** 替代。 36 | * **J** : `以字典的格式表示的加工信息,其中字典的键表示工件的序号,对应键的值表示加工该工件共需多少工序` 37 | * **J_num** : `工件的数量` 38 | * **M_num** : `机器的数量` 39 | * **O_num** : `所有工件的工序总数量` 40 | 41 | ## 2. 设置遗传算法的参数 42 | 43 | 在 **GA.py** 中设置遗传算法的参数。 44 | 45 | ```python 46 | class GA(): 47 | def __init__(self): 48 | self.Pop_size = 400 49 | self.Pc = 0.8 50 | self.Pm = 0.3 51 | self.Pv = 0.5 52 | self.Pw = 0.95 53 | self.Max_Itertions = 100 54 | ``` 55 | 56 | 各参数的具体含义如下: 57 | 58 | * **Pop_size** : `种群数量` 59 | * **Pc** : `进行交叉运算的概率` 60 | * **Pm** : `进行变异运算的概率` 61 | * **Pv** : `以何种方式进行交叉运算的概率` 62 | * **Pw** : `以何种方式进行变异运算的概率` 63 | * **Max_Itertions** : `种群最大的进化代数` 64 | 65 | ## 3. 运行 main.py 66 | 67 | 在运行 **main.py** 后,会产生以下两个结果。 68 | 69 | * **结果1** : 用甘特图绘制的所用工件各工序的加工时间表 70 | 71 | ![](assets/img1.png) 72 | 73 | * **结果2** : 每次种群进化后生产所有工件的最大完成时间 74 | 75 | ![](assets/img2.png) 76 | 77 | ## 4. 编程思路 78 | 79 | ### (1)问题描述 80 | 81 | * 柔性车间调度问题描述如下: 82 | 83 | * $n$ 个工件 $(J_1,J_2,J_3,...,J_n)$ 要在 $m$ 台机器 $(M_1,M_2,...,M_m)$ 上加工;每个工件包含一道或多道工序;工序顺序是预先确定的;每道工序可以在多台不同加工机器上进行加工;工序的加工时间随加工机器的不同而不同;调度目标是为每到工序选择最合适的机器,确定每台机器上各道工序最佳加工顺序及开工时间,使整个系统的某些性能指标达到最优。 84 | 85 | * 假设按照表2-2的加工时间表设计。 86 | 87 | ![](assets/img4.png) 88 | 89 | * 遗传算法的计算流程:假设P表示种群规模,t表示当前代,P(t)和C(t)表示第t代的父代和子代,那么基本的遗传算法执行步骤如下。 90 | 91 | * 按照一定的初始化方法产生初始种群P(t),t=0。 92 | * 评价种群P(t),计算每个个体的适应度值。 93 | * 判断是否满足终止条件,如果满足则输出结果;否则转下一步骤。 94 | * 按照选择、交叉、变异等遗传算子产生子代C(t)。 95 | * P(t) = C(t),转步骤2,t=t+1。 96 | 97 | ![](assets/img3.png) 98 | 99 | ### (2)编码 100 | 101 | * 染色体编码是将所研究的问题的解用染色体的形式来表达的,这是遗传算法的关键。编码的目的是为了实现交叉、变异等类似于生物界的遗传操作。必须考虑染色体的合法性、可行性、有效性,以及对问题解空间表达的完全性。良好的编码方式可以在后续遗传操作中易产生可行解,提高执行效率;否则,经过遗传操作会产生不可行解,需要一定的修补措施,这样就降低了执行效率。染色体编码对求解速度、计算精度等有着直接的关系,对算法具有重要影响。 102 | 103 | * FJSP包括两个子问题:机器选择和工序排序。机器选择解决每道工序在可选机器集中的哪台机器上进行加工的问题;工序排序解决所有工序确定加工机器后的排序和开工时间问题。针对编码过程中对两个子问题编码处理的不同,目前主要有以下两种不同的编码方法 104 | 105 | * 针对编码过程中两个问题编码处理的不同,目前主要有以下两种不同的编码方法。 106 | 107 | * **集成编码**:集成编码染色体中的每一个基因位 $(h,j,i)$ 代表一个工序任务,表示工件 $j$ 的第 $h$ 道工序在机器 $i$ 上加工,染色体总长度等于所有工件的工序总和 $T_o$ 。 108 | * **分段编码**:分段编码染色体有A/B两部分组成,将工序信息分开处理,分别表示FJSP的两个子问题,两部分染色体长度都等于 $T_o$ 。 109 | 110 | * 此处设计了一种整数编码MSOS,由两部分组成:机器选择部分(machines selection,MS)和工序排序部分(operations sequencing,OS)。 111 | 112 | * **机器选择部分**:机器选择部分染色体长度为 $T_o$ 。每个基因位用整数表示,依次按照工件和工件工序的顺序进行排列,每个整数表示当前工序选择的加工机器在可选机器集中的顺序编号,并不是对应的机器号,这保证了后续交叉、变异等操作后产生的解仍然是可行解。如下图所示,依次是工件 $J_1$ 和工件 $J_2$ 的所有工序。工序 $O_{11}$ 有5台机器可以选择,对应的4表示可选机器集中第4台机器,即在机器 $M_4$ 上进行加工。同理,工序 $O_{12}$ 有2台机器可以选择,分别为机器 $M_2$ 和机器 $M_4$ ,图中对应的“1”表示可选机器集中的第1台机器,即在机器 $M_2$ 上进行加工。 113 | * **工序选择部分**:此部分采用基于工序的编码方式进行编码,染色体长度等于所有工件的工序之和 $T_o$ 。每一个基因用工件号直接编码,工件号出现的顺序表示该工件工序间的先后加工顺序,即对染色体从左到右进行编译,对于第h次出现的工件号,表示该工件 $j$ 的第 $h$ 道工序,并且工件号的出现次数等于该工件的工序总数 $h_j$ 。如下图所示,假设工序染色体为 [2 2 1 1 2] ,则其中第一个“2”表示工件 $J_2$ 的工序 $O_{21}$ ,第二个“2”表示工件 $J_2$ 的工序 $O_{22}$ ,以此类推,转换成各工件工序的加工顺序为 $O_{21}$ → $O_{22}$ → $O_{11}$ → $O_{12}$ → $O_{23}$ 。 114 | 115 | ![](assets/img5.png) 116 | 117 | ### (3)解码 118 | 119 | * FJSP染色体MSOS由两部分组成,分别对它们进行解码,关键是需要将工序排序部分解码成对应于机器选择部分的活动调度,具体的解码步骤如下。 120 | 121 | ![](assets/img6.png) 122 | ![](assets/img7.png) 123 | 124 | ### (4)初始化 125 | 126 | * 种群初始化在进化算法中是一个关键问题,初始解的质量对遗传算法求解的速度和质量有非常大的影响。FJSP不但要解决机器选择问题,还要解决所有工序排序问题。目前,大部分文献一般采用的是随机初始化方法,使得初始解的质量偏低,机器之间负荷不均衡,导致要增加迭代次数或种群大小来达到最优解或近似最优解,这势必增加优化时间。 127 | 128 | * 针对FJSP特点,此处提出一种GLR机器选择方法,包括:全局选择(Global Selection)、局部选择(Local Selection)和随机选择(Random Selection)。GS和LS主要是为了考虑机器选择的负荷问题,使各台被选择的机器的工作负荷尽量平衡,充分提高机器的利用率。RS主要考虑尽量使初始种群分散地分布于整个解空间。通过三者的有机结合,提高初始解在机器选择部分中解的质量。下面分别介绍三种选择方法的具体执行步骤 129 | 130 | * **全局选择**:设置一个数组,长度和机器数相等,数组的顺序依次对应加工机器的顺序,每一位上的值对应相应机器上的加工时间。随机在工件集中选择一个工件,从当前工件的第1道工序开始,将当前工序的可选加工机器的加工时间加上数组中对应的时间。从中选择最短的时间作为当前工序的加工机器,并且将数组更新,即把被选择的加工机器的加工时间加到数组中相应的位置上,以此类推,直到当前工件的所有工序的加工机器选择完毕后,然后再随机选择一个工件开始,直到所有工件的工序选择完毕为止。这样保证了最短加工机器先被选到而且保证了加工机器上的工作负荷平衡。具体执行步骤如下。 131 | 132 | ![](assets/img8.png) 133 | 134 | * **局部选择**:局部选择同全局选择原理上基本一致,但是每次对一个工件选择完毕时,数组需要重新设置为零,不存在随机选择工件。设置一个数组,长度和机器数相等,选择工件集中第1个工件,选择当前工件的第1道工序开始,将当前工序的可选加工机器的加工时间加上数组中对应的时间。从中选择最短的时间作为当前工序的加工机器,并且将数组更新,即把被选择的加工机器的加工时间加到数组中相应的位置上,以此类推,直到当前工件的所有工序的加工机器选择完毕后,然后数组每一位重新设置为零,选择下一个工件,直到所有工件选择完毕为止。这样保证了一个工件的工序中优先加工时间最短或说选择机器负荷最小的加工机器进行加工。具体执行步骤如下。 135 | 136 | ![](assets/img9.png) 137 | 138 | * **随机选择**:为保证初始种群的多样性,初始种群应分散于解空间。一部分种群的机器选择部分采用随机初始化方法。RS与GS、LS的主要区别在于每一个基因位上的数字即表示工序可选机器集中的顺序号是随机产生的。具体执行步骤如下。 139 | 140 | ![](assets/img10.png) 141 | 142 | ### (5)交叉操作 143 | 144 | * 交叉的目的是利用父代个体经过一定操作组合后产生新个体,在尽量降低有效模式被破坏的概率基础上对解空间进行高效搜索。交叉操作是主要的遗传操作,GA的性能在很大程度上依赖于所使用的交叉操作,它决定了GA的全局搜索能力。在设计交叉操作时必须满足可行性、特征的有效继承性、完全性和信息非冗余性等指标。特征的有效继承性保证父代中的优良信息能够保留到子代;信息非冗余性保证子代中不会产生过多无用信息,这两个特征在交叉操作设计中是两个重要的指标。 145 | 146 | * **机器选择部分**:机器选择部分必须保证每位基因的先后顺序保持不变,采用均匀交叉操作。 147 | 148 | ![](assets/img11.png) 149 | 150 | * **工序排序部分**:每个染色体中对多个工件进行操作,能够较好地继承父代个体的优良特征。 151 | 152 | ![](assets/img12.png) 153 | 154 | ![](assets/img13.png) 155 | 156 | ### (6)变异操作 157 | 158 | * 变异操作通过随机改变染色体的某些基因对染色体进行较小扰动来生成新的个体,增加种群多样性,并在一定程度上影响着GA的局部搜索能力。此处结合FJSP特点,设计变异方法如下。 159 | 160 | ![](assets/img14.png) 161 | 162 | ### (7)选择操作 163 | 164 | * 选择操作的作用是使高性能的个体能以更高的概率生存,避免有效基因的损失,同时保持种群大小恒定,从而加快全局收敛性和提高计算效率。 165 | -------------------------------------------------------------------------------- /assets/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img1.png -------------------------------------------------------------------------------- /assets/img10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img10.png -------------------------------------------------------------------------------- /assets/img11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img11.png -------------------------------------------------------------------------------- /assets/img12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img12.png -------------------------------------------------------------------------------- /assets/img13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img13.png -------------------------------------------------------------------------------- /assets/img14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img14.png -------------------------------------------------------------------------------- /assets/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img2.png -------------------------------------------------------------------------------- /assets/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img3.png -------------------------------------------------------------------------------- /assets/img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img4.png -------------------------------------------------------------------------------- /assets/img5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img5.png -------------------------------------------------------------------------------- /assets/img6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img6.png -------------------------------------------------------------------------------- /assets/img7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img7.png -------------------------------------------------------------------------------- /assets/img8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img8.png -------------------------------------------------------------------------------- /assets/img9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Incalos/FJSP-With-Genetic-Algorithm/2600aacf1feb77f1366fabdede3188a208f00656/assets/img9.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | 6 | from Decode import Decode 7 | from Encode import Encode 8 | from GA import GA 9 | from Instance import * 10 | 11 | 12 | # 绘制甘特图 13 | def Gantt(Machines): 14 | M = ['red', 'blue', 'yellow', 'orange', 'green', 'palegoldenrod', 'purple', 'pink', 'Thistle', 'Magenta', 15 | 'SlateBlue', 'RoyalBlue', 'Cyan', 'Aqua', 'floralwhite', 'ghostwhite', 'goldenrod', 'mediumslateblue', 16 | 'navajowhite', 'navy', 'sandybrown', 'moccasin'] 17 | for i in range(len(Machines)): 18 | Machine = Machines[i] 19 | Start_time = Machine.O_start 20 | End_time = Machine.O_end 21 | for i_1 in range(len(End_time)): 22 | plt.barh(i, width=End_time[i_1] - Start_time[i_1], height=0.8, left=Start_time[i_1], 23 | color=M[Machine.assigned_task[i_1][0] - 1], edgecolor='black') 24 | plt.text(x=Start_time[i_1] + (End_time[i_1] - Start_time[i_1]) / 2 - 0.5, y=i, 25 | s=Machine.assigned_task[i_1][0]) 26 | plt.yticks(np.arange(len(Machines) + 1), np.arange(1, len(Machines) + 2)) 27 | plt.title('Scheduling Gantt chart') 28 | plt.ylabel('Machines') 29 | plt.xlabel('Time(min)') 30 | plt.savefig('优化后排程方案的甘特图.png') 31 | plt.show() 32 | 33 | 34 | if __name__ == '__main__': 35 | Optimal_fit = 9999 # 最佳适应度(初始化) 36 | Optimal_CHS = 0 # 最佳适应度对应的基因个体(初始化) 37 | g = GA() 38 | e = Encode(Processing_time, g.Pop_size, J, J_num, M_num) 39 | CHS1 = e.Global_initial() 40 | CHS2 = e.Random_initial() 41 | CHS3 = e.Local_initial() 42 | C = np.vstack((CHS1, CHS2, CHS3)) 43 | Best_fit = [] # 记录适应度在迭代过程中的变化,便于绘图 44 | for i in range(g.Max_Itertions): 45 | print("iter_{} start!".format(i)) 46 | Fit = g.fitness(C, J, Processing_time, M_num, O_num) 47 | Best = C[Fit.index(min(Fit))] 48 | best_fitness = min(Fit) 49 | if best_fitness < Optimal_fit: 50 | Optimal_fit = best_fitness 51 | Optimal_CHS = Best 52 | Best_fit.append(Optimal_fit) 53 | print('best_fitness', best_fitness) 54 | d = Decode(J, Processing_time, M_num) 55 | Fit.append(d.decode(Optimal_CHS, O_num)) 56 | Gantt(d.Machines) 57 | else: 58 | Best_fit.append(Optimal_fit) 59 | for j in range(len(C)): 60 | Cafter = [] 61 | if random.random() < g.Pc: 62 | N_i = random.choice(np.arange(len(C))) 63 | if random.random() < g.Pv: 64 | Cross = g.machine_cross(C[j], C[N_i], O_num) 65 | else: 66 | Cross = g.operation_cross(C[j], C[N_i], O_num, J_num) 67 | Cafter.append(Cross[0]) 68 | Cafter.append(Cross[1]) 69 | Cafter.append(C[j]) 70 | if random.random() < g.Pm: 71 | if random.random() < g.Pw: 72 | Variance = g.machine_variation(C[j], Processing_time, O_num, J) 73 | else: 74 | Variance = g.operation_variation(C[j], O_num, J_num, J, Processing_time, M_num) 75 | Cafter.append(Variance) 76 | if Cafter != []: 77 | Fit = g.fitness(Cafter, J, Processing_time, M_num, O_num) 78 | C[j] = Cafter[Fit.index(min(Fit))] 79 | x = np.linspace(0, 50, g.Max_Itertions) 80 | plt.plot(x, Best_fit, '-k') 81 | plt.title('the maximum completion time of each iteration') 82 | plt.ylabel('Cmax') 83 | plt.xlabel('Test Num') 84 | plt.savefig('最大完成时间的优化过程.png') 85 | plt.show() 86 | --------------------------------------------------------------------------------