使用SciPy Optimize优化工资/工人数量的报错解决咨询
问题解决与排班优化实现
错误原因分析
range(min_workers)报错:min_workers是numpy数组,而range()要求输入整数标量,直接传入数组会触发类型错误。- 约束定义错误:
scipy.optimize.minimize的constraints参数需要是由约束字典组成的序列,而非简单的元组。 - 变量逻辑偏差:你的变量定义方向错误,排班优化的决策变量应该是每个班次的雇佣人数,而非时间段的比例值。
正确实现思路
这是典型的线性规划问题,核心要素如下:
- 决策变量:设
x1, x2, x3, x4分别为Shift 1到Shift 4的雇佣工人数量 - 约束条件:每个时间段内,所有覆盖该时段的班次工人总数 ≥ 该时段的需求人数
- 目标函数:可选择三种优化方向:
- 最小化总工资:
135x1 + 140x2 + 190x3 + 188x4 - 最小化总雇佣人数:
x1 + x2 + x3 + x4 - 两者加权组合:比如
0.7*总工资 + 0.3*总人数(权重可按需调整)
- 最小化总工资:
完整代码实现
import pandas as pd import numpy as np import scipy.optimize as sco from io import StringIO # 加载数据 with StringIO( '''Time Windows,Shift 1,Shift 2,Shift 3,Shift 4,Workers Required 6:00 - 9:00, 1, 0, 0, 1, 55.0 9:00 - 12:00, 1, 0, 0, 0, 46.0 12:00 - 15:00, 1, 1, 0, 0, 59.0 15:00 - 18:00, 0, 1, 0, 0, 23.0 18:00 - 21:00, 0, 1, 1, 0, 60.0 21:00 - 24:00, 0, 0, 1, 0, 38.0 24:00 - 3:00, 0, 0, 1, 1, 20.0 3:00 - 6:00, 0, 0, 0, 1, 30.0 Wage_Rate, 135, 140, 190, 188, 0.0''') as f: df = pd.read_csv(f, skipinitialspace=True, index_col='Time Windows') # 提取关键数据 is_shift = df.columns.str.startswith('Shift') is_wage = df.index == 'Wage_Rate' shift_matrix = df.loc[~is_wage, is_shift].values # 8行4列的0-1矩阵,标记班次是否覆盖时段 wage_rates = df.loc[is_wage, is_shift].values[0] # 四个班次的时薪 worker_requirements = df.loc[~is_wage, 'Workers Required'].values # 各时段需求人数 # 1. 定义目标函数:最小化总工资 def objective_total_wage(x): return np.dot(x, wage_rates) # 2. 定义目标函数:最小化总人数 def objective_total_workers(x): return np.sum(x) # 3. 定义目标函数:加权组合示例(70%工资权重,30%人数权重) def objective_weighted(x): wage_weight = 0.7 worker_weight = 0.3 # 归一化处理,避免量纲影响 normalized_wage = np.dot(x, wage_rates) / np.max(wage_rates * np.max(worker_requirements)) normalized_workers = np.sum(x) / np.sum(worker_requirements) return wage_weight * normalized_wage + worker_weight * normalized_workers # 约束条件:每个时段的在岗人数≥需求人数 constraints = [ {'type': 'ineq', 'fun': lambda x: np.dot(shift_matrix[i], x) - worker_requirements[i]} for i in range(len(worker_requirements)) ] # 变量边界:每个班次的人数≥0,且为整数(注:scipy的SLSQP不支持整数变量,若需整数解建议用PuLP) bounds = [(0, None) for _ in range(len(wage_rates))] # 初始猜测值 x0 = np.ones(len(wage_rates)) * 20 # 选择目标函数进行优化,这里以最小化总工资为例 result = sco.minimize( fun=objective_total_wage, x0=x0, method='SLSQP', bounds=bounds, constraints=constraints ) # 输出结果 print("优化状态:", result.message) print("最优班次人数:", result.x.round(0)) # 取整作为实际排班参考 print("最小总工资:", result.fun)
补充说明
- 若需要整数解(工人数量必须是整数),SciPy的优化器支持有限,建议使用你之前学习的PuLP,它专门处理整数线性规划问题,更适合排班场景。
- 加权组合目标中,归一化步骤是为了避免工资和人数的量纲差异导致权重失效,可根据实际需求调整权重或移除归一化。
内容的提问来源于stack exchange,提问作者ASH
相关产品推荐
相关产品推荐

