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
相关产品推荐
相关产品推荐

