拟定了较大的输入数据

This commit is contained in:
Hgq 2025-12-06 17:43:18 +08:00
parent be23f5d29e
commit 6d4aa70df6
4 changed files with 66 additions and 122 deletions

View File

@ -17,7 +17,6 @@ class ChromosomeUtils:
self.risk = risk_data # 风险企业数据
self.supplier = supplier_data # 供应商数据
self.I = order_data.I # 物料种类数
# 预计算每个物料的可选企业列表0=风险企业1+为供应商ID
self.material_optional_enterprises = self._get_material_optional_enterprises()
# 每个物料的可选企业数量
@ -85,7 +84,7 @@ class ChromosomeUtils:
def repair_capacity_layer(self, enterprise_layer: np.ndarray, capacity_layer: np.ndarray) -> np.ndarray:
"""
修复能力层确保满足单物料产能约束和企业总产能约束
修复能力层满足单物料最小产能约束和企业总产能约束
:param enterprise_layer: 企业层用于确定哪些企业被选中
:param capacity_layer: 待修复的能力层
:return: 修复后的能力层
@ -94,7 +93,7 @@ class ChromosomeUtils:
split_points = np.cumsum(self.material_enterprise_count)
start = 0
# 1. 修复单物料产能约束(每个企业的单物料产能不超过上限
# 1. 修复单物料最小产能约束(每个企业的单物料产能不低于最小产能
for i in range(self.I):
end = split_points[i]
ents = self.material_optional_enterprises[i] # 可选企业列表
@ -102,15 +101,16 @@ class ChromosomeUtils:
enterprise_segment = enterprise_layer[start:end] # 企业选择状态
for idx, ent in enumerate(ents):
if enterprise_segment[idx] == 1: # 仅处理被选中的企业
# 确定该企业的单物料最产能
# 确定该企业的单物料最产能
if ent == 0:
max_cap = self.risk.C0_i_std[i] # 风险企业
min_cap = self.risk.C0_i_min[i] # 风险企业最小产能
else:
supplier_id = ent - 1
max_cap = self.supplier.Cj_i_std[supplier_id][i] # 供应商
# 若产能不合法重置为1到max_cap之间的随机值
if segment[idx] <= 0 or segment[idx] > max_cap:
segment[idx] = random.uniform(1, max_cap)
min_cap = self.supplier.Cj_i_min[supplier_id][i] # 供应商最小产能
# 若产能低于最小产能重置为最小产能到2倍最小产能之间的随机值无上限约束合理范围控制
if segment[idx] < min_cap:
segment[idx] = random.uniform(min_cap, min_cap * 2)
repaired[start:end] = segment
start = end
@ -140,7 +140,7 @@ class ChromosomeUtils:
max_total_cap = self.supplier.Cj_total_max[supplier_id] # 供应商总产能上限
if total_cap > max_total_cap: # 超出上限时缩放
scale = max_total_cap / total_cap # 缩放比例(会出现浮点)
scale = max_total_cap / total_cap # 缩放比例
start = 0
# 重新遍历,按比例缩小该企业所有物料的产能
for i in range(self.I):
@ -150,7 +150,14 @@ class ChromosomeUtils:
capacity_segment = repaired[start:end]
for idx, e in enumerate(ents):
if e == ent and enterprise_segment[idx] == 1:
capacity_segment[idx] *= scale # 按比例缩小
# 缩放后仍需保证不低于最小产能
scaled_cap = capacity_segment[idx] * scale
if ent == 0:
min_cap = self.risk.C0_i_min[i]
else:
supplier_id = e - 1
min_cap = self.supplier.Cj_i_min[supplier_id][i]
capacity_segment[idx] = max(scaled_cap, min_cap)
repaired[start:end] = capacity_segment
start = end

View File

@ -1,36 +1,28 @@
# 数据结构定义:存储订单、企业、供应商数据及算法配置
class OrderData:
"""订单数据类:存储物料需求、交货期、成本等信息"""
def __init__(self):
self.I = 5 # 物料种类数
self.Q = [250, 300, 200, 350, 280] # 各物料的需求数量
self.Dd = 12 # 需求交货期(单位:时间)
self.P0 = [50, 80, 60, 70, 90] # 风险企业的单位采购价
self.Q = [6000, 12000, 20000, 7500, 13500] # 各物料的需求数量
self.Dd = 30 # 需求交货期(单位:时间)
self.P0 = [45, 30, 30, 50, 40] # 风险企业的单位采购价
self.T0 = [5, 8, 6, 7, 9] # 风险企业的单位运输成本
self.transport_speed = 10 # 运输速度(单位:距离/时间)
class RiskEnterpriseData:
"""风险企业数据类:存储风险企业的产能、距离等信息"""
def __init__(self):
self.I = 5 # 物料种类数(与订单一致)
self.C0_i_std = [40, 50, 35, 45, 48] # 单物料的单位时间标准产能
self.C0_total_max = 100 # 总产能上限(单位时间)
self.distance = 10 # 与需求点的距离
self.C0_i_min = [50, 100, 150, 80, 100] # 单物料的单位时间最小产能
self.C0_total_max = 900 # 总产能上限(单位时间)
self.distance = 20 # 与需求点的距离
class SupplierData:
"""供应商数据类:存储各供应商的产能、价格、距离等信息"""
def __init__(self, I=5):
self.I = I # 物料种类数
self.supplier_count = 4 # 供应商数量
self.names = ["S0", "S1", "S2", "S3"] # 供应商名称
# 能否生产某物料的矩阵supplier_count × I1=能生产0=不能
self.can_produce = [
[1, 1, 1, 1, 1],
@ -38,76 +30,63 @@ class SupplierData:
[0, 1, 0, 1, 0],
[0, 0, 1, 1, 1]
]
# 单物料单位时间标准产能supplier_count × I
self.Cj_i_std = [
[20, 18, 15, 22, 25],
[25, 0, 30, 0, 28],
[0, 22, 0, 35, 0],
[0, 0, 20, 30, 22]
# 单物料单位时间最小产能supplier_count × I0表示不能生产该物料
self.Cj_i_min = [
[30, 80, 100, 60, 80],
[60, 0, 180, 0, 120],
[0, 150, 0, 120, 0],
[0, 0, 170, 105, 115]
]
# 供应商单位时间的最大总产能supplier_count
self.Cj_total_max = [120, 110, 100, 95]
self.Cj_total_max = [700, 800, 600, 850]
# 最小起订量supplier_count × I
self.MinOrder = [
[20, 20, 15, 25, 20],
[30, 0, 25, 0, 30],
[0, 25, 0, 30, 0],
[0, 0, 20, 35, 25]
[800, 1500, 3000, 800, 1500],
[1000, 0, 3500, 0, 1800],
[0, 1700, 0, 1000, 0],
[0, 0, 2500, 500, 1000]
]
# 最大供应量supplier_count × I
self.MaxOrder = [
[100, 150, 80, 120, 130],
[120, 0, 100, 0, 110],
[0, 140, 0, 150, 0],
[0, 0, 90, 130, 100]
[5000, 10000, 18000, 6500, 11000],
[8000, 0, 25000, 0, 15000],
[0, 8000, 0, 6000, 0],
[0, 0, 20000, 7500, 13500]
]
# 单位采购价格supplier_count × I
self.P_ij = [
[60, 85, 70, 80, 100],
[65, 0, 75, 0, 105],
[0, 90, 0, 85, 0],
[0, 0, 78, 88, 98]
[50, 35, 28, 47, 38],
[43, 0, 28, 0, 36],
[0, 31, 0, 52, 0],
[0, 0, 32, 52, 43]
]
# 单位运输成本supplier_count × I
self.T_ij = [
[7, 9, 8, 10, 12],
[6, 0, 9, 0, 11],
[0, 10, 0, 12, 0],
[0, 0, 10, 13, 14]
[6, 9, 8, 9, 12],
[4, 0, 5, 0, 15],
[0, 10, 0, 7, 0],
[0, 0, 8, 9, 11]
]
# 供应商与需求点的距离supplier_count
self.distance = [45, 35, 60, 50]
self.distance = [60, 50, 70, 40]
class Config:
"""算法参数配置类存储NSGA-II的各类参数"""
def __init__(self):
# 种群参数
self.pop_size = 300 # 种群大小
self.pop_size = 100 # 种群大小
self.N1_ratio = 0.2 # 优先成本的种群比例
self.N2_ratio = 0.2 # 优先延期的种群比例
self.N3_ratio = 0.3 # 强制风险企业的种群比例
self.N4_ratio = 0.3 # 随机种群比例
# 遗传操作参数
self.crossover_prob = 0.8 # 交叉概率
self.mutation_prob = 0.2 # 变异概率
self.max_generations = 800 # 最大进化代数
self.mutation_prob = 0.3 # 变异概率
self.max_generations = 300 # 最大进化代数
# 惩罚系数
self.delta = 1.3 # 变更惩罚系数
self.gamma = 500 # 提前交付惩罚系数
# 早停参数
self.early_stop_patience = 50 # 连续多少代无改进则早停
# 目标函数数量
self.objective_num = 2 # 双目标(成本+延期)

View File

@ -5,10 +5,8 @@ from chromosome_utils import ChromosomeUtils
from objective_calculator import ObjectiveCalculator
from nsga2 import NSGA2
class Encoder:
"""种群初始化编码器:生成初始种群,包含多种初始化策略"""
def __init__(self, config: Config, utils: ChromosomeUtils):
"""
初始化编码器
@ -18,7 +16,6 @@ class Encoder:
self.config = config
self.utils = utils
self.pop_size = config.pop_size # 种群总大小
# 按比例分配四种初始化策略的个体数量
self.N1 = int(config.N1_ratio * self.pop_size) # 优先最小化成本的个体数
self.N2 = int(config.N2_ratio * self.pop_size) # 优先最小化延期的个体数
@ -34,11 +31,9 @@ class Encoder:
enterprise_layer = [] # 企业选择层0/1
capacity_layer = [] # 产能层
quantity_layer = [] # 数量层
for i in range(self.utils.I): # 遍历每种物料
ent_count = self.utils.material_enterprise_count[i] # 当前物料的可选企业数量
ents = self.utils.material_optional_enterprises[i] # 可选企业列表0为风险企业
# 1. 生成企业层随机选择至少1个企业
e_genes = np.zeros(ent_count) # 初始化企业选择为0未选择
select_count = random.randint(1, ent_count) # 随机选择1到ent_count个企业
@ -47,19 +42,18 @@ class Encoder:
if force_risk_enterprise:
e_genes[0] = 1 # 强制选择风险企业索引0
enterprise_layer.extend(e_genes)
# 2. 生成能力层(为选中的企业随机分配产能)
# 2. 生成能力层(为选中的企业分配不低于最小产能的随机产能)
c_genes = np.zeros(ent_count)
for idx, ent in enumerate(ents):
if e_genes[idx] == 1: # 仅为选中的企业分配产能
if ent == 0:
max_cap = self.utils.risk.C0_i_std[i] # 风险企业的最大产能
min_cap = self.utils.risk.C0_i_min[i] # 风险企业的最小产能
else:
supplier_id = ent - 1
max_cap = self.utils.supplier.Cj_i_std[supplier_id][i] # 供应商的最大产能
c_genes[idx] = random.uniform(1, max_cap) # 随机产能
min_cap = self.utils.supplier.Cj_i_min[supplier_id][i] # 供应商的最小产能
# 生成不低于最小产能的随机值无上限用2倍最小产能控制初始范围
c_genes[idx] = random.uniform(min_cap, min_cap * 2)
capacity_layer.extend(c_genes)
# 3. 生成数量层(为选中的企业随机分配数量)
q_genes = np.zeros(ent_count)
for idx, ent in enumerate(ents):
@ -71,7 +65,6 @@ class Encoder:
max_q = self.utils.supplier.MaxOrder[supplier_id][i] # 供应商的最大供应量
q_genes[idx] = random.uniform(1, max_q) # 随机数量
quantity_layer.extend(q_genes)
# 合并三层并修复染色体(确保满足所有约束)
chromosome = self.utils._merge_chromosome(
np.array(enterprise_layer), np.array(capacity_layer), np.array(quantity_layer)
@ -88,12 +81,10 @@ class Encoder:
pop2 = self._initialize_by_objective(self.N2, "tardiness") # 优先延期
pop3 = self._initialize_by_risk_enterprise(self.N3) # 强制风险企业
pop4 = self._initialize_random(self.N4) # 随机生成
# 过滤空数组(避免合并时报错)
population_list = [p for p in [pop1, pop2, pop3, pop4] if len(p) > 0]
if len(population_list) == 0: # 所有子种群都为空时返回空数组
return np.array([])
# 合并子种群并打乱顺序
population = np.vstack(population_list)
np.random.shuffle(population)
@ -108,16 +99,13 @@ class Encoder:
"""
if count <= 0: # 数量为0时返回空数组
return np.array([])
# 生成候选解数量为count的3倍或至少20个确保有足够选择空间
candidate_count = max(3 * count, 20)
candidates = [self._generate_random_chromosome() for _ in range(candidate_count)]
# 计算候选解的目标函数值
calculator = ObjectiveCalculator(self.utils.order, self.utils.risk, self.utils.supplier, self.utils,
self.config)
objectives = [calculator.calculate_objectives(chrom) for chrom in candidates]
# 按目标函数排序并选择前count个
if objective_type == "cost":
# 按成本升序排序(成本越小越优)
@ -125,7 +113,6 @@ class Encoder:
else:
# 按延期升序排序(延期越小越优)
sorted_indices = sorted(range(candidate_count), key=lambda x: objectives[x][1])
return np.array([candidates[i] for i in sorted_indices[:count]])
def _initialize_by_risk_enterprise(self, count: int) -> np.ndarray:
@ -136,18 +123,15 @@ class Encoder:
"""
if count <= 0:
return np.array([])
# 生成强制选择风险企业的候选解
candidate_count = max(2 * count, 20)
candidates = [self._generate_random_chromosome(force_risk_enterprise=True) for _ in range(candidate_count)]
# 计算目标函数并进行非支配排序
calculator = ObjectiveCalculator(self.utils.order, self.utils.risk, self.utils.supplier, self.utils,
self.config)
objectives = [calculator.calculate_objectives(chrom) for chrom in candidates]
nsga2 = NSGA2(candidate_count, 2) # 2个目标函数
ranks, fronts = nsga2.fast_non_dominated_sort(objectives) # 非支配排序
# 从帕累托前沿开始选择,直到满足数量
selected = []
for front in fronts:
@ -157,7 +141,6 @@ class Encoder:
# 前沿数量超过剩余需求时,取部分
selected.extend([candidates[i] for i in front[:count - len(selected)]])
break
return np.array(selected)
def _initialize_random(self, count: int) -> np.ndarray:

View File

@ -3,10 +3,8 @@ import numpy as np
from data_structures import Config # 算法配置参数类
from chromosome_utils import ChromosomeUtils # 染色体工具类
class GeneticOperator:
"""遗传操作类:实现交叉和变异操作,用于产生新个体"""
def __init__(self, config: Config, utils: ChromosomeUtils):
"""
初始化遗传操作器
@ -26,17 +24,14 @@ class GeneticOperator:
length = self.utils.chromosome_length # 染色体总长度
if length < 2: # 染色体长度不足2时无法交叉直接返回父代副本
return parent1.copy(), parent2.copy()
# 随机选择两个交叉点point1 < point2
point1 = random.randint(0, length // 2)
point2 = random.randint(point1 + 1, length - 1)
# 复制父代基因并交换片段
child1 = parent1.copy()
child2 = parent2.copy()
child1[point1:point2] = parent2[point1:point2] # 交换point1到point2之间的基因
child2[point1:point2] = parent1[point1:point2]
# 修复染色体(确保满足约束条件)
child1 = self.utils.repair_chromosome(child1)
child2 = self.utils.repair_chromosome(child2)
@ -49,14 +44,11 @@ class GeneticOperator:
:return: 变异后的染色体经过修复
"""
mutated = chromosome.copy() # 复制原始染色体,避免直接修改
# 拆分染色体为三层
enterprise_layer, capacity_layer, quantity_layer = self.utils._split_chromosome(mutated)
split_points = np.cumsum(self.utils.material_enterprise_count)
# ========== 第一步:记录原始企业层状态 ==========
original_enterprise_layer = enterprise_layer.copy()
# ========== 第二步:企业层变异 ==========
start = 0
for i in range(self.utils.I):
@ -65,37 +57,30 @@ class GeneticOperator:
if random.random() < self.config.mutation_prob:
enterprise_layer[idx] = 1 - enterprise_layer[idx]
start = end
# 修复企业层
enterprise_layer = self.utils.repair_enterprise_layer(enterprise_layer)
# ========== 第三步:同步更新能力和数量层 ==========
start = 0
for i in range(self.utils.I):
end = split_points[i]
ents = self.utils.material_optional_enterprises[i]
for idx in range(start, end):
current_ent_selected = enterprise_layer[idx] # 变异后的选择状态
original_ent_selected = original_enterprise_layer[idx] # 变异前的选择状态
# 情况1企业从选中变为未选中1→0
if original_ent_selected == 1 and current_ent_selected == 0:
capacity_layer[idx] = 0 # 产能设为0
quantity_layer[idx] = 0 # 数量设为0
# 情况2企业从未选中变为选中0→1
elif original_ent_selected == 0 and current_ent_selected == 1:
ent = ents[idx - start]
# 初始化产能
# 初始化产能(不低于最小产能)
if ent == 0:
max_cap = self.utils.risk.C0_i_std[i]
min_cap = self.utils.risk.C0_i_min[i]
else:
supplier_id = ent - 1
max_cap = self.utils.supplier.Cj_i_std[supplier_id][i]
capacity_layer[idx] = random.uniform(1, max_cap)
min_cap = self.utils.supplier.Cj_i_min[supplier_id][i]
capacity_layer[idx] = random.uniform(min_cap, min_cap * 2)
# 初始化数量
qi = self.utils.order.Q[i]
if ent == 0:
@ -104,43 +89,36 @@ class GeneticOperator:
supplier_id = ent - 1
max_q = self.utils.supplier.MaxOrder[supplier_id][i]
quantity_layer[idx] = random.uniform(1, max_q)
# 情况3企业保持选中1→1或保持未选中0→0
# 保持不变,后续的变异步骤会处理
start = end
# ========== 第四步:能力层变异(仅对保持选中的企业) ==========
start = 0
for i in range(self.utils.I):
end = split_points[i]
ents = self.utils.material_optional_enterprises[i]
e_segment = enterprise_layer[start:end]
for idx in range(start, end):
# 只对保持选中的企业进行变异1→1的情况
if e_segment[idx - start] == 1 and original_enterprise_layer[idx] == 1:
if random.random() < self.config.mutation_prob:
ent = ents[idx - start]
if ent == 0:
max_cap = self.utils.risk.C0_i_std[i]
min_cap = self.utils.risk.C0_i_min[i]
else:
supplier_id = ent - 1
max_cap = self.utils.supplier.Cj_i_std[supplier_id][i]
capacity_layer[idx] = random.uniform(1, max_cap)
min_cap = self.utils.supplier.Cj_i_min[supplier_id][i]
# 变异后产能不低于最小产能
capacity_layer[idx] = random.uniform(min_cap, min_cap * 2)
start = end
# 修复能力层(考虑所有选中企业的产能)
capacity_layer = self.utils.repair_capacity_layer(enterprise_layer, capacity_layer)
# ========== 第五步:数量层变异(仅对保持选中的企业) ==========
start = 0
for i in range(self.utils.I):
end = split_points[i]
ents = self.utils.material_optional_enterprises[i]
e_segment = enterprise_layer[start:end]
for idx in range(start, end):
# 只对保持选中的企业进行变异1→1的情况
if e_segment[idx - start] == 1 and original_enterprise_layer[idx] == 1:
@ -153,12 +131,9 @@ class GeneticOperator:
supplier_id = ent - 1
max_q = self.utils.supplier.MaxOrder[supplier_id][i]
quantity_layer[idx] = random.uniform(1, max_q)
start = end
# 修复数量层(考虑所有选中企业的分配)
quantity_layer = self.utils.repair_quantity_layer(enterprise_layer, quantity_layer)
# 合并三层
mutated = self.utils._merge_chromosome(enterprise_layer, capacity_layer, quantity_layer)
return mutated