Kotlin ojAlgo线性优化-防止轮班重叠?
我对线性优化有点陌生,我想把它应用到经典的调度问题上。对于人员配置问题,我不太清楚如何声明捕获“转移”概念的函数 我用的是ojAlgo,到目前为止它非常棒。这是我人为设计的小问题:Kotlin ojAlgo线性优化-防止轮班重叠?,kotlin,mathematical-optimization,linear-programming,ojalgo,Kotlin,Mathematical Optimization,Linear Programming,Ojalgo,我对线性优化有点陌生,我想把它应用到经典的调度问题上。对于人员配置问题,我不太清楚如何声明捕获“转移”概念的函数 我用的是ojAlgo,到目前为止它非常棒。这是我人为设计的小问题: SCENARIO: You have three drivers to make deliveries. Driver 1 costs $10 / hr Driver 2 costs $12 / hr Driver 3 costs $14 / hr Each driver can only work 3-6 h
SCENARIO:
You have three drivers to make deliveries.
Driver 1 costs $10 / hr
Driver 2 costs $12 / hr
Driver 3 costs $14 / hr
Each driver can only work 3-6 hours a day.
Only one shift can be worked by a worker a day.
Operating day is 6:00 to 22:00, which must be fully covered.
Driver 2 cannot work after 11:00.
Create a schedule that minimizes the cost.
Solve Variables:
Tsx = shift start for Driver X
Tex = shift end for Driver X
Minimize:
10(Te1 - Ts1) + 12(Te2 - Ts2) + 14(Te3 - Ts3)
10Te1 - 10Te2 + 12Te2 - 12Ts2 + 14Te3 - 14Ts3
Constraints:
4.0 <= Te - Ts <= 6.0
6.0 <= Ts, Te <= 22.0
(Te1 - Ts1) + (Te2 - Ts2) + (Te3 - Ts3) = (22.0 - 6.0)
Te2 <= 11
但我得到的结果表明,所有三个司机都被分配在早上6点同时工作。司机1从6:00-11:00,司机2从6:00-12:00,司机3从6:00-11:00
OPTIMAL 624.0 @ [6.0, 11.0, 6.0, 12.0, 6.0, 11.0]
我不想让它们重叠。我希望一次只分配一名驾驶员,并且我希望覆盖整个工作日。我该如何表达被占用时间的二进制状态呢?多亏了你,我似乎已经启动并运行了这个程序。钥匙是一个二进制开关 结果如下。驱动程序1安排在16:00-22:00,驱动程序2安排在6:00-10:00,驱动程序3安排在10:00-16:00
import org.ojalgo.optimisation.ExpressionsBasedModel
import org.ojalgo.optimisation.Variable
// declare model
val model = ExpressionsBasedModel()
// parameters
val operatingDay = 6..22
val operatingDayLength = operatingDay.endInclusive - operatingDay.start
val allowableShiftSize = 4..6
// Map drivers by their ID for ad hoc retrieval
val drivers = sequenceOf(
Driver(driverNumber = 1, rate = 10.0),
Driver(driverNumber = 2, rate = 12.0, availability = 6..11),
Driver(driverNumber = 3, rate = 14.0)
).map { it.driverNumber to it }
.toMap()
fun main(args: Array<String>) {
drivers.values.forEach { it.addToModel() }
val result = model.minimise()
println(result)
}
// Driver class will put itself into the Model
data class Driver(val driverNumber: Int,
val rate: Double,
val availability: IntRange? = null) {
val shiftStart = Variable.make("${driverNumber}shiftStart").weight(rate).lower(6).upper(22).apply(model::addVariable)
val shiftEnd = Variable.make("${driverNumber}shiftEnd").weight(rate).lower(6).upper(22).apply(model::addVariable)
fun addToModel() {
//constrain shift length
model.addExpression("${driverNumber}shiftLength")
.lower(allowableShiftSize.start)
.upper(allowableShiftSize.endInclusive)
.set(shiftEnd, 1)
.set(shiftStart, -1)
//ensure coverage of entire day
model.addExpression("EnsureCoverage")
.level(operatingDayLength)
.apply {
drivers.values.forEach {
set(it.shiftEnd, 1)
set(it.shiftStart, -1)
}
}
//add specific driver availability
availability?.let {
model.addExpression("${driverNumber}StartAvailability")
.lower(it.start)
.upper(it.endInclusive)
.set(shiftStart, 1)
model.addExpression("${driverNumber}EndAvailability")
.lower(it.start)
.upper(it.endInclusive)
.set(shiftEnd, 1)
}
//prevent shift overlap
drivers.values.asSequence()
.filter { it != this }
.forEach { otherDriver ->
val occupied = Variable.make("${driverNumber}occupyStatus").lower(0).upper(1).integer(true).apply(model::addVariable)
model.addExpression("${driverNumber}to${otherDriver.driverNumber}Binary1")
.upper(0)
.set(otherDriver.shiftEnd, 1)
.set(occupied, operatingDayLength * - 1)
.set(shiftStart, -1)
model.addExpression("${driverNumber}to${otherDriver.driverNumber}Binary2")
.upper(operatingDayLength)
.set(shiftEnd, 1)
.set(occupied, operatingDayLength)
.set(otherDriver.shiftStart, -1)
}
}
}
下面是一个类似问题的示例]。它与其他公式和方法有联系。这是一个建模问题,并不是ojAlgo所特有的。如果你在谷歌上搜索日程安排和/或作业问题,你会发现有很多东西值得一读。
import org.ojalgo.optimisation.ExpressionsBasedModel
import org.ojalgo.optimisation.Variable
// declare model
val model = ExpressionsBasedModel()
// parameters
val operatingDay = 6..22
val operatingDayLength = operatingDay.endInclusive - operatingDay.start
val allowableShiftSize = 4..6
// Map drivers by their ID for ad hoc retrieval
val drivers = sequenceOf(
Driver(driverNumber = 1, rate = 10.0),
Driver(driverNumber = 2, rate = 12.0, availability = 6..11),
Driver(driverNumber = 3, rate = 14.0)
).map { it.driverNumber to it }
.toMap()
fun main(args: Array<String>) {
drivers.values.forEach { it.addToModel() }
val result = model.minimise()
println(result)
}
// Driver class will put itself into the Model
data class Driver(val driverNumber: Int,
val rate: Double,
val availability: IntRange? = null) {
val shiftStart = Variable.make("${driverNumber}shiftStart").weight(rate).lower(6).upper(22).apply(model::addVariable)
val shiftEnd = Variable.make("${driverNumber}shiftEnd").weight(rate).lower(6).upper(22).apply(model::addVariable)
fun addToModel() {
//constrain shift length
model.addExpression("${driverNumber}shiftLength")
.lower(allowableShiftSize.start)
.upper(allowableShiftSize.endInclusive)
.set(shiftEnd, 1)
.set(shiftStart, -1)
//ensure coverage of entire day
model.addExpression("EnsureCoverage")
.level(operatingDayLength)
.apply {
drivers.values.forEach {
set(it.shiftEnd, 1)
set(it.shiftStart, -1)
}
}
//add specific driver availability
availability?.let {
model.addExpression("${driverNumber}StartAvailability")
.lower(it.start)
.upper(it.endInclusive)
.set(shiftStart, 1)
model.addExpression("${driverNumber}EndAvailability")
.lower(it.start)
.upper(it.endInclusive)
.set(shiftEnd, 1)
}
//prevent shift overlap
drivers.values.asSequence()
.filter { it != this }
.forEach { otherDriver ->
val occupied = Variable.make("${driverNumber}occupyStatus").lower(0).upper(1).integer(true).apply(model::addVariable)
model.addExpression("${driverNumber}to${otherDriver.driverNumber}Binary1")
.upper(0)
.set(otherDriver.shiftEnd, 1)
.set(occupied, operatingDayLength * - 1)
.set(shiftStart, -1)
model.addExpression("${driverNumber}to${otherDriver.driverNumber}Binary2")
.upper(operatingDayLength)
.set(shiftEnd, 1)
.set(occupied, operatingDayLength)
.set(otherDriver.shiftStart, -1)
}
}
}
OPTIMAL 936.0 @ [16.0, 22.0, 6.0, 10.0, 10.0, 16.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0]