Python中带启动时间约束的堆叠面积图优化实现求助
解决方案:带启动时间约束的堆叠面积图优化
核心思路
每个项目的人力需求曲线可沿时间轴(X轴)平移,平移量即为项目的启动时间。我们需要在给定约束下,遍历/搜索所有可行的启动时间组合,计算每个组合对应的堆叠曲线目标值(总面积、峰值、斜率平缓度、总曲线长度),最终筛选出Top3最优方案并可视化。
实现步骤与代码
1. 基础准备与约束定义
先明确每个项目的人力曲线、时间轴长度,以及启动时间约束:
- 时间轴设为
0~4(对应原数据的5个时间点) - 约束示例:
- 项目1:
1 ≤ x1 < 2(离散时间下取x1=1) - 项目2:
x2=4(固定启动时间) - 项目3:
x3=x1(与项目1同步启动)
- 项目1:
2. 曲线平移函数
实现函数将项目的人力曲线按启动时间平移,生成对应时间轴上的完整序列:
import pandas as pd import matplotlib.pyplot as plt import numpy as np from itertools import product def shift_curve(curve, start_time, total_time): """ 将项目人力曲线按启动时间平移,生成总时间轴上的序列 :param curve: 项目原始人力需求列表 :param start_time: 启动时间(时间轴起始为0) :param total_time: 总时间点数量(原数据长度) :return: 平移后的人力序列 """ shifted = [0]*total_time # 曲线从start_time开始填充,超出总时间的部分截断 end_idx = min(start_time + len(curve), total_time) shifted[start_time:end_idx] = curve[:end_idx - start_time] return shifted
3. 目标函数定义
根据需求定义四个优化目标:
def calculate_objectives(stacked_curve): """计算堆叠曲线的四个目标值""" # 1. 总面积:所有时间点人力之和 total_area = sum(stacked_curve) # 2. 峰值:最大人力值 peak = max(stacked_curve) # 3. 斜率平缓度:相邻时间点变化的绝对值之和(越小越平缓) slope_smoothness = sum(np.abs(np.diff(stacked_curve))) # 4. 总曲线长度:相邻点欧氏距离之和(近似曲线长度) curve_length = sum(np.sqrt(np.diff(stacked_curve)**2 + np.ones(len(stacked_curve)-1)**2)) return { 'total_area': total_area, 'peak': peak, 'slope_smoothness': slope_smoothness, 'curve_length': curve_length }
4. 搜索可行方案并排序
根据约束生成所有可行的启动时间组合,计算每个组合的目标值并排序:
# 原始项目数据 projects = { 'Project_1': [0,25,36,14,2], 'Project_2': [2,78,45,11,1], 'Project_3': [25,16,59,8,0] } total_time = len(projects['Project_1']) # 定义约束下的可行启动时间范围 x1_options = [1] # 项目1的可行启动时间 x2_options = [4] # 项目2固定启动时间 x3_options = x1_options # 项目3与项目1同步 # 生成所有可行组合 valid_combinations = [] for x1, x2, x3 in product(x1_options, x2_options, x3_options): # 平移每个项目的曲线 p1_shifted = shift_curve(projects['Project_1'], x1, total_time) p2_shifted = shift_curve(projects['Project_2'], x2, total_time) p3_shifted = shift_curve(projects['Project_3'], x3, total_time) # 计算堆叠曲线 stacked = np.array(p1_shifted) + np.array(p2_shifted) + np.array(p3_shifted) # 计算目标值 objs = calculate_objectives(stacked) valid_combinations.append({ 'start_times': {'x1':x1, 'x2':x2, 'x3':x3}, 'stacked_curve': stacked, 'curves': {'Project_1':p1_shifted, 'Project_2':p2_shifted, 'Project_3':p3_shifted}, **objs }) # 按不同目标排序取Top3 sorted_by_area = sorted(valid_combinations, key=lambda x: x['total_area'])[:3] sorted_by_peak = sorted(valid_combinations, key=lambda x: x['peak'])[:3] sorted_by_smoothness = sorted(valid_combinations, key=lambda x: x['slope_smoothness'])[:3]
5. 可视化Top3方案
编写函数批量绘制Top3堆叠面积图:
def plot_top_solutions(top_solutions, sort_by): fig, axes = plt.subplots(3, 1, figsize=(10, 15)) for i, sol in enumerate(top_solutions): ax = axes[i] df = pd.DataFrame(sol['curves']) df.plot.area(ax=ax, title=f"Top {i+1} by {sort_by}\n启动时间: {sol['start_times']}") ax.set_xlabel('时间') ax.set_ylabel('人力资源数量') ax.legend(loc='upper right') # 添加目标值标注 ax.text(0.02, 0.95, f"总面积: {sol['total_area']}\n峰值: {sol['peak']}\n平缓度: {sol['slope_smoothness']:.2f}\n曲线长度: {sol['curve_length']:.2f}", transform=ax.transAxes, bbox=dict(facecolor='white', alpha=0.8)) plt.tight_layout() plt.show() # 绘制按总面积排序的Top3 plot_top_solutions(sorted_by_area, '总面积最小') # 绘制按峰值排序的Top3 plot_top_solutions(sorted_by_peak, '峰值最小')
补充说明
- 如果约束允许连续时间或更多可选启动时间,可改用
scipy.optimize.differential_evolution等优化算法代替枚举,提升搜索效率 - 可根据业务需求调整目标函数的权重,或组合多个目标进行多目标优化
- 平移函数可根据实际逻辑调整(比如项目启动后需完整执行曲线,此时总时间轴需延长以容纳完整曲线)
内容的提问来源于stack exchange,提问作者Paul Landrum
相关产品推荐
相关产品推荐

