From 6d4aa70df6ff92ffb88ba9b52ca0b01016418288 Mon Sep 17 00:00:00 2001 From: Hgq <2757430053@qq.com> Date: Sat, 6 Dec 2025 17:43:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=9F=E5=AE=9A=E4=BA=86=E8=BE=83=E5=A4=A7?= =?UTF-8?q?=E7=9A=84=E8=BE=93=E5=85=A5=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chromosome_utils.py | 33 ++++++++++------- data_structures.py | 87 +++++++++++++++++--------------------------- encoder.py | 27 +++----------- genetic_operators.py | 41 ++++----------------- 4 files changed, 66 insertions(+), 122 deletions(-) diff --git a/chromosome_utils.py b/chromosome_utils.py index 109d624..dbf661e 100644 --- a/chromosome_utils.py +++ b/chromosome_utils.py @@ -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 @@ -161,8 +168,8 @@ class ChromosomeUtils: repaired = quantity_layer.copy() split_points = np.cumsum(self.material_enterprise_count) start = 0 - for i in range(self.I): # 遍历每种物料 - qi = self.order.Q[i] # 物料i的需求量 + for i in range(self.I): # 遍历每种物料 + qi = self.order.Q[i] # 物料i的需求量 end = split_points[i] ents = self.material_optional_enterprises[i] segment = repaired[start:end] diff --git a/data_structures.py b/data_structures.py index 2448e7c..18b1e7c 100644 --- a/data_structures.py +++ b/data_structures.py @@ -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 × I),1=能生产,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 × I),0表示不能生产该物料 + 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 # 双目标(成本+延期) \ No newline at end of file diff --git a/encoder.py b/encoder.py index f998355..eda9116 100644 --- a/encoder.py +++ b/encoder.py @@ -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: diff --git a/genetic_operators.py b/genetic_operators.py index 0fce959..1d52aa3 100644 --- a/genetic_operators.py +++ b/genetic_operators.py @@ -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 \ No newline at end of file