基于JuMP与GLPK的员工调度优化代码报错排查
JuMP+GLPK员工调度优化问题错误修复
问题背景
使用JuMP与GLPK求解员工调度优化问题,目标是确定满足工作量需求的最优员工数量,代码基于数学模型编写,但运行时出现类型转换错误,无法得到结果。
出错代码
using JuMP using GLPK # Constants and parameters Wjk = [[19, 19, 14] [16, 16, 11] [22, 22, 16] [22, 22, 16] [22, 22, 16] [22, 22, 16] [22, 22, 16]] # Workload requirement Ajk = 8 # Some constant value for Ajk M = 395 # Total number of shifts # Create a JuMP model model = Model(GLPK.Optimizer) # Decision variables for shift allocation @variable(model, x[1:N, 1:3, 1:7] >= 0, Int) # Decision variable for shift allocation # Objective function @objective(model, Min, sum(x[i, j, k] for i in 1:N, j in 1:3, k in 1:7)) # Objective function #@objective(model, Min, sum(x[i, j, k] for i in 1:N for j in 1:3 for k in 1:7)) # Constraints for i in 1:N for k in 1:7 @constraint(model, sum(x[i, j, k] for j in 1:3) <= 2) # Constraints (8) - (10) @constraint(model, sum(x[i, j, k]) + sum(x[i, 2, mod(k + 1, 7) + 1] for j in 2:3) <= 2) # Constraint (9) @constraint(model, sum(x[i, 3, k]) + sum(x[i, 1, mod(k + 1, 7) + 1]) + sum(x[i, 2, (mod(k + 1, 7)) + 1]) <= 2) # Constraint (10) end end for j in 1:3 for k in 1:7 @constraint(model, sum(x[i, j, k] for i in 1:N) >= Wjk[k, j]) # Constraint (11) @constraint(model, sum(x[i, j, k] for i in 1:N) <= Wjk[k, j] + Ajk) # Constraint (12) end end for i in 1:N @constraint(model, sum(x[i, j, ((i - 1) % 7) + 1] + x[i, j, i % 7 + 1] for j in 1:3) == 0) # Constraint (13) end for i in 1:N for k in 1:5 @constraint(model, sum(x[i, j, (i + k) % 7 + 1] for j in 1:3) >= 1) # Constraint (14) end end @constraint(model, sum(x[i, j, k] for i in 1:N for j in 1:3 for k in 1:7) == M) # Solve the optimization problem optimize!(model) # Display the results println("Optimal Number of Employees: ", value(N)) println("Optimal Shift Allocation:") for i in 1:value(N) for j in 1:3 for k in 1:7 if value(x[i, j, k]) > 0.5 println("Employee $i, Shift $j on Day $k") end end end end
错误信息
ERROR: MethodError: Cannot `convert` an object of type VariableRef to an object of type Float64 Closest candidates are: convert(::Type{T}, ::Base.TwicePrecision) where T<:Number at twiceprecision.jl:250 convert(::Type{T}, ::AbstractChar) where T<:Number at char.jl:180 convert(::Type{T}, ::CartesianIndex{1}) where T<:Number at multidimensional.jl:136 ... Stacktrace: [1] MathOptInterface.GreaterThan{Float64}(lower::VariableRef) MathOptInterface C:\Users\doriiido\.julia\packages\MathOptInterface\IiXiU\src\sets.jl:171 [2] _moi_constrain_variable(moi_backend::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{GLPK.Optimizer}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, index::MathOptInterface.VariableIndex, info::VariableInfo{VariableRef, Float64, Float64, Float64}, #unused#::Type{Float64}) JuMP C:\Users\doriiido\.julia\packages\JuMP\ToPd2\src\variables.jl:1754 [3] _moi_add_variable(moi_backend::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{GLPK.Optimizer}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, model::Model, v::ScalarVariable{VariableRef, Float64, Float64, Float64}, name::String) JuMP C:\Users\doriiido\.julia\packages\JuMP\ToPd2\src\variables.jl:1737 [4] add_variable(model::Model, v::ScalarVariable{VariableRef, Float64, Float64, Float64}, name::String) JuMP C:\Users\doriiido\.julia\packages\JuMP\ToPd2\src\variables.jl:1726 [5] macro expansion C:\Users\doriiido\.julia\packages\JuMP\ToPd2\src\macros.jl:1213 [inlined] [6] top-level scope Untitled-2:13
错误根源
- N未定义且误用:代码中直接使用
1:N作为数组维度,但从未定义N。JuMP不允许用变量作为数组维度(数组大小必须在建模前确定),导致编译器将N误判为变量引发类型转换错误。 - Wjk维度错误:原定义方式生成1行21列数组,与需求的7天×3班维度不符,后续索引
Wjk[k,j]逻辑错误。 - 目标函数偏差:原目标是最小化总班次,而非实际需求的最小化员工数量。
- 约束逻辑漏洞:部分约束的索引计算存在歧义,且缺少未雇佣员工不可排班的约束。
修复后的代码
using JuMP using GLPK # Constants and parameters # 7天×3班的工作量需求:每行对应1天,每列对应1个班次 Wjk = [ 19 19 14; 16 16 11; 22 22 16; 22 22 16; 22 22 16; 22 22 16; 22 22 16 ] Ajk = 8 # 每班允许的最大超额人数 N_max = 100 # 员工数量的上界(可根据实际情况调整) # 创建JuMP模型 model = Model(GLPK.Optimizer) # 决策变量 # y[i]:是否雇佣第i个员工(1=雇佣,0=不雇佣) @variable(model, y[1:N_max], Bin) # x[i,j,k]:第i个员工在第k天是否排第j个班次(1=排,0=不排) @variable(model, x[1:N_max, 1:3, 1:7], Bin) # 目标函数:最小化雇佣的员工数量 @objective(model, Min, sum(y[i] for i in 1:N_max)) # 约束1:未雇佣的员工不能排班 for i in 1:N_max for j in 1:3 for k in 1:7 @constraint(model, x[i,j,k] <= y[i]) end end end # 约束2:每个员工每天最多排2个班次 for i in 1:N_max for k in 1:7 @constraint(model, sum(x[i,j,k] for j in 1:3) <= 2) end end # 约束3:连续班次限制(原约束9-10) for i in 1:N_max for k in 1:7 next_day = k % 7 + 1 # 当天排2/3班 + 次日排2班 ≤2 @constraint(model, sum(x[i,j,k] for j in 2:3) + x[i,2,next_day] <= 2) # 当天排3班 + 次日排1/2班 ≤2 @constraint(model, x[i,3,k] + sum(x[i,j,next_day] for j in 1:2) <= 2) end end # 约束4:每班每日的员工数量满足工作量需求 for j in 1:3 for k in 1:7 @constraint(model, sum(x[i,j,k] for i in 1:N_max) >= Wjk[k,j]) @constraint(model, sum(x[i,j,k] for i in 1:N_max) <= Wjk[k,j] + Ajk) end end # 约束5:新员工前2天不排班(原约束13) for i in 1:N_max day1 = (i-1) % 7 + 1 day2 = i % 7 + 1 @constraint(model, sum(x[i,j,day1] + x[i,j,day2] for j in 1:3) == 0) end # 约束6:每个员工连续5天至少排1个班(原约束14) for i in 1:N_max for k in 1:5 target_day = (i + k) % 7 + 1 @constraint(model, sum(x[i,j,target_day] for j in 1:3) >= 1) end end # 可选约束:总班次数量固定(根据需求启用) # @constraint(model, sum(x[i,j,k] for i in 1:N_max, j in 1:3, k in 1:7) == 395) # 求解模型 optimize!(model) # 输出结果 optimal_employees = sum(value(y[i]) for i in 1:N_max) println("最优员工数量: ", optimal_employees) println("每班每日分配员工数:") for k in 1:7 for j in 1:3 total = sum(value(x[i,j,k]) for i in 1:N_max) println("第$k天 第$j班: $total 人") end end # 可选:输出详细排班情况 # println("\n详细排班:") # for i in 1:N_max # if value(y[i]) > 0.5 # println("员工$i:") # for k in 1:7 # shifts = [j for j in 1:3 if value(x[i,j,k]) > 0.5] # if !isempty(shifts) # println(" 第$k天: 班次$(join(shifts, ","))") # end # end # end # end
修复说明
- 员工数量处理:通过设定员工数量上界
N_max,引入二进制变量y[i]标记是否雇佣,既规避了变量作为数组维度的问题,又实现了最小化员工数量的目标。 - 维度修正:将Wjk调整为7行3列矩阵,符合7天×3班的索引逻辑。
- 约束完善:添加未雇佣员工不可排班的约束,修正连续班次限制的索引计算,保留原有的工作量、新员工排班限制等约束。
- 结果输出:直接计算并输出每班每日的员工总数,匹配需求。
内容的提问来源于stack exchange,提问作者Dddd97
相关产品推荐
相关产品推荐

