【教程3】OptaPlanner Spring Boot HelloworldJava快速入门

本教程介绍了如何使用OptaPlanner和Spring Boot构建一个解决学校课程表优化问题的REST应用程序。通过建模Timeslot、Room和Lesson,设置约束条件并计算得分,实现自动分配课程到时间段和教室。文章详细讲解了从构建文件、领域对象建模到解算器服务的创建过程,并提供了测试和进一步集成数据库和UI的建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OptaPlanner Spring Boot HelloworldJava快速入门


本指南将引导您创建一个使用OptaPlanner的约束求解人工智能(AI)的Spring Boot应用程序。

1. 构建什么

您将构建一个优化学生和教师的学校课程表的REST应用程序:

schoolTimetablingScreenshot

您的服务将使用AI自动将Lesson实例分配给Timeslot和Room实例,以遵守硬性和软性调度约束,例如以下示例:

  • 一个房间在同一时间内最多只能有一节课。
  • 一位教师在同一时间内最多只能上一节课。
  • 一个学生在同一时间内最多只能上一节课。
  • 一位教师更喜欢在同一个房间上所有课程。
  • 一位教师更喜欢连续的课程,并且不喜欢课程之间的空隙。
  • 一个学生不喜欢连续的相同科目的课程。

从数学角度来说,学校课程安排是一个NP困难问题。这意味着很难进行扩展。即使在超级计算机上,简单地通过迭代所有可能的组合也需要数百万年才能处理一个非平凡的数据集。幸运的是,像OptaPlanner这样的AI约束求解器具有先进的算法,可以在合理的时间内提供近乎最优的解决方案。

2. 解决方案源代码

按照下一节中的说明逐步创建应用程序(推荐)。

或者,您也可以直接跳转到已完成的示例:

克隆Git仓库:

$ git clone https://github.com/kiegroup/optaplanner-quickstarts

或下载一个归档文件。

在technology目录中找到解决方案并运行它(参见其README文件)。

3. 先决条件

要完成本指南,您需要:

  • 配置了JAVA_HOME的JDK 11+
  • Apache Maven 3.8.1+或Gradle 4+
  • 一个IDE,例如IntelliJ IDEA、VSCode或Eclipse

4. 构建文件和依赖项

使用以下依赖项创建一个Spring Boot应用程序:

  • Spring Web(spring-boot-starter-web)
  • OptaPlanner(optaplanner-spring-boot-starter)

如果选择Maven,则pom.xml文件的内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.7</version>
    </parent>

    <groupId>org.acme</groupId>
    <artifactId>optaplanner-spring-boot-school-timetabling-quickstart</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>11</java.version>
        <version.org.optaplanner>9.44.0.Final</version.org.optaplanner>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.optaplanner</groupId>
                <artifactId>optaplanner-bom</artifactId>
                <version>${version.org.optaplanner}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.optaplanner</groupId>
            <artifactId>optaplanner-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.optaplanner</groupId>
            <artifactId>optaplanner-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

另一方面,在Gradle中,您的build.gradle文件如下所示:

plugins {
   
    id "org.springframework.boot" version "3.0.7"
    id "io.spring.dependency-management" version "1.0.11.RELEASE"
    id "java"
}

def optaplannerVersion = "9.44.0.Final"

group = "org.acme"
version = "1.0-SNAPSHOT"
sourceCompatibility = "11"

repositories {
   
    mavenCentral()
}

dependencies {
   
    implementation "org.springframework.boot:spring-boot-starter-web"
    implementation "org.springframework.boot:spring-boot-starter-data-rest"
    testImplementation("org.springframework.boot:spring-boot-starter-test")

    implementation platform("org.optaplanner:optaplanner-bom:${
     optaplannerVersion}")
    implementation "org.optaplanner:optaplanner-spring-boot-starter"
    testImplementation("org.optaplanner:optaplanner-test")
}

test {
   
    useJUnitPlatform()
}

5. 建模领域对象

您的目标是将每节课分配给一个时间段和一个教室。您将创建以下类:

schoolTimetablingClassDiagramPure

5.1 Timeslot

Timeslot类表示教学课程的时间间隔,例如周一10:30 - 11:30或周二13:30 - 14:30。为简单起见,所有时间段的持续时间相同,午餐时间或其他休息时间没有时间段。

一个时间段没有日期,因为高中课程表每周都会重复。因此,在连续规划中不需要连续规划。

创建src/main/java/org/acme/schooltimetabling/domain/Timeslot.java类:

package org.acme.schooltimetabling.domain;

import java.time.DayOfWeek;
import java.time.LocalTime;

public class Timeslot {
   

    private DayOfWeek dayOfWeek;
    private LocalTime startTime;
    private LocalTime endTime;

    public Timeslot() {
   
    }

    public Timeslot(DayOfWeek dayOfWeek, LocalTime startTime, LocalTime endTime) {
   
        this.dayOfWeek = dayOfWeek;
        this.startTime = startTime;
        this.endTime = endTime;
    }

    public DayOfWeek getDayOfWeek() {
   
        return dayOfWeek;
    }

    public LocalTime getStartTime() {
   
        return startTime;
    }

    public LocalTime getEndTime() {
   
        return endTime;
    }

    @Override
    public String toString() {
   
        return dayOfWeek + " " + startTime;
    }

}

由于在求解过程中Timeslot实例不会改变,因此Timeslot被称为问题事实。这样的类不需要任何OptaPlanner特定的注释。

注意toString()方法保持输出简短,以便更容易阅读OptaPlanner的DEBUG或TRACE日志,如后面所示。

5.2 Room

Room类表示教学课程的地点,例如A教室或B教室。为简单起见,所有教室都没有容量限制,可以容纳所有课程。

创建src/main/java/org/acme/schooltimetabling/domain/Room.java类:

package org.acme.schooltimetabling.domain;

public class Room {
   

    private String name;

    public Room() {
   
    }

    public Room(String name) {
   
        this.name = name;
    }

    public String getName() {
   
        return name;
    }

    @Override
    public String toString() {
   
        return name;
    }

}

Room实例在求解过程中不会改变,因此Room也是一个问题事实。

5.3 Lesson

在由Lesson类表示的课程中,教师向一组学生教授一个科目,例如9年级的A.Turing的数学课或10年级的M.Curie的化学课。如果同一位教师每周多次教授同一科目给同一学生群体,则有多个仅通过id区分的Lesson实例。例如,9年级每周有六节数学课。

在求解过程中,OptaPlanner会更改Lesson类的timeslot和room字段,以将每节课分配给时间段和教室。因为OptaPlanner会更改这些字段,所以Lesson是一个计划实体:

schoolTimetablingClassDiagramAnnotated

前图中大部分字段包含输入数据,除了橙色字段:输入数据中的课程的timeslot和room字段未分配(null),输出数据中已分配(非null)。OptaPlanner在求解过程中更改这些字段。这样的字段称为计划变量。为了让OptaPlanner识别它们,timeslot和room字段都需要一个@PlanningVariable注释。它们所属的类Lesson需要一个@PlanningEntity注释

创建src/main/java/org/acme/schooltimetabling/domain/Lesson.java类:

package org.acme.schooltimetabling.domain;

import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.lookup.PlanningId;
import org.optaplanner.core.api.domain.variable.PlanningVariable;

@PlanningEntity
public class Lesson {
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值