Compare commits

..

No commits in common. "master" and "NSGA-2" have entirely different histories.

6 changed files with 204 additions and 255 deletions

View File

@ -2,7 +2,6 @@ import numpy as np
from Job import Job
from Machine import Machine_Time_window
class Decode:
def __init__(self, J, Processing_time, M_num):
"""
@ -80,11 +79,11 @@ class Decode:
End_work_time = M_Ealiest + P_t # 当前工件当前工序的结束时间
return M_Ealiest, Selected_Machine, P_t, O_num, last_O_end, End_work_time
# 解码操作适配新编码前半部分为OS后半部分为MS
# 解码操作
def decode(self, CHS, Len_Chromo):
"""
:param CHS: 种群基因
:param Len_Chromo: 工序总数OS和MS各占一半长度
:param Len_Chromo: MS与OS的分解线
:return: 双目标值 [最大加工时间, 负载标准差]
"""
# 重置状态
@ -103,11 +102,10 @@ class Decode:
job.Last_Processing_Machine = None
job.Last_Processing_end_time = 0
OS = list(CHS[0:Len_Chromo]) # 前半部分为工件排列
MS = list(CHS[Len_Chromo:2 * Len_Chromo]) # 后半部分为机器索引
MS = list(CHS[0:Len_Chromo])
OS = list(CHS[Len_Chromo:2 * Len_Chromo])
Needed_Matrix = self.Order_Matrix(MS)
JM, TM = Needed_Matrix[0], Needed_Matrix[1]
for i in OS:
Job = i
O_num = self.Jobs[Job].Current_Processed() # 现在加工的工序

142
Encode.py
View File

@ -1,7 +1,6 @@
import random
import numpy as np
class Encode:
def __init__(self, Matrix, Pop_size, J, J_num, M_num):
"""
@ -16,25 +15,24 @@ class Encode:
self.J_num = J_num
self.M_num = M_num
self.CHS = []
# 调整初始化比例增加随机选择比例到50%
self.GS_num = int(0.3 * Pop_size) # 全局选择初始化
self.GS_num = int(0.6 * 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
for i in J.values():
self.Len_Chromo += i # 遍历字典J的所有值并求和即工序数之和
# 生成工序准备的部分(工件排列,天然满足工序顺序约束)
# 生成工序准备的部分
def OS_List(self):
OS_list = []
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)
return OS_list
# 生成初始化矩阵
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第几道工序
def Site(self, Job, Operation):
@ -48,74 +46,88 @@ class Encode:
# 全局初始化
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 = self.CHS_Matrix(self.GS_num)
for i in range(self.GS_num):
random.shuffle(OS_list)
OS = OS_list.copy()
MS = [0] * self.Len_Chromo
Machine_time = np.zeros(self.M_num, dtype=int)
GJ_list = [i_1 for i_1 in range(self.J_num)]
random.shuffle(GJ_list)
for g in GJ_list:
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
Machine_time = np.zeros(self.M_num, dtype=int) # 步骤1 生成一个整型数组长度为机器数且初始化每个元素为0
random.shuffle(OS_list) # 生成工序排序部分
OS[i] = np.array(OS_list) # 随机打乱后将其赋值给OS的某一行
GJ_list = [i_1 for i_1 in range(self.J_num)] # 生成工件集
random.shuffle(GJ_list) # 随机打乱工件集
for g in GJ_list: # 选择第一个工件
h = self.Matrix[g] # h为第一个工件包含的工序对应的时间矩阵
for j in range(len(h)): # 从此工件的第一个工序开始
D = h[j] # D为第一个工件的第一个工序对应的时间矩阵
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
# 局部初始化
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 = self.CHS_Matrix(self.LS_num)
for i in range(self.LS_num):
random.shuffle(OS_list)
OS = OS_list.copy()
MS = [0] * self.Len_Chromo
random.shuffle(OS_list) # (随机打乱)生成工序排序部分
OS[i] = np.array(OS_list)
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):
CHS = self.CHS_Matrix(self.RS_num)
MS = self.CHS_Matrix(self.RS_num) # 根据RS_num生成随机选择的种群大小
OS_list = self.OS_List()
OS = self.CHS_Matrix(self.RS_num)
for i in range(self.RS_num):
random.shuffle(OS_list)
OS = OS_list.copy()
MS = [0] * self.Len_Chromo
for g in range(self.J_num):
OS[i] = np.array(OS_list)
GJ_List = [i_1 for i_1 in range(self.J_num)] # 生成工件集
for g in GJ_List: # 选择第一个工件
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]
# 随机选择可用机器
selected = random.choice(List_Machine_weizhi)
K = List_Machine_weizhi.index(selected)
MS[self.Site(g, j)] = K
CHS[i] = np.hstack((OS, MS))
return CHS
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)
number = random.choice(List_Machine_weizhi) # 从可选择的机器编号中随机选择一个
K = List_Machine_weizhi.index(number) # 即为该工序可选择的机器里的第K个机器
site = self.Site(g, j) # 定位每个工件的每道工序的位置
MS[i][site] = K # 即将每个工序选择的第K个机器赋值到对应位置
CHS1 = np.hstack((MS, OS))
return CHS1

