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

OptaPlanner:能否为两个变量自定义ChangeMove?

嘿,这个场景我之前做类似排程需求时碰到过,给你详细说说怎么搞!

核心结论

是的,你确实需要分别编写PeriodChangeMove和RoomChangeMove两个独立的ChangeMove类。因为period和room的约束逻辑是完全独立的,分开实现不仅代码更清晰,后续维护、调整约束也更方便,还能避免把不同维度的约束混在一起导致逻辑混乱。

具体编写步骤

1. 编写自定义ChangeMove类

每个类都要继承OptaPlanner提供的AbstractChangeMove,然后重写isMoveDoable方法来加入你的特殊约束检查——这个方法的作用是提前过滤掉那些明显违反约束的无效移动,提升求解效率。

示例1:PeriodChangeMove
public class PeriodChangeMove extends AbstractChangeMove<Lecture, Period> {

    // 构造方法:传入要调整的讲座对象,以及目标时段
    public PeriodChangeMove(Lecture lecture, Period targetPeriod) {
        super(lecture, "period", targetPeriod);
    }

    // 重写isMoveDoable,加入特殊讲座的时段约束
    @Override
    protected boolean isMoveDoable(ScoreDirector<CourseSchedule> scoreDirector) {
        Lecture lecture = getEntity();
        Period targetPeriod = getToValue();
        
        // 示例约束:特殊讲座不能安排在受限时段(比如周末或深夜)
        if (lecture.isSpecialLecture() && targetPeriod.isRestricted()) {
            return false; // 直接拒绝这个移动
        }
        
        // 保留父类的基础检查(比如目标时段和当前时段是否不同)
        return super.isMoveDoable(scoreDirector);
    }
}
示例2:RoomChangeMove
public class RoomChangeMove extends AbstractChangeMove<Lecture, Room> {

    public RoomChangeMove(Lecture lecture, Room targetRoom) {
        super(lecture, "room", targetRoom);
    }

    @Override
    protected boolean isMoveDoable(ScoreDirector<CourseSchedule> scoreDirector) {
        Lecture lecture = getEntity();
        Room targetRoom = getToValue();
        
        // 示例约束:特殊讲座必须安排在带投影设备的房间
        if (lecture.isSpecialLecture() && !targetRoom.hasProjector()) {
            return false;
        }
        
        return super.isMoveDoable(scoreDirector);
    }
}

2. 在Solver配置文件中注册自定义Move

接下来要修改你的Solver配置文件(比如courseScheduleSolverConfig.xml),替换默认的changeMoveSelector为你自定义的两个Move选择器:

<moveSelectors>
    <!-- 注册时段调整的自定义Move -->
    <customMoveSelector>
        <moveClass>com.yourpackage.PeriodChangeMove</moveClass>
        <entitySelector>
            <entityClass>com.yourpackage.Lecture</entityClass>
        </entitySelector>
        <valueSelector>
            <variableName>period</variableName>
        </valueSelector>
    </customMoveSelector>
    
    <!-- 注册房间调整的自定义Move -->
    <customMoveSelector>
        <moveClass>com.yourpackage.RoomChangeMove</moveClass>
        <entitySelector>
            <entityClass>com.yourpackage.Lecture</entityClass>
        </entitySelector>
        <valueSelector>
            <variableName>room</variableName>
        </valueSelector>
    </customMoveSelector>
    
    <!-- 如果你还需要其他移动类型(比如SwapMove),可以继续添加在这里 -->
</moveSelectors>

3. 额外注意事项

  • 确保你的Lecture实体有对应的 getter 方法(比如isSpecialLecture()、getPeriod()),因为Move类需要访问这些属性来做约束检查。
  • 如果你的约束涉及多个实体的关联(比如两个特殊讲座不能在同一时段同一房间),这类全局约束更适合放到ScoreCalculator里,而isMoveDoable主要用来做单实体的快速校验。
  • 如果你用的是OptaPlanner 8.x及以上版本,API细节可能略有不同,但核心思路完全一致:自定义Move类实现约束过滤,再配置到Solver中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:08:24