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

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后:

  1. 移除可空规划变量,强制所有任务分配到真实或虚拟资源
  2. 新增unusedRealBedPenalty约束,对闲置真实床位设惩罚,推动求解器优先用满真实资源
  3. 虚拟床位惩罚与闲置资源惩罚形成平衡,避免求解器留着真实空位却用虚拟资源

四、通用过约束规划代码模板

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 11:20:31