You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SciPy Optimize优化工资/工人数量的报错解决咨询

问题解决与排班优化实现

错误原因分析

  1. range(min_workers)报错:min_workers是numpy数组,而range()要求输入整数标量,直接传入数组会触发类型错误。
  2. 约束定义错误:scipy.optimize.minimize的constraints参数需要是由约束字典组成的序列,而非简单的元组。
  3. 变量逻辑偏差:你的变量定义方向错误,排班优化的决策变量应该是每个班次的雇佣人数,而非时间段的比例值。

正确实现思路

这是典型的线性规划问题,核心要素如下:

  • 决策变量:设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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:55:06