增加解的评价指标,最终解的重复过滤和排序,可选输出数量

This commit is contained in:
Hgq 2025-12-07 17:06:36 +08:00
parent 6a482e6af1
commit dfdb10d207
3 changed files with 102 additions and 12 deletions

View File

@ -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 # 双目标(成本+延期)
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

62
main.py
View File

@ -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:

View File

@ -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])