OptaPlanner 入门:课程表规划(上)

515 阅读6分钟

本文基于 OptaPlanner 官方文档的 2.2. Hello world Java quick start,根据实际情况做出了修改。新手勿喷。

是什么?

OptaPlanner 是一个轻量级、可嵌入的规划引擎 / AI 约束求解器。

OptaPlanner 使用 java 语言开发,它在内部封装了各种 AI 优化算法(如禁忌搜索,模拟退火等),可让开发者透过其 API 即可求解 NPC / NP-Hard 问题。

什么是P问题、NP问题和NPC问题,下面是本人总结出的:

NP 问题

  • 无法用确定的算法直接求解(确定的算法如:y=2x+1),但对于在一定时间内得出的解(如通过遍历或猜测取得),是可进行验证的。

NPC 问题

  • 不但无法用确定的算法直接求解,对所得的解,也没有一个确定的办法去验证的问题。1. NPC 问题也是一个 NP 问题,2. 所有的NP问题都可以约化到它(约化具备传递性,NPC 约化为-> NP 约化为-> 它)。
  • 官方描述:在合理的时间内验证给定的问题解决方案很容易。

NP-Hard 问题

  • 不一定是 NP 问题,但所有的 NP 问题都可以约化到它。
  • 官方描述:没有灵丹妙药可以在合理的时间内找到问题的最佳解决方案(目前)。

要干什么?

通过本教程,你将能创建一张符合要求(约束)的课程表。类似这样:

               房间A          房间B          房间C         
|------------|------------|------------|------------|
  MON 08:30    语文           化学           英语          
               张 老师         里 老师         钟 老师        
               124 班         
|------------|------------|------------|------------|
  MON 09:30    数学           生物           物理          
               李 老师         达 老师         牛 老师        
               124 班         
|------------|------------|------------|------------|
  MON 10:30    英语           语文           化学          
               钟 老师         张 老师         里 老师        
               134 班         
|------------|------------|------------|------------|
  MON 13:30    物理           数学           生物          
               牛 老师         李 老师         达 老师        
               134 班         
|------------|------------|------------|------------|
  MON 14:30    化学           英语           语文          
               里 老师         钟 老师         张 老师        
               135 班         
|------------|------------|------------|------------|
  TUE 08:30    生物           物理           数学          
               达 老师         牛 老师         李 老师        
               135 班         
|------------|------------|------------|------------|
  TUE 09:30    语文           化学           英语          
               张 老师         里 老师         钟 老师        
               235 班         
|------------|------------|------------|------------|
  TUE 10:30    数学           生物           物理          
               李 老师         达 老师         牛 老师        
               235 班         
|------------|------------|------------|------------|
  TUE 13:30    英语           语文           化学          
               钟 老师         张 老师         里 老师        
               245 班         
|------------|------------|------------|------------|
  TUE 14:30    物理           数学           生物          
               牛 老师         李 老师         达 老师        
               245 班         
|------------|------------|------------|------------|

进程已结束,退出代码0

课程表规划问题是 NP-Hard 问题,即没有灵丹妙药可以在合理的时间内找到问题的最佳解决方案

OptaPlanner 等 AI 约束求解器拥有先进的算法,可以在合理的时间内提供接近最优的解决方案。

我们将会学习到 OptaPlanner 的几个基本概念:

约束问题事实规划实体规划变量解决方案

确定需求(约束)

根据一般情况,我们有以下需求:

  1. 一个房间最多可以同时上一节课
  2. 一位老师最多可以同时教一堂课
  3. 一个班级最多可以同时上一堂课
  4. 老师更喜欢在同一个房间里教授所有课程
  5. 老师喜欢连续上课,不喜欢课程之间的空隙(连堂)
  6. 学生不喜欢连续上同样课程的课

在 OptaPlanner 中,上面的需求被称为 约束,可分为 硬约束软约束

  • 硬约束:OptaPlanner 在规划的过程中必须遵守,如【1-3】条
  • 软约束:可不必遵守,遵守了更好,如【4-6】条

建立模型

我们需要根据需求创建三个实体模型。

我们需要为每节课分配到合适的房间和时间段。

在 OPtaPlanner 上有 问题事实规划实体规划变量 概念。

