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

如何为Timefold排程示例添加教师休息日约束?

实现Timefold中“特定教师仅在特定日期工作”的约束

问题背景

我在扩展Timefold的Java“Hello World”示例时,添加了自定义的Lesson、Class和Teacher数据,想要实现“特定教师仅能在特定日期工作”的约束,但编写的约束代码报错。

错误信息:

The method forEachUniquePair(Class, BiJoiner<A,A>, BiJoiner<A,A>) in the type ConstraintFactory is not applicable for the arguments (Class, BiJoiner<Lesson,Lesson>, BiJoiner<Teacher,Teacher>)

我的约束代码:

Constraint restDayConstraint(ConstraintFactory constraintFactory) {
        // teachers don't teach on their rest day
        return constraintFactory
                .forEachUniquePair(Lesson.class, Teacher.class,
                        Joiners.equal(Lesson::getTimeslot),
                        Joiners.equal(Teacher::getTeacher))
                .filter((lesson1, teacher) -> lesson1.getTimeslot().getDayOfWeek() != teacher.getRestDay())
                .penalize(HardSoftScore.ONE_HARD)
                .justifyWith(
                        (lesson1, teacher, score) -> new TeacherConflictJustification(lesson1.getTeacher(), lesson1, teacher))
                .asConstraint("Teacher conflict");
    }

Teacher类代码:

package org.acme.schooltimetabling.domain;

import ai.timefold.solver.core.api.domain.entity.PlanningEntity;
import ai.timefold.solver.core.api.domain.lookup.PlanningId;
import ai.timefold.solver.core.api.domain.variable.PlanningVariable;

@PlanningEntity
public class Teacher {

    @PlanningId
    private Long id;

    private String subject;
    private String restDay;

    @PlanningVariable
    private Timeslot timeslot;

    @PlanningVariable
    private Room room;

    // No-arg constructor required for Timefold
    public Teacher() {
    }

    public Teacher(long id, String subject, String restDay) {
        this.id = id;
        this.subject = subject;
        this.restDay = restDay;
    }

    @Override
    public String toString() {
        return subject + "(" + id + ")";
    }

    // ************************************************************************
    // Getters and setters
    // ************************************************************************

    public Long getId() {
        return id;
    }

    public String getRestDay() {
        return restDay;
    }

    public String getSubject() {
        return subject;
    }

}

错误原因

  1. forEachUniquePair方法使用错误:该方法仅用于同一类型的两个实体配对约束,你传入了Lesson.class和Teacher.class两种不同类型,导致方法签名不匹配。
  2. Teacher类定位错误:在课程表场景中,Teacher是固定的问题事实(ProblemFact),不是需要被分配的规划实体(PlanningEntity),不应该添加@PlanningEntity和@PlanningVariable注解。

修正方案

第一步:修正Teacher类定义

将Teacher改为问题事实,移除规划实体相关注解:

package org.acme.schooltimetabling.domain;

import ai.timefold.solver.core.api.domain.lookup.PlanningId;

// 移除@PlanningEntity注解
public class Teacher {

    @PlanningId
    private Long id;

    private String subject;
    private String restDay;

    // 移除@PlanningVariable注解的timeslot和room字段(这些属性属于Lesson规划实体)

    // No-arg constructor required for Timefold
    public Teacher() {
    }

    public Teacher(long id, String subject, String restDay) {
        this.id = id;
        this.subject = subject;
        this.restDay = restDay;
    }

    @Override
    public String toString() {
        return subject + "(" + id + ")";
    }

    // Getters and setters
    public Long getId() {
        return id;
    }

    public String getRestDay() {
        return restDay;
    }

    public String getSubject() {
        return subject;
    }

}

第二步:修正约束代码

通过遍历Lesson,关联其对应的Teacher,检查课程时间是否与教师休息日冲突:

Constraint teacherRestDayConstraint(ConstraintFactory constraintFactory) {
    return constraintFactory
            // 遍历所有Lesson规划实体
            .forEach(Lesson.class)
            // 关联Lesson对应的Teacher(假设Lesson有getTeacher()方法返回关联的Teacher对象)
            .join(Teacher.class, Joiners.equal(Lesson::getTeacher))
            // 过滤出违反约束的情况:课程时间段的星期等于教师休息日
            .filter((lesson, teacher) -> lesson.getTimeslot().getDayOfWeek().equals(teacher.getRestDay()))
            // 对每个违反情况施加硬惩罚
            .penalize(HardSoftScore.ONE_HARD)
            .justifyWith((lesson, teacher, score) -> 
                new TeacherConflictJustification(teacher, lesson, "教师" + teacher.getSubject() + "在休息日" + teacher.getRestDay() + "被安排课程"))
            .asConstraint("Teacher cannot teach on rest day");
}

额外优化建议

将Teacher的restDay字段类型从String改为java.time.DayOfWeek枚举,避免字符串格式错误导致的匹配问题,代码更健壮:

// Teacher类中修改字段类型
private DayOfWeek restDay;

// 构造方法调整
public Teacher(long id, String subject, DayOfWeek restDay) {
    this.id = id;
    this.subject = subject;
    this.restDay = restDay;
}

// 约束过滤条件调整
.filter((lesson, teacher) -> lesson.getTimeslot().getDayOfWeek() == teacher.getRestDay())

内容的提问来源于stack exchange,提问作者Peter C

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 20:30:38