OptaPlanner中基于列表型规划变量的约束连接实现方法问询
嘿,很高兴能帮你搞定OptaPlanner里列表型规划变量的约束问题!
首先,咱们先确认下那个中间对象模式的核心思路:既然OptaPlanner原生不支持列表型规划变量,咱们就用一个中间关联实体(比如叫LessonRoomAssignment)来把「课程-多房间」的一对多关系拆成多个「课程-单个房间」的一对一关系。每个LessonRoomAssignment实例代表某门课程分配到了某一个房间,这样课程的房间列表就可以通过关联的所有LessonRoomAssignment实例来间接表示。
接下来,咱们就可以基于这个中间实体来构建你需要的约束,实现“展平房间列表、获取所有课程-房间配对”的效果。下面给你两种实用的写法:
方法一:直接从中间实体入手(更简洁)
这种方式直接以LessonRoomAssignment为起点,通过fromUniquePair获取所有不同的分配实例对,再过滤出属于不同课程的配对,最后加入你的业务逻辑:
return factory // 获取所有不同的LessonRoomAssignment配对 .fromUniquePair(LessonRoomAssignment.class) // 过滤掉同一门课程的不同房间分配(我们只关心不同课程的配对) .filter((assignmentA, assignmentB) -> !assignmentA.getLesson().getId().equals(assignmentB.getLesson().getId())) // 避免重复计算(比如课程A-房间1 vs 课程B-房间2,和课程B-房间2 vs 课程A-房间1算同一对) .filter((assignmentA, assignmentB) -> assignmentA.getLesson().getId().compareTo(assignmentB.getLesson().getId()) < 0) // 这里加入你的业务过滤逻辑,比如检查课程时间重叠且房间相同 .filter((assignmentA, assignmentB) -> { Lesson lessonA = assignmentA.getLesson(); Lesson lessonB = assignmentB.getLesson(); // 检查时间是否重叠 boolean timeOverlap = !lessonA.getEndTime().isBefore(lessonB.getStartTime()) && !lessonA.getStartTime().isAfter(lessonB.getEndTime()); // 检查是否是同一个房间(可替换成你的其他条件) boolean sameRoom = assignmentA.getRoom().equals(assignmentB.getRoom()); return timeOverlap && sameRoom; }) // 定义违规惩罚 .penalize("Overlapping lesson in same room", HardSoftScore.ONE_HARD);
方法二:从课程出发,关联中间实体(更直观)
如果你更习惯从课程Lesson开始构建约束,可以通过多次join关联到对应的LessonRoomAssignment,把课程的房间列表“展平”:
return factory // 取第一门课程 .from(Lesson.class, "lessonA") // 关联它的所有房间分配 .join(LessonRoomAssignment.class, "assignmentA", Joiners.equal(Lesson::getId, LessonRoomAssignment::getLessonId)) // 取第二门不同的课程 .join(Lesson.class, "lessonB", Joiners.notEqual("lessonA", "lessonB")) // 关联第二门课程的所有房间分配 .join(LessonRoomAssignment.class, "assignmentB", Joiners.equal((Lesson lessonB) -> lessonB.getId(), LessonRoomAssignment::getLessonId)) // 避免重复配对 .filter((lessonA, assignmentA, lessonB, assignmentB) -> lessonA.getId().compareTo(lessonB.getId()) < 0) // 业务逻辑过滤 .filter((lessonA, assignmentA, lessonB, assignmentB) -> { boolean timeOverlap = !lessonA.getEndTime().isBefore(lessonB.getStartTime()) && !lessonA.getStartTime().isAfter(lessonB.getEndTime()); boolean sameRoom = assignmentA.getRoom().equals(assignmentB.getRoom()); return timeOverlap && sameRoom; }) .penalize("Overlapping lesson in same room", HardSoftScore.ONE_HARD);
几个关键注意点:
- 确保
LessonRoomAssignment是正确的规划实体:加上@PlanningEntity注解,Room字段标记为@PlanningVariable,Lesson字段最好是不可变的引用(比如用@PlanningId标识课程ID)。 - 加入“避免重复配对”的过滤逻辑:用ID的
compareTo来确保每对课程只被计算一次,能有效提升约束的执行性能。 - 这种中间实体模式完全符合OptaPlanner的设计理念,它把复杂的列表型变量拆解成了框架原生支持的单值规划变量,后续的约束和求解逻辑都能顺畅运行。
内容的提问来源于stack exchange,提问作者Justin Phillips
相关产品推荐
相关产品推荐