& 对于课程时间段

  • 如:

    • MON 08:30 - 09:30,星期一 八点半 到 九点半
    • TUE 10:30 - 11:30,星期二 十点半 到 十一点半
  • 在 OPtaPlanner 求解前,课程时间段需要用户输入;在 OPtaPlanner 求解时,各个时间段实例没有发生变化,此即为 问题事实

& 对于房间:

  • 如:

    • 房间A
    • 房间B
  • 在 OPtaPlanner 求解前,房间需要用户输入;在 OPtaPlanner 求解时,各个房间实例没有发生变化,此即为 问题事实

& 对于课程

  • 如:

    • {id=0, subject='语文', teacher='张 老师', studentGroup='1 班', timeslot=MONDAY 08:30, room=房间A}
    • {id=1, subject='数学', teacher='李 老师', studentGroup='1 班', timeslot=MONDAY 09:30, room=房间A}
  • 在 OPtaPlanner 求解时,会更改课程的 时间段 字段和 房间 字段,这样一节课就确定规划下来了,这些更改的字段叫 规划变量(图中橙色部分)。因为 OptaPlanner 更改了课程的字段值,所以课程是一个 规划实体(图中绿色部分)。

定义约束(代码)并计算得分

OptaPlanner 在求解规则问题的过程中会产生很多的解(即解决方案)。

  • 可能解:一个规划问题的任意一个解都称为可能解
  • 可行解:可行解就是那些完全符合硬约束的解
  • 相对最优解:在约定的时间内(如2分钟内)OptaPlanner 能找到的最好的解。

OptaPlanner 是如何找到的相对最优解的?需要我们根据需求编写约束代码,对找到的解进行打分。

接下来我们将阅读部分示例代码,无需担心,已帮忙探路(其实就是注释翻译得尽量详细点),下一篇会详细讲如何搭建项目,大概看一下就行。

首先是 TimeTableConstraintProvider 类,它负责告诉 OptaPlanner 有哪些 硬约束软约束

//课程表约束提供者
public class TimeTableConstraintProvider implements ConstraintProvider {
    //  定义约束条件
    @Override
    public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
        return new Constraint[] {
                // 硬约束
                // 一个房间最多可以同时上一节课
                roomConflict(constraintFactory),
                // 一位老师最多可以同时教一堂课
                teacherConflict(constraintFactory),
                // 一个班级最多可以同时上一堂课
                studentGroupConflict(constraintFactory),
                // -----
                // 软约束
                // 老师更喜欢在同一个房间里教授所有课程
                teacherRoomStability(constraintFactory),
                // 老师喜欢连续上课,不喜欢课程之间的空隙(连堂)
                teacherTimeEfficiency(constraintFactory),
                // 学生不喜欢连续上同样课程的课
                studentGroupSubjectVariety(constraintFactory)
        };
    }
//  ...
}

我们阅读其中的第一个硬约束 roomConflict(constraintFact)

//  关于房间的硬约束
    Constraint roomConflict(ConstraintFactory constraintFactory) {
        // 一个房间在同一时间最多只能容纳一个课程。
        return constraintFactory
//                选择2个不同的课程配对
                .forEachUniquePair(Lesson.class,
//                        1. 在同一时间段
                        Joiners.equal(Lesson::getTimeslot),
//                        2. 在同一房间
                        Joiners.equal(Lesson::getRoom))
//                对每一对这种情况进行沉重的惩罚
                .penalize("Room conflict", HardSoftScore.ONE_HARD);
    }

如果存在 两个 1. 在同一时间段 和 2. 在同一房间 的不同课程,那么将进行沉重的(HardSoftScore.ONE_HARD)惩罚(penalize)。这表明是一个硬约束(ONE_HARD),必须不能出现。

除了惩罚, OptaPlanner 还有 奖励(reward)。

// ...
.reward("Teacher time efficiency", HardSoftScore.ONE_SOFT);

这句代码表示,这是一个软约束(ONE_SOFT),当出现时会给予奖励(reward)。

// ...
.penalize("Teacher time efficiency", HardSoftScore.ONE_SOFT);

这句代码表示,这是一个软约束(ONE_SOFT),当出现时会给予“适当”的惩罚(penalize)。


不知不觉写了快 2000 字了,本篇主要是讲 OptaPlanner 的一些概念,一来巩固本人的学习成果,二来想为 OptaPlanner 社区尽微薄之力。希望能帮助到和我一样刚入门的新人朋友们。

下一篇将会从零到一,搭建课程表规划的示例项目。