
在资源有限的规划问题中,有时会出现需求量超过可用资源容量的情况,这被称为“过约束规划”(overconstrained planning)。例如,医院只有9张病床,却有10名患者需要住院。在这种情况下,我们不能简单地拒绝服务,而是需要一种机制来识别并管理这些超出容量的需求。optaplanner提供了两种主要策略来应对这类问题:将规划变量设为可空(nullable=true)或引入“虚拟值”(virtual values)。
当某些实体无法被分配到资源时,可以将其对应的规划变量设置为null。这种方法适用于以下场景:
业务目标:未分配的实体可以被视为“无法满足的需求”,其处理责任可能转移给其他系统或被直接拒绝。例如,如果一个任务无法被安排,它可能被推迟到下一个规划周期,或者被视为一个失败的请求。
工作原理:
约束处理:
示例代码(概念性):
// 规划实体类:Task
@PlanningEntity
public class Task {
private String id;
// timeslot是规划变量,可以为null
@PlanningVariable(valueRangeProviderRefs = {"timeslotRange"}, nullable = true)
private Timeslot timeslot;
// ... 其他属性和方法
}
// 分数计算器(Drools或ConstraintStreams)
// 假设使用ConstraintStreams
class MyConstraintProvider implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory factory) {
return new Constraint[] {
// 硬约束:例如,一个timeslot不能同时处理两个任务
factory.forEachUniquePair(Task.class,
// 两个任务不能在同一个timeslot且timeslot不为null
Joiners.equal(Task::getTimeslot),
Joiners.filtering((task1, task2) -> task1.getTimeslot() != null))
.penalize("Timeslot冲突", HardSoftScore.ONE_HARD),
// 中等约束:惩罚未分配timeslot的任务
factory.forEach(Task.class)
.filter(task -> task.getTimeslot() == null)
.penalize("未分配任务", HardMediumSoftScore.ONE_MEDIUM) // 使用HardMediumSoftScore
};
}
}在这种模式下,求解器会尽量将任务分配给实际的timeslot,以避免MEDIUM惩罚,但如果无法满足所有硬约束,则允许部分任务被分配到null。
虚拟值是一种更高级的过约束处理机制,它模拟了“额外”或“外部”资源的存在,用于吸收超出实际容量的需求。这适用于以下场景:
业务目标:所有实体都必须得到处理,即使这意味着需要付出额外成本(例如,租用外部设施、雇佣临时工)。未分配的实体被视为“你的问题”,需要找到解决方案。
概念解释:虚拟值不是真正的资源,而是规划域中代表“应急”或“溢出”容量的抽象值。例如,如果医院有9张实际病床,但有10名患者,我们可以创建1个“虚拟病床”,表示第10名患者将被安排到外部合作医院的床位。
工作原理:
约束处理:
如何估算虚拟值数量:
示例代码(概念性):
// Timeslot类需要一个属性来标识是否是虚拟Timeslot
public class Timeslot {
private String id;
private boolean isVirtual; // 标识是否为虚拟timeslot
// ... 其他属性和方法
}
// 规划实体类:Task
@PlanningEntity
public class Task {
private String id;
// timeslot是规划变量,不能为null,因为它必须被分配到真实或虚拟timeslot
@PlanningVariable(valueRangeProviderRefs = {"timeslotRange"}, nullable = false) // 注意这里是false
private Timeslot timeslot;
// ... 其他属性和方法
}
// 规划解决方案类:定义值域提供者
@PlanningSolution
public class MyPlanningSolution {
// ... 其他属性
private List<Timeslot> allTimeslots; // 包含真实和虚拟Timeslot
@ValueRangeProvider(id = "timeslotRange")
public List<Timeslot> getTimeslotRange() {
return allTimeslots;
}
// 假设在初始化时,allTimeslots会被填充
public void initializeTimeslots(List<Timeslot> realTimeslots, int virtualTimeslotCount) {
this.allTimeslots = new ArrayList<>(realTimeslots);
for (int i = 0; i < virtualTimeslotCount; i++) {
Timeslot virtual = new Timeslot("VIRTUAL_" + i, true);
this.allTimeslots.add(virtual);
}
}
// ...
}
// 分数计算器(Drools或ConstraintStreams)
class MyConstraintProvider implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory factory) {
return new Constraint[] {
// 硬约束:一个timeslot(无论是真实还是虚拟)不能同时处理两个任务
factory.forEachUniquePair(Task.class,
Joiners.equal(Task::getTimeslot))
.penalize("Timeslot冲突", HardSoftScore.ONE_HARD),
// 中等约束:惩罚分配到虚拟timeslot的任务
factory.forEach(Task.class)
.filter(task -> task.getTimeslot().isVirtual())
.penalize("使用虚拟Timeslot", HardMediumSoftScore.ONE_MEDIUM)
};
}
}在这种模式下,所有任务都会被分配到一个timeslot,但那些被分配到虚拟timeslot的任务会产生MEDIUM惩罚。由于硬约束仍然适用于虚拟timeslot,求解器必须确保即使使用虚拟资源,所有规则也得到遵守。
在决定使用nullable=true还是虚拟值时,应考虑以下几个关键因素:
OptaPlanner在处理过约束规划时,提供了nullable=true和虚拟值两种有效策略。nullable=true适用于那些可以被“拒绝服务”或转移责任的未分配实体,其核心特点是未分配实体不计入硬软约束。而虚拟值则适用于所有实体都必须得到处理的场景,即使这意味着引入“额外”成本,其关键优势在于即使是虚拟资源,也必须遵守所有规划规则。
在实际应用中,开发者应根据具体的业务需求、对未分配实体的处理方式以及对约束严格性的要求,审慎选择最合适的过约束规划策略。正确地应用这些策略,将有助于构建更健壮、更符合实际业务需求的OptaPlanner解决方案。
以上就是OptaPlanner过约束规划:深入理解虚拟值与空值处理策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号