使用 Optapy 的学校时间表 - 允许从列表中选择老师上课

School timetabling with Optapy - allow to choose teacher from a list for a lesson

我正在尝试以学校时间表示例为基础使用 Optapy。 对于任何给定的科目,我都有一份可能教授该科目的教师名单,每位教师可能会教授 1 到 3 门科目。我想优化课程的教师分配,这样,每个学生组在任何给定科目上只能有一名老师,并且每个学生组在任何科目上都应该很少上课(即给定学生组 A 的 4 次数学课由老师 B)

为此,我尝试添加这个问题事实:

@problem_fact
class Teacher:
    def __init__(self, id, name, subject, subject2, subject3):
        self.id = id
        self.name = name
        self.subject = subject
        self.subject2 = subject2
        self.subject3 = subject3

    @planning_id
    def get_id(self):
        return self.id

    def __str__(self):
        return (
                f"Teacher("
                f"id={self.id}, "
                f"name={self.name}, "
                f"subject={self.subject}, "
                f"subject2={self.subject2}, "
                f"subject3={self.subject3})"
        )

并且我向规划实体添加了规划变量:

@planning_variable(Teacher, ["teacherRange"])
def get_teacher(self):
    return self.teacher

def set_teacher(self, new_teacher):
    self.teacher = new_teacher

但我不知道如何继续制作从列表中选择一位老师的算法,以及如何为任何科目的一个学生组添加一位老师的约束。有帮助吗?

为了使算法从列表中选择老师,必须从 @planning_solution class 上的方法返回列表,并用适当的 @value_range_provider 注释:

@planning_solution
class TimeTable:
    def __init__(self, teacher_list, ..., score=None):
        self.teacher_list = teacher_list
        # ...
        self.score = score

    @problem_fact_collection_property(Teacher)
    @value_range_provider("teacherRange")
    def get_teacher_list(self):
        return self.teacher_list
    # rest of the class

对于“任何科目的一个学生组的一位老师”的约束,我将其改写为“学生组的科目一致的老师”(即,如果一节课有相同的学生组和科目,它也必须有同一个老师)。应该这样写:

def consistent_teacher_for_subject_and_student_group(constraint_factory: ConstraintFactory):
    return constraint_factory \
        .forEach(LessonClass) \
        .join(LessonClass,
              [
                  # ... the same student group ...
                  Joiners.equal(lambda lesson: lesson.student_group),
                  # ... the same subject ...
                  Joiners.equal(lambda lesson: lesson.subject),
                  # form unique pairs
                  Joiners.lessThan(lambda lesson: lesson.id)
              ]) \
        .filter(lambda lesson1, lesson2: lesson1.teacher != lesson2.teacher) \
        .penalize("Different teacher for the same student group and subject", HardSoftScore.ONE_HARD)

对于“teacher must be able to teach subject”这一约束条件,您可以使用过滤器:

def teacher_must_be_able_to_teach_subject(constraint_factory: ConstraintFactory):
    return constraint_factory \
        .forEach(LessonClass) \
        .filter(lambda lesson: lesson.subject not in {lesson.teacher.subject, lesson.teacher.subject2, lesson.teacher.subject3}) \
        .penalize("Teacher cannot teach subject", HardSoftScore.ONE_HARD)