OptaPlanner过约束规划:virtual values释义、示例及约束代码咨询
OptaPlanner过约束规划:Virtual Values详解与代码示例
一、Virtual Values(虚拟值)概念
Virtual Values是OptaPlanner处理过约束场景的核心手段——当真实资源(比如床位、时段)不足以覆盖所有需求时,用虚拟资源实例承接超额任务,把“未分配”状态转化为“分配给虚拟资源”的合法状态。核心优势:
- 避免规划变量设为可空(可空变量会提升求解复杂度,易引发逻辑漏洞)
- 精准控制约束惩罚(针对虚拟资源分配单独设罚分,而非笼统的“未分配”惩罚)
- 帮助求解器清晰识别资源缺口,优化分配策略
二、Virtual Values实现示例(床位场景)
1. 定义资源实体(真实+虚拟)
扩展床位实体,增加类型标识区分真实与虚拟资源:
@PlanningEntity public class Bed { private Long id; private boolean isVirtual; private LocalDateTime slotTime; // 真实床位的时段信息 // 虚拟床位构造器 public Bed(Long virtualId) { this.id = virtualId; this.isVirtual = true; this.slotTime = null; } // 真实床位构造器 public Bed(Long id, LocalDateTime slotTime) { this.id = id; this.isVirtual = false; this.slotTime = slotTime; } // getter/setter省略 }
2. 调整规划变量
将原可空的timeslot变量改为非空,允许分配真实或虚拟床位:
@PlanningEntity public class PatientAssignment { private Patient patient; // 规划变量不再可空,必须分配一个Bed(真实/虚拟) @PlanningVariable(valueRangeProviderRefs = {"bedRange"}) private Bed assignedBed; // getter/setter省略 }
3. 配置值范围提供者
在规划解决方案类中,合并真实与虚拟床位作为可选范围:
@PlanningSolution public class BedAllocationSolution { @ProblemFactCollectionProperty private List<Bed> realBeds; // 真实床位集合 @ProblemFactCollectionProperty private List<Bed> virtualBeds; // 虚拟床位集合(数量按需设置,比如等于最大预期缺口) @ValueRangeProvider(id = "bedRange") public List<Bed> getAllBeds() { List<Bed> allBeds = new ArrayList<>(realBeds); allBeds.addAll(virtualBeds); return allBeds; } @PlanningEntityCollectionProperty private List<PatientAssignment> patientAssignments; @PlanningScore private HardSoftScore score; // getter/setter省略 }
4. 约束配置(区分真实/虚拟资源)
通过约束流设置不同规则,优先利用真实资源:
public class BedAllocationConstraintProvider implements ConstraintProvider { @Override public Constraint[] defineConstraints(ConstraintFactory constraintFactory) { return new Constraint[] { assignVirtualBedPenalty(constraintFactory), realBedConflict(constraintFactory), unusedRealBedPenalty(constraintFactory) }; } // 分配虚拟床位设中等惩罚 private Constraint assignVirtualBedPenalty(ConstraintFactory constraintFactory) { return constraintFactory.from(PatientAssignment.class) .filter(assignment -> assignment.getAssignedBed().isVirtual()) .penalize("Assign Virtual Bed", HardSoftScore.ONE_SOFT); } // 同一真实床位同时段仅允许分配给一位患者(硬约束) private Constraint realBedConflict(ConstraintFactory constraintFactory) { return constraintFactory.fromUniquePair(PatientAssignment.class, Joiners.equal(PatientAssignment::getAssignedBed), Joiners.filtering((a1, a2) -> !a1.getAssignedBed().isVirtual())) .penalize("Real Bed Conflict", HardSoftScore.ONE_HARD); } // 闲置真实床位设惩罚,推动求解器用满资源 private Constraint unusedRealBedPenalty(ConstraintFactory constraintFactory) { return constraintFactory.from(Bed.class) .filter(bed -> !bed.isVirtual()) .filter(bed -> constraintFactory.from(PatientAssignment.class) .filter(assignment -> assignment.getAssignedBed().equals(bed)) .count() == 0) .penalize("Unused Real Bed", HardSoftScore.ONE_SOFT); } }
三、解决你的问题:未利用Timeslot+惩罚无法清零
你的问题核心是可空变量导致求解器对“未分配”与“闲置资源”的关联逻辑识别模糊。改用Virtual Values后:
- 移除可空规划变量,强制所有任务分配到真实或虚拟资源
- 新增
unusedRealBedPenalty约束,对闲置真实床位设惩罚,推动求解器优先用满真实资源 - 虚拟床位惩罚与闲置资源惩罚形成平衡,避免求解器留着真实空位却用虚拟资源
四、通用过约束规划代码模板
1. 虚拟资源基类
public abstract class AbstractResource { private Long id; private boolean isVirtual; public AbstractResource(Long id, boolean isVirtual) { this.id = id; this.isVirtual = isVirtual; } // getter/setter省略 }
2. 真实资源实现
public class RealResource extends AbstractResource { private LocalDateTime timeSlot; private int capacity; // 资源容量 public RealResource(Long id, LocalDateTime timeSlot, int capacity) { super(id, false); this.timeSlot = timeSlot; this.capacity = capacity; } // getter/setter省略 }
3. 虚拟资源实现
public class VirtualResource extends AbstractResource { public VirtualResource(Long id) { super(id, true); } }
4. 规划实体
@PlanningEntity public class TaskAssignment { private Task task; @PlanningVariable(valueRangeProviderRefs = {"resourceRange"}) private AbstractResource assignedResource; // getter/setter省略 }
5. 核心约束逻辑
public class TaskConstraintProvider implements ConstraintProvider { @Override public Constraint[] defineConstraints(ConstraintFactory constraintFactory) { return new Constraint[] { virtualResourcePenalty(constraintFactory), resourceCapacityExceeded(constraintFactory), unusedRealResourcePenalty(constraintFactory) }; } private Constraint virtualResourcePenalty(ConstraintFactory constraintFactory) { return constraintFactory.from(TaskAssignment.class) .filter(assignment -> assignment.getAssignedResource().isVirtual()) .penalize("Virtual Resource Assignment", HardSoftScore.ONE_SOFT); } // 真实资源容量超限设硬约束 private Constraint resourceCapacityExceeded(ConstraintFactory constraintFactory) { return constraintFactory.from(TaskAssignment.class) .filter(assignment -> !assignment.getAssignedResource().isVirtual()) .groupBy(TaskAssignment::getAssignedResource, count()) .filter((resource, count) -> ((RealResource) resource).getCapacity() < count) .penalize("Resource Capacity Exceeded", HardSoftScore.ONE_HARD, (resource, count) -> count - ((RealResource) resource).getCapacity()); } // 闲置真实资源设惩罚 private Constraint unusedRealResourcePenalty(ConstraintFactory constraintFactory) { return constraintFactory.from(RealResource.class) .filter(resource -> constraintFactory.from(TaskAssignment.class) .filter(assignment -> assignment.getAssignedResource().equals(resource)) .count() == 0) .penalize("Unused Real Resource", HardSoftScore.ONE_SOFT); } }
内容的提问来源于stack exchange,提问作者Pezetter
相关产品推荐
相关产品推荐

