From dfdb10d207e5eec680240f1ec08eabe5483f0678 Mon Sep 17 00:00:00 2001 From: Hgq <2757430053@qq.com> Date: Sun, 7 Dec 2025 17:06:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=A7=A3=E7=9A=84=E8=AF=84?= =?UTF-8?q?=E4=BB=B7=E6=8C=87=E6=A0=87=EF=BC=8C=E6=9C=80=E7=BB=88=E8=A7=A3?= =?UTF-8?q?=E7=9A=84=E9=87=8D=E5=A4=8D=E8=BF=87=E6=BB=A4=E5=92=8C=E6=8E=92?= =?UTF-8?q?=E5=BA=8F=EF=BC=8C=E5=8F=AF=E9=80=89=E8=BE=93=E5=87=BA=E6=95=B0?= =?UTF-8?q?=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data_structures.py | 29 +++++++++++++++++++--- main.py | 62 +++++++++++++++++++++++++++++++++++++++++++++- visualizer.py | 23 +++++++++++------ 3 files changed, 102 insertions(+), 12 deletions(-) diff --git a/data_structures.py b/data_structures.py index b6c1675..a8dd21e 100644 --- a/data_structures.py +++ b/data_structures.py @@ -71,7 +71,7 @@ class Config: """算法参数配置类:存储NSGA-II的各类参数""" def __init__(self): # 种群参数 - self.pop_size = 200 # 种群大小 + self.pop_size = 300 # 种群大小 self.N1_ratio = 0.2 # 优先成本的种群比例 self.N2_ratio = 0.2 # 优先延期的种群比例 self.N3_ratio = 0.3 # 强制风险企业的种群比例 @@ -79,10 +79,33 @@ class Config: # 遗传操作参数 self.crossover_prob = 0.8 # 交叉概率 self.mutation_prob = 0.3 # 变异概率 - self.max_generations = 100 # 最大进化代数 + self.max_generations = 500 # 最大进化代数 # 惩罚系数 self.delta = 1.3 # 变更惩罚系数 # 早停参数 self.early_stop_patience = 50 # 连续多少代无改进则早停 # 目标函数数量 - self.objective_num = 2 # 双目标(成本+延期) \ No newline at end of file + self.objective_num = 2 # 双目标(成本+延期) + self.duplicate_threshold = 0.1 # 重复解保留数量 + self.print_top_n = 5 # 打印前N个最优解 + +class DataStructures: + """数据结构工具类:提供评价指标计算等功能""" + @staticmethod + def calculate_evaluation_index(objectives, optimal_cost, optimal_tardiness): + """ + 计算评价指标 + :param objectives: 解的目标值 (成本, 延期) + :param optimal_cost: 最优成本值 + :param optimal_tardiness: 最优延期值 + :return: 评价指标值 + """ + cost, tardiness = objectives + # 避免除以零(成本最优值为0时的保护) + if optimal_cost == 0: + cost_ratio = 0 if cost == 0 else float('inf') + else: + cost_ratio = cost / optimal_cost + # 延期处理(+1避免除以零) + tardiness_ratio = (tardiness + 1) / (optimal_tardiness + 1) + return cost_ratio + tardiness_ratio \ No newline at end of file diff --git a/main.py b/main.py index 2341358..4266a39 100644 --- a/main.py +++ b/main.py @@ -7,6 +7,8 @@ from encoder import Encoder from genetic_operators import GeneticOperator from nsga2 import NSGA2 from visualizer import ResultVisualizer +from data_structures import DataStructures +"""主函数:执行NSGA-II算法求解多目标优化问题(整数化版本)""" def main(): """主函数:执行NSGA-II算法求解多目标优化问题(整数化版本)""" try: @@ -31,6 +33,7 @@ def main(): print("初始化种群(整数化)...") population = encoder.initialize_population() print(f"初始化种群完成,(种群大小,染色体长度): {population.shape if population.size > 0 else '空'}") + # 若种群初始化失败(为空),直接退出 if population.size == 0: print("错误:种群初始化失败,无法继续进化") @@ -53,6 +56,7 @@ def main(): ranks, fronts = nsga2.fast_non_dominated_sort(objectives) current_front = fronts[0] if fronts else [] # 第0层为最优前沿 current_front_objs = [objectives[i] for i in current_front] if current_front else [] + best_front = population[current_front] if current_front else [] # 更新当前最优前沿解(整数) best_front_objs = current_front_objs # 更新当前最优前沿目标值(整数) # 记录收敛趋势(基于最优前沿的平均目标值,整数) @@ -75,6 +79,7 @@ def main(): # 校验选择后的种群大小 assert len(selected) == config.pop_size, \ f"选择后的种群大小({len(selected)})与目标大小({config.pop_size})不符" + # 交叉操作(两点交叉)- 修复索引越界问题 offspring = [] # 子代种群(整数) selected_len = len(selected) # selected的长度(等于pop_size) @@ -98,6 +103,7 @@ def main(): if len(offspring) < config.pop_size: offspring.append(child2) i += 2 # 处理下一对个体 + else: # 直接添加当前父代(避免i+1越界) offspring.append(selected[i]) @@ -119,6 +125,7 @@ def main(): population, objectives = nsga2.environmental_selection(combined, combined_objs) # 校验环境选择后的种群大小和整数类型 population = population.astype(int) + assert len(population) == config.pop_size, \ f"环境选择后的种群大小({len(population)})与目标大小({config.pop_size})不符" # 早停检查(连续多代无改进则停止) @@ -135,9 +142,62 @@ def main(): # 7. 结果可视化与输出(整数化) print("进化完成,处理结果(整数化)...") if len(best_front_objs) > 0: + # 1. 过滤重复解 + unique_front = [] + unique_front_objs = [] + seen = set() + + for sol, obj in zip(best_front, best_front_objs): + # 将解和目标值组合为元组用于查重 + sol_tuple = tuple(sol.tolist()) + obj_tuple = tuple(obj) + if (sol_tuple, obj_tuple) not in seen: + seen.add((sol_tuple, obj_tuple)) + unique_front.append(sol) + unique_front_objs.append(obj) + + # 2. 计算评价指标并排序 + if unique_front_objs: + # 找出各目标的最优值 + optimal_cost = min(obj[0] for obj in unique_front_objs) + optimal_tardiness = min(obj[1] for obj in unique_front_objs) + + # 计算每个解的评价指标 + evaluated_solutions = [] + for sol, obj in zip(unique_front, unique_front_objs): + index = DataStructures.calculate_evaluation_index( + obj, optimal_cost, optimal_tardiness + ) + evaluated_solutions.append((sol, obj, index)) + + # 按评价指标升序排序 + evaluated_solutions.sort(key=lambda x: x[2]) + + # 选择前n个解 + top_n = min(config.print_top_n, len(evaluated_solutions)) + top_solutions = evaluated_solutions[:top_n] + top_population = [sol for sol, _, _ in top_solutions] + top_objectives = [obj for _, obj, _ in top_solutions] + + # 打印排序后的解的评价指标 + print("\n" + "=" * 100) + print(f"排序后的前{top_n}个最优解(评价指标从小到大)") + print("=" * 100) + for i, (_, obj, idx) in enumerate(top_solutions): + print(f"解 {i + 1}: 成本={obj[0]}, 延期={obj[1]}, 评价指标={idx:.4f}") + else: + top_population = [] + top_objectives = [] + + # 保持原有图表绘制逻辑不变 visualizer.plot_pareto_front(all_objectives, best_front_objs) # 绘制帕累托前沿(整数) visualizer.plot_convergence(convergence_history) # 绘制收敛趋势(整数) - visualizer.print_pareto_solutions(best_front, best_front_objs) # 打印最优解详情(整数) + + # 打印处理后的最优解详情 + if top_population: + visualizer.print_pareto_solutions(top_population, top_objectives) + else: + print("未找到有效帕累托前沿解") else: print("未找到有效帕累托前沿解") except Exception as e: diff --git a/visualizer.py b/visualizer.py index 15afcfc..d11044c 100644 --- a/visualizer.py +++ b/visualizer.py @@ -1,6 +1,9 @@ +# 染色体工具类 +"""结果可视化工具类:绘制帕累托前沿、收敛曲线,打印最优解详情(适配整数化)""" import matplotlib.pyplot as plt import numpy as np -from chromosome_utils import ChromosomeUtils # 染色体工具类 +from chromosome_utils import ChromosomeUtils + class ResultVisualizer: """结果可视化工具类:绘制帕累托前沿、收敛曲线,打印最优解详情(适配整数化)""" def __init__(self, utils: ChromosomeUtils): @@ -12,6 +15,7 @@ class ResultVisualizer: self.figsize = (12, 8) # 图表大小 # 企业名称列表(风险企业+供应商) self.supplier_names = ["RiskEnterprise"] + self.utils.supplier.names + def plot_pareto_front(self, all_objectives: list[tuple[int, int]], non_dominated_objectives: list[tuple[int, int]]): """ @@ -38,6 +42,7 @@ class ResultVisualizer: plt.grid(True, alpha=0.5) plt.savefig('pareto_front_integerized.png', dpi=300, bbox_inches='tight') # 保存图片 plt.show() + def plot_convergence(self, convergence_history: list[tuple[int, int]]): """ 绘制收敛趋势图(每代最优前沿的平均成本和延期,整数化) @@ -64,6 +69,7 @@ class ResultVisualizer: plt.tight_layout() # 调整布局 plt.savefig('convergence_history_integerized.png', dpi=300, bbox_inches='tight') # 保存图片 plt.show() + def print_solution_details(self, solution: np.ndarray, objectives: tuple[int, int]): """ 打印单个解的详细信息(企业选择、产能、数量等,整数化) @@ -101,16 +107,17 @@ class ResultVisualizer: print("✅ 所有物料数量满足需求约束(整数匹配)") else: print("❌ 部分物料数量未满足需求约束") + def print_pareto_solutions(self, population: np.ndarray, objectives: list[tuple[int, int]]): """ - 打印帕累托前沿解的详细信息(最多打印前5个,整数化) - :param population: 帕累托前沿解的种群(整数) - :param objectives: 对应的目标值列表(整数) + 打印排序后的帕累托前沿解详细信息(按评价指标排序) + :param population: 排序后的帕累托前沿解种群 + :param objectives: 对应的目标值列表 """ print("\n" + "=" * 100) - print("帕累托前沿解详细方案(整数化)") + print("按评价指标排序的帕累托前沿解详细方案(整数化)") print("=" * 100) - # 打印前5个解(或所有解,取较少者) - for i in range(min(5, len(population))): - print(f"\n===== 最优解 {i + 1} =====") + # 打印所有选定的解 + for i in range(len(population)): + print(f"\n===== 排序解 {i + 1} =====") self.print_solution_details(population[i], objectives[i]) \ No newline at end of file