235 lines
13 KiB
Python
235 lines
13 KiB
Python
import random
|
||
import numpy as np
|
||
from data_structures import OrderData, RiskEnterpriseData, SupplierData, Config
|
||
from chromosome_utils import ChromosomeUtils
|
||
from objective_calculator import ObjectiveCalculator
|
||
from encoder import Encoder
|
||
from genetic_operators import GeneticOperator
|
||
from nsga2 import NSGA2
|
||
from visualizer import ResultVisualizer
|
||
from data_structures import DataStructures
|
||
def main():
|
||
"""主函数:执行NSGA-II算法求解多目标优化问题"""
|
||
try:
|
||
# 1. 初始化随机种子(确保结果可复现)
|
||
random.seed(42)
|
||
np.random.seed(42)
|
||
# 2. 初始化数据(订单、风险企业、供应商、算法配置)
|
||
print("初始化数据结构...")
|
||
order_data = OrderData() # 订单数据(需求、交货期等,整数)
|
||
risk_data = RiskEnterpriseData() # 风险企业数据
|
||
supplier_data = SupplierData() # 供应商数据
|
||
config = Config() # 算法参数配置
|
||
# 3. 初始化工具类和算法组件
|
||
print("初始化算法组件...")
|
||
utils = ChromosomeUtils(order_data, risk_data, supplier_data) # 染色体工具
|
||
calculator = ObjectiveCalculator(order_data, risk_data, supplier_data, utils, config) # 目标函数计算器
|
||
encoder = Encoder(config, utils) # 种群初始化编码器
|
||
genetic_op = GeneticOperator(config, utils) # 遗传操作器
|
||
nsga2 = NSGA2(config.pop_size, config.objective_num) # NSGA-II算法实例
|
||
visualizer = ResultVisualizer(utils) # 结果可视化工具
|
||
# 4. 初始化种群
|
||
print("初始化种群...")
|
||
population = encoder.initialize_population()
|
||
print(f"初始化种群完成,(种群大小,染色体长度): {population.shape if population.size > 0 else '空'}")
|
||
|
||
|
||
# 若种群初始化失败(为空),直接退出
|
||
if population.size == 0:
|
||
print("错误:种群初始化失败,无法继续进化")
|
||
return
|
||
# 5. 记录进化过程中的历史数据
|
||
all_objectives = [] # 所有代的目标函数值
|
||
convergence_history = [] # 收敛趋势(每代最优前沿的平均目标值,整数)
|
||
best_front = [] # 最终帕累托前沿解
|
||
best_front_objs = [] # 最终帕累托前沿的目标值
|
||
prev_avg_cost = None # 上一代的平均成本
|
||
prev_avg_tardiness = None # 上一代的平均延期
|
||
no_improve_count = 0 # 无改进计数器
|
||
# 6. 进化主循环
|
||
print(f"开始进化(最大代数:{config.max_generations},早停耐心:{config.early_stop_patience})...")
|
||
for generation in range(config.max_generations):
|
||
try:
|
||
# 计算当前种群的目标函数值
|
||
objectives = [calculator.calculate_objectives(chrom) for chrom in population]
|
||
all_objectives.extend(objectives) # 记录所有目标值
|
||
# 非支配排序,获取当前代的帕累托前沿
|
||
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 # 更新当前最优前沿目标值
|
||
# 记录收敛趋势(基于最优前沿的平均目标值,整数)
|
||
# 记录收敛趋势并判断早停条件
|
||
if len(current_front_objs) > 0:
|
||
avg_cost = sum(obj[0] for obj in current_front_objs) // len(current_front_objs)
|
||
avg_tardiness = sum(obj[1] for obj in current_front_objs) // len(current_front_objs)
|
||
convergence_history.append((avg_cost, avg_tardiness))
|
||
|
||
# 早停判断逻辑
|
||
if prev_avg_cost is not None and prev_avg_tardiness is not None:
|
||
# 计算两个目标的变化量
|
||
cost_change = abs(avg_cost - prev_avg_cost)
|
||
tardiness_change = abs(avg_tardiness - prev_avg_tardiness)
|
||
|
||
# 检查是否两个目标的变化率
|
||
if (cost_change < config.early_stop_threshold * prev_avg_cost and
|
||
tardiness_change < config.early_stop_threshold * prev_avg_tardiness):
|
||
no_improve_count += 1
|
||
|
||
else:
|
||
no_improve_count = 0 # 有改进,重置计数器
|
||
prev_avg_cost = avg_cost
|
||
prev_avg_tardiness = avg_tardiness
|
||
else:
|
||
# 初始化历史值(第一代)
|
||
prev_avg_cost = avg_cost
|
||
prev_avg_tardiness = avg_tardiness
|
||
no_improve_count = 0
|
||
else:
|
||
no_improve_count += 1 # 无前沿解,视为无改进
|
||
|
||
# 早停检查(连续多代无改进则停止)
|
||
if no_improve_count >= config.early_stop_patience:
|
||
print(
|
||
f"早停触发:连续{no_improve_count}代两个目标值变化率均小于{config.early_stop_threshold},终止于第{generation}代")
|
||
break
|
||
# 选择操作(锦标赛选择)
|
||
selected = nsga2.selection(population, objectives)
|
||
# 校验选择后的种群大小
|
||
assert len(selected) == config.pop_size, \
|
||
f"选择后的种群大小({len(selected)})与目标大小({config.pop_size})不符"
|
||
|
||
# 交叉操作(两点交叉)- 修复索引越界问题
|
||
offspring = [] # 子代种群
|
||
selected_len = len(selected) # selected的长度(等于pop_size)
|
||
i = 0
|
||
max_iter = 2 * config.pop_size # 最大迭代次数,避免无限循环
|
||
|
||
iter_count = 0
|
||
while len(offspring) < config.pop_size and iter_count < max_iter:
|
||
iter_count += 1
|
||
# 检查i是否超出selected范围,若超出则重置(循环使用个体)
|
||
if i >= selected_len:
|
||
i = 0
|
||
# 尝试两两交叉(需i+1在范围内,且满足交叉概率)
|
||
if i + 1 < selected_len and random.random() < config.crossover_prob:
|
||
parent1 = selected[i]
|
||
parent2 = selected[i + 1]
|
||
child1, child2 = genetic_op.two_point_crossover(parent1, parent2)
|
||
# 添加child1(若未达到种群大小)
|
||
if len(offspring) < config.pop_size:
|
||
offspring.append(child1)
|
||
# 添加child2(若未达到种群大小)
|
||
if len(offspring) < config.pop_size:
|
||
offspring.append(child2)
|
||
i += 2 # 处理下一对个体
|
||
|
||
else:
|
||
# 直接添加当前父代(避免i+1越界)
|
||
offspring.append(selected[i])
|
||
i += 1 # 处理下一个个体(步长改为1,避免快速越界)
|
||
# 若迭代次数用尽仍未生成足够子代,补充随机个体(健壮性处理,整数)
|
||
while len(offspring) < config.pop_size:
|
||
|
||
offspring.append(encoder._generate_random_chromosome()) # 整数化随机染色体
|
||
# 变异操作(均匀变异,整数化)
|
||
offspring = [
|
||
genetic_op.uniform_mutation(chrom) if random.random() < config.mutation_prob else chrom
|
||
for chrom in offspring
|
||
]
|
||
offspring = np.array(offspring[:config.pop_size]).astype(int) # 确保子代大小和整数类型
|
||
# 合并父代和子代,准备环境选择
|
||
combined = np.vstack([population, offspring]).astype(int) # 合并种群
|
||
# 计算合并种群的目标函数值
|
||
combined_objs = objectives + [calculator.calculate_objectives(chrom) for chrom in offspring]
|
||
# 环境选择(保留最优的pop_size个个体)
|
||
population, objectives = nsga2.environmental_selection(combined, combined_objs)
|
||
# 校验环境选择后的种群大小和整数类型
|
||
population = population.astype(int)
|
||
|
||
assert len(population) == config.pop_size, \
|
||
f"环境选择后的种群大小({len(population)})与目标大小({config.pop_size})不符"
|
||
# 早停检查(连续多代无改进则停止)
|
||
if no_improve_count >= config.early_stop_patience:
|
||
print(f"早停触发:连续{no_improve_count}代无改进,终止于第{generation}代")
|
||
|
||
break
|
||
# 每50代打印一次进度
|
||
if generation % 50 == 0:
|
||
front_size = len(current_front) if current_front else 0
|
||
print(f"第{generation}代完成,当前最优前沿大小: {front_size},无改进计数: {no_improve_count}")
|
||
except Exception as e:
|
||
print(f"第{generation}代进化出错:{str(e)},跳过当前代")
|
||
continue
|
||
# 7. 结果可视化与输出
|
||
print("进化完成,处理结果...")
|
||
if len(best_front_objs) > 0:
|
||
# 1. 过滤重复解(关键改进:基于目标值去重,确保相同目标值只保留一个解)
|
||
unique_front = []
|
||
unique_front_objs = []
|
||
seen_obj = set() # 仅跟踪目标值,确保相同目标值只保留一个
|
||
|
||
for sol, obj in zip(best_front, best_front_objs):
|
||
obj_tuple = tuple(obj) # 将目标值转为元组用于查重
|
||
if obj_tuple not in seen_obj:
|
||
seen_obj.add(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)
|
||
max_cost = max(obj[0] for obj in unique_front_objs)
|
||
max_tardiness = max(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, max_cost, max_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) # 绘制收敛趋势
|
||
|
||
# 打印处理后的最优解详情
|
||
if top_population:
|
||
visualizer.print_pareto_solutions(top_population, top_objectives)
|
||
else:
|
||
print("未找到有效帕累托前沿解")
|
||
else:
|
||
print("未找到有效帕累托前沿解")
|
||
except Exception as e:
|
||
print(f"程序运行出错:{str(e)}")
|
||
import traceback
|
||
traceback.print_exc() # 打印详细错误栈
|
||
if __name__ == "__main__":
|
||
print("程序启动...")
|
||
main()
|
||
print("程序结束") |