215
GA.py
View File

@ -11,10 +11,10 @@ class GA():
def __init__(self):
self.Pop_size = 500 # 种群数量
self.Pc = 0.8 # 交叉概率
self.Pm = 0.15 # 降低变异概率到0.15
self.Pm = 0.3 # 变异概率
self.Pv = 0.5 # 选择何种方式进行交叉的概率阈值
self.Pw = 0.95 # 选择何种方式进行变异的概率阈值
self.Max_Itertions = 100
self.Max_Itertions = 100 # 最大迭代次数
# 适应度
def fitness(self, CHS, J, Processing_time, M_num, Len):
@ -24,141 +24,128 @@ class GA():
Fit.append(d.decode(CHS[i], Len))
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):
"""
:param CHS1: 基因1
:param CHS2: 基因2
:param CHS1: 机器选 择部分的基因1
:param CHS2: 机器选择部分的基因2
:param T0: 工序总数
:return: 交叉后的基因
:return: 交叉后的机器选择部分的基因
"""
OS1, MS1 = CHS1[:T0], CHS1[T0:]
OS2, MS2 = CHS2[:T0], CHS2[T0:]
# 随机选择交叉位置
T_r = list(range(T0))
random.shuffle(T_r)
r = random.randint(1, T0 // 2) # 交叉部分长度
R = T_r[:r]
# 交换机器选择部分
T_r = [j for j in range(T0)]
r = random.randint(1, 10) # 在区间[1,T0]内产生一个整数r
random.shuffle(T_r) #直接打乱T_r原地修改
R = T_r[0:r] # 按照随机数r产生r个互不相等的整数
OS_1 = CHS1[O_num:2 * T0]
OS_2 = CHS2[O_num:2 * T0]
MS_1 = CHS2[0:T0]
MS_2 = CHS1[0:T0]
for i in R:
MS1[i], MS2[i] = MS2[i], MS1[i]
return np.hstack((OS1, MS1)), np.hstack((OS2, MS2))
# 工序部分交叉使用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))
K, K_2 = MS_1[i], MS_2[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
# 机器部分变异(仅在可用机器集中选择)
# 工序部分交叉
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):
"""
:param CHS: 基因
:param CHS: 机器选择部分的基因
:param O: 加工时间矩阵
:param T0: 工序总数
:param J: 各工件加工信息
:return: 变异后的基因
:return: 变异后的机器选择部分的基因
"""
OS = CHS[:T0]
MS = CHS[T0:]
# 确定变异位置数量
r = random.randint(1, max(1, T0 // 10)) # 最多变异10%的位置
positions = random.sample(range(T0), r)
for pos in positions:
# 找到该位置对应的工件和工序
site = 0
job = -1
op = -1
Tr = [i_num for i_num in range(T0)]
MS = CHS[0:T0]
OS = CHS[T0:2 * T0]
# 机器选择部分
r = random.randint(1, T0 - 1) # 在变异染色体中选择r个位置
random.shuffle(Tr)
T_r = Tr[0:r]
for num in T_r:
T_0 = [j for j in range(T0)]
K = []
Site = 0
for k, v in J.items():
if site + v > pos:
job = k - 1 # 转换为0索引
op = pos - site
K.append(T_0[Site:Site + v])
Site += v
for i in range(len(K)):
if num in K[i]:
O_i = i
O_j = K[i].index(num)
break
site += v
# 获取该工序的可用机器
D = O[job][op]
available_machines = [k for k, val in enumerate(D) if val != 9999]
if len(available_machines) <= 1:
continue # 只有一个可用机器时不变异
# 随机选择一个不同的可用机器
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))
Machine_using = O[O_i][O_j]
Machine_time = []
for j in Machine_using:
if j != 9999:
Machine_time.append(j)
Min_index = Machine_time.index(min(Machine_time))
MS[num] = Min_index
CHS = np.hstack((MS, OS))
return CHS
# 工序部分变异
def operation_variation(self, CHS, T0, J_num, J, O, M_num):
"""
:param CHS: 基因
:param CHS: 工序选择部分的基因
:param T0: 工序总数
:param J_num: 工件总数
:param J: 各工件加工信息
:param O: 加工时间矩阵
:param M_num: 机器总数
:return: 变异后的基因
:return: 变异后的工序选择部分的基因
"""
OS = list(CHS[:T0])
MS = CHS[T0:]
# 随机选择两个位置交换
i, j = random.sample(range(T0), 2)
OS[i], OS[j] = OS[j], OS[i]
return np.hstack((OS, MS))
MS = CHS[0:T0]
OS = list(CHS[T0:2 * T0])
r = random.randint(1, J_num - 1)
Tr = [i for i in range(J_num)]
random.shuffle(Tr)
Tr = Tr[0:r]
J_os = dict(enumerate(OS)) # 随机选择r个不同的基因
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))]

