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

如何在OptaPlanner中动态定义PlanningVariable(如Map形式)

实现带动态PlanningVariable的可复用PlanningEntity

原代码的问题

你的实现思路存在几个核心问题,导致OptaPlanner无法正常工作:

  1. Map不能直接作为PlanningVariable:OptaPlanner需要独立追踪每个规划变量的变化(比如单独修改教室或时间段),而将整个Map作为单个变量会让OptaPlanner只能替换整个Map,无法对单个属性进行调整。
  2. ValueRangeProvider配置不匹配:你的planningVariableMapRange提供的是Map<String, List<Object>>,但OptaPlanner期望每个规划变量的范围是该变量可能取值的集合,而非键值对结构。

正确实现方案:使用程序化领域配置

要实现可复用的动态规划实体,需要借助OptaPlanner的程序化领域配置API(而非静态注解),这样可以在不同项目中动态定义所需的规划变量,同时复用同一个实体类。

1. 定义通用实体类

创建一个通用实体类,用Map存储动态属性:

import java.util.HashMap;
import java.util.Map;

public class GenericPlanningEntity {
    private Long id;
    private Map<String, Object> properties = new HashMap<>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Object getProperty(String key) {
        return properties.get(key);
    }

    public void setProperty(String key, Object value) {
        properties.put(key, value);
    }
}

2. 定义通用解决方案类

同样,创建通用解决方案类,用于存储实体集合和各类问题事实:

import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import java.util.List;

public class GenericPlanningSolution {
    private List<GenericPlanningEntity> entityList;
    // 存储各类问题事实,比如教室、时间段等
    private List<?> roomList;
    private List<?> timeslotList;
    private HardSoftScore score;

    // Getters and Setters
    public List<GenericPlanningEntity> getEntityList() {
        return entityList;
    }

    public void setEntityList(List<GenericPlanningEntity> entityList) {
        this.entityList = entityList;
    }

    public List<?> getRoomList() {
        return roomList;
    }

    public void setRoomList(List<?> roomList) {
        this.roomList = roomList;
    }

    public List<?> getTimeslotList() {
        return timeslotList;
    }

    public void setTimeslotList(List<?> timeslotList) {
        this.timeslotList = timeslotList;
    }

    public HardSoftScore getScore() {
        return score;
    }

    public void setScore(HardSoftScore score) {
        this.score = score;
    }
}

3. 程序化配置领域模型

在每个项目中,通过程序化API动态定义规划变量和值范围:

import org.optaplanner.core.api.config.domain.EntityConfigurer;
import org.optaplanner.core.api.config.domain.DomainConfigurer;
import org.optaplanner.core.api.config.domain.SolutionConfigurer;
import org.optaplanner.core.api.config.solver.SolverConfig;
import org.optaplanner.core.api.solver.SolverFactory;

public class DynamicDomainConfig {
    public static SolverFactory<GenericPlanningSolution> createSolverFactory() {
        // 创建领域配置器
        DomainConfigurer domainConfigurer = SolverConfig.create().domainConfigurer();

        // 配置通用实体类
        EntityConfigurer<GenericPlanningEntity> entityConfigurer = domainConfigurer.entityConfigurer(GenericPlanningEntity.class);
        entityConfigurer.planningId("id"); // 指定ID属性

        // 动态添加规划变量:教室
        entityConfigurer.planningVariableConfigurer("room")
                .valueRangeProviderRefs("roomRange")
                .getter(entity -> entity.getProperty("room"))
                .setter((entity, value) -> entity.setProperty("room", value));

        // 动态添加规划变量:时间段
        entityConfigurer.planningVariableConfigurer("timeslot")
                .valueRangeProviderRefs("timeslotRange")
                .getter(entity -> entity.getProperty("timeslot"))
                .setter((entity, value) -> entity.setProperty("timeslot", value));

        // 配置通用解决方案类
        SolutionConfigurer<GenericPlanningSolution> solutionConfigurer = domainConfigurer.solutionConfigurer(GenericPlanningSolution.class);
        solutionConfigurer.planningEntityCollectionProperty("entityList");
        solutionConfigurer.score(HardSoftScore.class);

        // 配置值范围提供者
        solutionConfigurer.valueRangeProviderConfigurer("roomRange")
                .property("roomList");
        solutionConfigurer.valueRangeProviderConfigurer("timeslotRange")
                .property("timeslotList");

        // 构建SolverFactory
        return SolverFactory.create(domainConfigurer.build());
    }
}

4. 编写项目专属的约束规则

每个项目需要根据自身业务规则编写ConstraintProvider,比如:

import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.optaplanner.core.api.score.stream.Constraint;
import org.optaplanner.core.api.score.stream.ConstraintFactory;
import org.optaplanner.core.api.score.stream.ConstraintProvider;

public class ProjectConstraintProvider implements ConstraintProvider {
    @Override
    public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
        return new Constraint[]{
                // 示例:同一时间同一教室不能有两个课程
                roomConflict(constraintFactory),
                // 其他业务约束...
        };
    }

    private Constraint roomConflict(ConstraintFactory constraintFactory) {
        return constraintFactory.from(GenericPlanningEntity.class)
                .join(GenericPlanningEntity.class,
                        equal(entity -> entity.getProperty("room")),
                        equal(entity -> entity.getProperty("timeslot")),
                        lessThan(GenericPlanningEntity::getId))
                .penalize("Room conflict", HardSoftScore.ONE_HARD);
    }
}

关键说明

  • 灵活性:通过程序化API,你可以在不同项目中定义不同的规划变量(比如添加teacher变量),无需修改通用实体类。
  • 性能:OptaPlanner对程序化配置的支持和注解配置一样高效,不会影响求解性能。
  • 可维护性:通用实体类负责基础属性存储,项目专属配置负责业务相关的变量和约束,职责分离清晰。

内容的提问来源于stack exchange,提问作者Bourne Wang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 05:59:58