import random import numpy as np from data_structures import OrderData, RiskEnterpriseData, SupplierData class ChromosomeUtils: """染色体编码、解码、修复工具类""" def __init__(self, order_data: OrderData, risk_data: RiskEnterpriseData, supplier_data: SupplierData): self.order = order_data self.risk = risk_data self.supplier = supplier_data self.I = order_data.I self.material_optional_enterprises = self._get_material_optional_enterprises() self.material_enterprise_count = [len(ents) for ents in self.material_optional_enterprises] self.chromosome_length = 3 * sum(self.material_enterprise_count) def _get_material_optional_enterprises(self) -> list[list[int]]: """获取每个物料的可选企业列表:0=风险企业,1~supplier_count=供应商""" optional = [] for i in range(self.I): ents = [0] # 先加入风险企业 for j in range(self.supplier.supplier_count): if self.supplier.can_produce[j][i] == 1: ents.append(j + 1) optional.append(ents) return optional def _split_chromosome(self, chromosome: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """拆分染色体为三层:企业编码层、能力编码层、数量编码层""" total_ent_count = sum(self.material_enterprise_count) enterprise_layer = chromosome[:total_ent_count] capacity_layer = chromosome[total_ent_count:2*total_ent_count] quantity_layer = chromosome[2*total_ent_count:] return enterprise_layer, capacity_layer, quantity_layer def _merge_chromosome(self, enterprise_layer: np.ndarray, capacity_layer: np.ndarray, quantity_layer: np.ndarray) -> np.ndarray: """合并三层为完整染色体""" return np.hstack([enterprise_layer, capacity_layer, quantity_layer]) def repair_enterprise_layer(self, enterprise_layer: np.ndarray) -> np.ndarray: """修复企业编码层:确保每种物料至少有一个企业生产""" repaired = enterprise_layer.copy() split_points = np.cumsum(self.material_enterprise_count) start = 0 for i in range(self.I): end = split_points[i] segment = repaired[start:end] if np.sum(segment) == 0: select_idx = random.randint(0, len(segment)-1) segment[select_idx] = 1 repaired[start:end] = segment start = end return repaired def repair_capacity_layer(self, enterprise_layer: np.ndarray, capacity_layer: np.ndarray) -> np.ndarray: """修复能力编码层:满足单物料产能和总产能约束""" repaired = capacity_layer.copy() split_points = np.cumsum(self.material_enterprise_count) start = 0 # 单物料产能约束 for i in range(self.I): end = split_points[i] ents = self.material_optional_enterprises[i] segment = repaired[start:end] 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] else: supplier_id = ent - 1 max_cap = self.supplier.Cj_i_std[supplier_id][i] if segment[idx] <= 0 or segment[idx] > max_cap: segment[idx] = random.uniform(1, max_cap) repaired[start:end] = segment start = end # 总产能约束 enterprise_total_capacity = {} start = 0 for i in range(self.I): end = split_points[i] ents = self.material_optional_enterprises[i] enterprise_segment = enterprise_layer[start:end] capacity_segment = repaired[start:end] for idx, ent in enumerate(ents): if enterprise_segment[idx] == 1: if ent not in enterprise_total_capacity: enterprise_total_capacity[ent] = 0 enterprise_total_capacity[ent] += capacity_segment[idx] start = end # 调整超出总产能的企业 for ent, total_cap in enterprise_total_capacity.items(): if ent == 0: max_total_cap = self.risk.C0_total_max else: supplier_id = ent - 1 max_total_cap = self.supplier.Cj_total_max[supplier_id] if total_cap > max_total_cap: scale = max_total_cap / total_cap start = 0 for i in range(self.I): end = split_points[i] ents = self.material_optional_enterprises[i] enterprise_segment = enterprise_layer[start:end] capacity_segment = repaired[start:end] for idx, e in enumerate(ents): if e == ent and enterprise_segment[idx] == 1: capacity_segment[idx] *= scale repaired[start:end] = capacity_segment start = end return repaired def repair_quantity_layer(self, enterprise_layer: np.ndarray, quantity_layer: np.ndarray) -> np.ndarray: """修复数量编码层:满足总量需求、起订量和最大供应量约束""" 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] end = split_points[i] ents = self.material_optional_enterprises[i] segment = repaired[start:end] enterprise_segment = enterprise_layer[start:end] selected_indices = np.where(enterprise_segment == 1)[0] if len(selected_indices) == 0: start = end continue selected_ents = [ents[idx] for idx in selected_indices] # 步骤1:初始化合法范围(起订量~最大供应量) for idx, ent in zip(selected_indices, selected_ents): if ent == 0: min_q = 0 max_q = qi else: supplier_id = ent - 1 min_q = self.supplier.MinOrder[supplier_id][i] max_q = self.supplier.MaxOrder[supplier_id][i] if segment[idx] < min_q: segment[idx] = min_q elif segment[idx] > max_q: segment[idx] = max_q # 步骤2:强制总量等于需求数量(核心修复) current_total = np.sum(segment[selected_indices]) if current_total <= 0: weights = np.random.rand(len(selected_indices)) weights /= np.sum(weights) segment[selected_indices] = qi * weights else: scale = qi / current_total segment[selected_indices] *= scale # 步骤3:处理超出最大供应量的情况(修复中文变量名+逻辑) need_adjust = True while need_adjust: need_adjust = False total_excess = 0 for idx, ent in zip(selected_indices, selected_ents): if ent == 0: max_q = qi else: supplier_id = ent - 1 max_q = self.supplier.MaxOrder[supplier_id][i] if segment[idx] > max_q: excess = segment[idx] - max_q total_excess += excess segment[idx] = max_q need_adjust = True if total_excess > 0: available_indices = [] available_max = [] for idx, ent in zip(selected_indices, selected_ents): if ent == 0: max_q = qi else: supplier_id = ent - 1 max_q = self.supplier.MaxOrder[supplier_id][i] remaining = max_q - segment[idx] if remaining > 0: available_indices.append(idx) available_max.append(remaining) if len(available_indices) > 0: available_total = sum(available_max) if available_total > 0: alloc_ratio = np.array(available_max) / available_total alloc_excess = total_excess * alloc_ratio for idx, alloc in zip(available_indices, alloc_excess): segment[idx] += alloc else: segment[selected_indices] = np.minimum(segment[selected_indices], [self.supplier.MaxOrder[ent-1][i] if ent !=0 else qi for ent in selected_ents]) current_total = np.sum(segment[selected_indices]) if abs(current_total - qi) > 1e-6: scale = qi / current_total segment[selected_indices] *= scale need_adjust = True repaired[start:end] = segment start = end return repaired def repair_chromosome(self, chromosome: np.ndarray) -> np.ndarray: """完整修复染色体:企业层→能力层→数量层""" enterprise_layer, capacity_layer, quantity_layer = self._split_chromosome(chromosome) enterprise_layer = self.repair_enterprise_layer(enterprise_layer) capacity_layer = self.repair_capacity_layer(enterprise_layer, capacity_layer) quantity_layer = self.repair_quantity_layer(enterprise_layer, quantity_layer) return self._merge_chromosome(enterprise_layer, capacity_layer, quantity_layer)