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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 11:33:13