constraints - 使用 OptaPlanner 创建具有一些棘手约束的学校时间表
问题描述
我将使用 OptaPlanner 为学校安排时间表。
我们正在制定整个学期的时间表,如有必要,每周可能会略有不同。
有一些棘手的限制需要考虑:
1. 每周时间表
- 一个学科的讲座应该在整个学期中均匀分布。
- 例如,我们不能在第一周上 20 节数学课,然后在本学期“完成”数学。
- 事实上,每周有一些可预测性很好
- “理科二年级的星期二早上有生物课”
- 然而,这种限制不能一成不变。有些周必须包括工作经验课程、体育游览等,在这种情况下,它们必须与其他周有所不同。
问题
如果我创建一个约束条件,即-1soft
不安排与前一周相同的时间,那么 OptaPlanner 将浪费大量时间,然后“意外”找到合适的讲座位置,即使它设法收敛因此,每个主题每周都安排在相同的时间,它永远无法通过一个一个地移动它们来移动整个系列的讲座。(那个局部最优值永远不会被逃避。)
2.跨学生组科目
- 学生群体和课程之间有很大的相关性;例如,科学二年级的所有学生大多阅读相同的课程:科学二年级的化学,科学二年级的生物学,......
- 语言课程是个例外。
- 每个学生可以选择学习法语、德语或西班牙语。因此,二年级的西班牙语由科学二年级学生和社会研究二年级学生等组成。
- 从以往(人工)排课的经验来看,几乎可以保证在同一时间段内安排所有语言课程的最优方案。(如果法语安排在星期四的 9 点,那么德语和西班牙语可以安排在星期四的 9 点“免费”。)
问题
一个学期有很多时间段,OptaPlanner 会通过随机移动单个讲座来发现同时安排所有语言讲座的解决方案的可能性很小。
此外,与问题 1 类似:如果OptaPlanner 确实设法同时安排法语、德语和西班牙语,这些“块”将永远不会移动到其他地方,因为它们是单独的讲座,并且所有讲座都“随机”移动的机会到同一个新插槽很小。即使有很大的禁忌历史长度等等。
到目前为止我的想法
至于问题 1(“每周可预测性”),我正在考虑执行以下操作:
在整个学期计划的构建阶段,我创建了问题的简化版本,将(一组减少的讲座)安排到一个“模板周”中。我们称之为“单周预调度”。然后在整个学期的初始解决方案的构建中重复这个模板周,这是“真正的”计划实体。
然后,本地搜索步骤将只专注于插入 PE 游览等,并调整受影响周的时间表。
至于问题 2,我认为问题 1 的解决方案可能会解决这个问题。在 1 周的日程安排中,假设 OptaPlaner 会意识到应该同时安排语言课程似乎是合理的。
关于通过单周预调度解决的局部最优(“生物学安排在星期二早上”),我想我可以创建一个自定义移动操作,将这些讲座“捆绑”成一个移动。我不知道这有多简单。我真的很想让代码尽可能简单。
问题
我的想法合理吗?有没有更聪明的方法来解决这些问题?如果无论如何我必须创建自定义动作,也许我不需要构建模板周?
有没有办法为动作分配提示或权重?如果是这样,我也许可以生成权重稍大的动作,以调整日程安排以遵守可预测的周数和在同一时间段安排的语言。
解决方案
一个问题问得好!
关于你的第一个问题,我建议你看看OptaWeb Employee Rostering和轮换的概念。轮换是“一般情况下的情况”,然后 Planner 可以自由地偏离轮换,但会受到惩罚。一旦您从 UI 中了解了轮换的概念,请查看规划实体Shift
以及如何使用employee
和rotationEmployee
变量来实现轮换。请注意,只有employee
是实际@PlanningVariable
的,并且rotationEmployee
是固定的。
这意味着您必须手动定义旋转,因此您需要自己完成求解器的工作。然而,由于我假设这个操作每学期只进行一次,也许解决方案可能是让一个更简单的求解器首先生成一个合理的一般旋转,然后第二个求解器会接受它并找出具体的必要调整?
关于你的第二个问题,轮换也有帮助。但我在想也许一些移动过滤和自定义移动来帮助 OptaPlanner 移动所有语言类,或者不移动?编写高效的自定义动作并不容易,过滤库存动作很麻烦。所以我只会在其他选项的潜力用尽时才这样做。如果您最终这样做,请查找MoveIteratorFactory
.
我的回答有点含糊,因为我们没有深入了解领域模型的细节,但出于设计整体解决方案的目的,它希望能提供足够的线索。
推荐阅读
- javascript - Angular errorHandler vs window.onError 全局错误处理
- php - 如何使用 codeigniter 获取项目文件夹基本路径?
- javascript - 如何在 Google 表格中制作一个按钮,连续触发两个不同的功能
- javascript - 使用 css 或 js 以一些倾斜角度或椭圆形状旋转对象 360 度
- spring-boot - Sprinboot:尝试将对象传递给我的模型属性时出现 java.lang.StackOverflowError
- python - Reporlab 使用 PDFImage 类更改 hAline
- android - 如何在日历意图中插入多天?
- c# - 展开可为空的
在 IValueConverter 中获取 T - java - 无法从 Scaleset 获取虚拟机列表
- powershell - 为什么powershell会收集未捕获的值并将其作为数组返回