View File

@ -1,5 +1,4 @@
import random
import numpy as np
class NSGA2:
def __init__(self, pop_size, obj_num):
@ -9,7 +8,7 @@ class NSGA2:
def fast_non_dominated_sort(self, pop_obj):
"""快速非支配排序"""
pop_size = len(pop_obj)
dominated = [[] for _ in range(pop_size)] # 被支配个体列表
dominated = [[] for _ in range(pop_size)] # 被支配个体列表_通常用作占位符变量表示不关心这个变量的具体值
rank = [0] * pop_size # 个体的非支配等级
n = [0] * pop_size # 支配该个体的个体数量
@ -51,7 +50,7 @@ class NSGA2:
"""计算拥挤度距离"""
pop_size = len(pop_obj)
distance = [0.0] * pop_size
max_rank = max(rank) if rank else 0
max_rank = max(rank)
# 对每个等级的个体计算拥挤度
for r in range(max_rank + 1):
@ -80,7 +79,7 @@ class NSGA2:
return distance
def selection(self, pop, pop_obj):
"""选择操作:基于非支配排序和拥挤度的锦标赛选择惩罚拥挤度为0的个体"""
"""选择操作:基于非支配排序和拥挤度的锦标赛选择"""
pop_size = len(pop)
rank = self.fast_non_dominated_sort(pop_obj)
distance = self.crowding_distance(pop_obj, rank)
@ -96,14 +95,9 @@ class NSGA2:
selected.append(pop[i])
elif rank[i] > rank[j]:
selected.append(pop[j])
# 等级相同则选择拥挤度大的个体惩罚拥挤度为0的重复解
# 等级相同则选择拥挤度大的个体
else:
# 对拥挤度为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]:
if distance[i] > distance[j]:
selected.append(pop[i])
else:
selected.append(pop[j])

68
main.py
View File

@ -7,6 +7,7 @@ from GA import GA
from Instance import *
from NSGA2 import NSGA2
# 绘制甘特图
def Gantt(Machines):
M = ['red', 'blue', 'yellow', 'orange', 'green', 'palegoldenrod', 'purple', 'pink', 'Thistle', 'Magenta',
@ -28,6 +29,7 @@ def Gantt(Machines):
plt.savefig('优化后排程方案的甘特图.png')
plt.show()
if __name__ == '__main__':
# 初始化参数
g = GA()
@ -42,40 +44,18 @@ if __name__ == '__main__':
Optimal_solutions = [] # 存储非支配解
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):
print(f"iter_{i} start!")
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)
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]
# 更新全局非支配解
new_non_dominated = False
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
Optimal_solutions.extend(current_non_dominated)
Optimal_fit_values.extend(current_non_dominated_fit)
# 对全局解重新筛选非支配解
if Optimal_solutions:
@ -91,20 +71,6 @@ if __name__ == '__main__':
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]]
# 早停策略检查
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
selected = nsga2.selection(C, Fit)
@ -162,23 +128,15 @@ if __name__ == '__main__':
print(f"\n选中解的目标值: (最大完工时间: {final_fitness[0]}, 负载标准差: {final_fitness[1]:.2f})")
Gantt(d.Machines)
# 绘制帕累托前沿(非支配解)
# 绘制帕累托前沿
if Optimal_fit_values:
plt.figure(figsize=(10, 6))
cmax_nd = [fit[0] for fit in Optimal_fit_values]
load_std_nd = [fit[1] for fit in Optimal_fit_values]
plt.scatter(cmax_nd, load_std_nd, color='red', label='Non-dominated solutions', zorder=5)
# 绘制所有解的点图(所有代的所有个体)
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.figure()
cmax = [fit[0] for fit in Optimal_fit_values]
load_std = [fit[1] for fit in Optimal_fit_values]
plt.scatter(cmax, load_std, color='red')
plt.title('ParetoFront')
plt.xlabel('maxtime')
plt.ylabel('standard deviation')
plt.grid(True)
plt.savefig('all_solutions_pareto.png')
plt.savefig('pareto.png')
plt.show()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB