SQL高效调度生成算法

2024-01-16

The idea

想象一下教育中心有branches. Courses该教育中心的信息对所有分支机构都是通用的。

Branches

CREATE TABLE `Branch` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;


CREATE TABLE `Course` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `active` tinyint(1) DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

Rooms在管理员生成的每门课程的每个分支中。例如,管理员输入数学课程的房间数。系统生成3个房间。换句话说,它们受到数量的限制。

CREATE TABLE `Room` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `branch_id` int(10) unsigned DEFAULT NULL,
  `course_id` int(10) unsigned DEFAULT NULL,
  `occupied_hours` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

每个房间每天有 5 个小时的可用教学时间。换句话说,Math-1每个教学小时(共 5 个)将有 1 个不同的学生小组。

Students-也按分支分组。每个学生都有自己喜欢的每周计划(week_day_mode)来中学。

  • 一周中的第 1 天、第 3 天、第 5 天
  • 一周中的第 2 天、第 4 天、第 6 天

class字段是学校(主要学校)的年级,

CREATE TABLE `Student` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `fullname` varchar(255) NOT NULL,
  `class` tinyint(2) DEFAULT NULL,
  `branchID` int(10) unsigned DEFAULT NULL,
  `week_day_mode` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `branchID` (`branchID`)
) ENGINE=InnoDB AUTO_INCREMENT=246 DEFAULT CHARSET=utf8;

当管理员第一次注册学生时,他选择了学生想要参加的所有课程。例如,如果选择了 5 门课程StudentCourseAssoc将为该学生填充 5 行。在测试学生每门课程的基础知识水平后,管理员将学生在特定课程上评估为“聪明”(+1) 或“愚蠢”(-1)。所以knowledge_level是学生与课程连接的价值。

CREATE TABLE `StudentCourseAssoc` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `studentID` int(10) unsigned DEFAULT NULL,
  `courseID` int(10) unsigned DEFAULT NULL,
  `knowledge_level` tinyint(1) DEFAULT NULL,
  `group_id` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1144 DEFAULT CHARSET=utf8;

申请必须:

自动对符合以下条件的每个分支的学生进行分组(可以创建新组或将学生添加到现有组)

  • 聪明和笨的学生必须分在不同的组
  • 组可能由一些等级混合组成。所以,把9年级和10年级混在一起是可以的。第 11 名已毕业(第 12 名意味着 sql 专业毕业)。但不是10日至11日。 (将有2种模式:9-10、11-12)
  • 团体最多可包含 8 名学生。
  • 课程房间有限。因此,每个房间白天只能接待 5 组客人
  • 每个学生必须在 1 天内选修(自己)选择的每门课程

搜索后group满足上述条件,如果没有找到,应用程序必须创建并将学生分配给group. Then :

CREATE TABLE `StudentGroupAssoc` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `group_id` int(10) unsigned DEFAULT NULL,
  `student_id` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

CREATE TABLE `Schedule` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `group_id` int(10) unsigned DEFAULT NULL,
  `week_day_mode` tinyint(1) DEFAULT NULL,
  `hour` tinyint(1) DEFAULT NULL,
  `room_id` int(4) unsigned DEFAULT NULL,
  `teacher_id` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `Unique Room for exact time` (`week_day_mode`,`hour`,`room_id`) USING BTREE,
  UNIQUE KEY `Unique Group for exact time` (`group_id`,`week_day_mode`) USING BTREE,
  KEY `Unique Teacher for exact time` (`week_day_mode`,`hour`,`teacher_id`),
  KEY `room_id` (`room_id`),
  KEY `teacher_id` (`teacher_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

这是可以玩的小提琴。 http://sqlfiddle.com/#!9/7de96

我做了什么

我正在尝试将学生安排到group(现有的或创建新的)在知识评估期间。例如,如果学生选择数学作为其中一门课程,当管理员评估他的数学知识并评分为肯定时,程序开始为该学生选择正确的组:

  • 功能标志着学生的知识水平
  • 检查学生的可用时间(例如,第 1 个小时已经占用,那么他有 4 个可用时间)
  • 添加班级覆盖条件进行搜索(例如 9-10 年级或 11-12 年级)
  • 检查学生的每周计划中是否有可用时间的小组

如果没有,则尝试创建。

所以 PHP 表示看起来像这样

        //sets knowledge level of student
        $studentCourse->knowledge_level = intval($_POST["mark"]);

        //check hours of student, and keep only available hours
        $availableHours = array_combine(range(1, 5), range(1, 5));

        //Unsets students unavailable hours from possible hours
        if ($student->GroupRels)
            foreach ($student->GroupRels as $groupRel)
                unset($availableHours[$groupRel->hour]);

        //Checks available groups based on class coverage
        if (in_array($student->class, ['11', 'G']))
            $classCoverage = "11-m";
        else if (in_array($student->class, ['9', '10']))
            $classCoverage = "9-10";

        $availableGroups = Group::find()
            ->with("schedule")
            ->where([
                    "Group.class_coverage" => $classCoverage,
                    "Group.knowledge_level" => $studentCourse->knowledge_level,
                    "Group.participiant_count<8",
                    "Schedule.hour" => $availableHours,
                    'Schedule.week_day_mode' => $student->week_day_mode
                ]
            )->all();


        if (count($availableGroups) > 0) {
             //Selecting one of groups
             //adding row to StudentGroupAssoc
            //adding row to Schedule
        } else {
            $group = new Group();
            $group->branch_id = $student->branchID;
            $group->class_coverage = $classCoverage;
            $group->course_id=$studentCourse->courseID;
            $group->knowledge_level=$studentCourse->knowledge_level;
            $group->save();
            ...
            //adding row to StudentGroupAssoc
            //adding row to Schedule


        }

问题是

理论上,我所做的就像买飞机票一样。没有错误,并且必须工作,但它不是高效和最佳的。必须以最有效的方式满足所有分组条件:最小组数并满足有限房间数政策。这种方法很快就会造成大量无法容纳可用房间时间的团体。

当我一个接一个地对学生进行数小时的培训时,(在评估过程中)获得真正有效的结果变得越来越困难。由于房间限制,找不到小组并且无法创建新小组的可能性会随着学生的时间而不断增加。

您建议用什么来充分利用每个房间的每一个小时?

UPDATE

根据 @norbert_van_nobelen 的回答,我创建了“虚拟”小时表和以下视图,以获得每个学生所有可能的小时-房间-课程组合列表。

hours实际计划时间hours_available是二进制开关。 因此,在实际代码中,我们添加一个 where 子句: WHERE hours_available=0 来仅获取我们想要计划的小时数:

SELECT
    `s`.`id` AS `student_id`,

IF ((ifnull(`sch`.`hour`, 0) > 0), 1, 0) AS `hour_available`,
 `d`.`hours` AS `hours`,
 `sca`.`courseID` AS `courseID`,
 `sch`.`room_id` AS `room_id`,
 `sca`.`knowledge_level` AS `knowledge_level`,
 (
    CASE
    WHEN (
        (`s`.`class` = 9)
        OR (`s`.`class` = 10)
    ) THEN
        '9-10'
    WHEN (
        (`s`.`class` = 11)
        OR (`s`.`class` = 12)
    ) THEN
        '11-12'
    ELSE
        '??'
    END
) AS `class_variant`
FROM
    (
        (
            (
                (
                    `dummy_hours` `d`
                    JOIN `Student` `s`
                )
                LEFT JOIN `StudentCourseAssoc` `sca` ON ((`s`.`id` = `sca`.`studentID`))
            )
            LEFT JOIN `StudentGroupAssoc` `b` ON ((`s`.`id` = `b`.`student_id`))
        )
        LEFT JOIN `Schedule` `sch` ON (
            (
                (
                    `sch`.`group_id` = `b`.`group_id`
                )
                AND (`d`.`hours` = `sch`.`hour`)
            )
        )
    )

使用此视图可以全面了解当前情况。但我仍然无法弄清楚算法

  • 将学生分组
  • 将组放置在房间中

以最有效、最佳的方式创建最少的组数。

有什么建议么?


这个答案只是作为日程部分的解决方案方向,而不是 100% 好的解决方案:

您创建的内容需要循环才能满足所有条件。

为了更快地解决这种情况,可以在向量中工作,而不是在向量中所有位置都由 0(可用)和 1(采取)表示。

所以学生/数学 1 问题:

假设有 2 个房间和 3 小时:每个房间的 math-1 向量为:

Room 1: [0 0 0]
Room 2: [0 0 0]

本质上(至少我)不关心某个房间是否可用,只要有 1 个房间可用即可: 因此,在这种情况下,每个索引的 AND 可能是可用性的答案(记住:0 可用):

房间 1:[1 0 0] 房间 2:[0 0 0] 房间结果:[1 0 0] AND [0 0 0]=[0 0 0]

因此 AND 可以判断第一个小时是否仍然可用。

如果您现在将其与具有可用时间的学生结合起来(本例中也只有 3 个时间):

学生A:[0 0 1] 房间结果:[0 0 0] 学生使用 OR 与房间进行此操作匹配: [0 0 1] 或 [0 0 0]=[0 0 1]

所以学生 A 会匹配到房间结果。

在 SQL 中:数据模型(部分:缺少课程匹配): 表间:

CREATE TABLE room(
room_id INT,
space TINYINT DEFAULT 0,
hour INT DEFAULT 1
);

CREATE TABLE student(
student_id INT,
space TINYINT DEFAULT 0,
hour INT DEFAULT 1
)

所有数据均已完整插入表中:在本例中,1 个房间、3 小时、3 个可用位置。

INSERT INTO room VALUES (1,0,1);
INSERT INTO room VALUES (1,0,1);
INSERT INTO room VALUES (1,0,1);
INSERT INTO room VALUES (1,0,2);
INSERT INTO room VALUES (1,0,2);
INSERT INTO room VALUES (1,0,2);
INSERT INTO room VALUES (1,0,3);
INSERT INTO room VALUES (1,0,3);
INSERT INTO room VALUES (1,0,3);

学生有:

INSERT INTO student VALUES(1,0,1);   
INSERT INTO student VALUES(1,0,2);   
INSERT INTO student VALUES(1,1,3);   

因此,学生只能在前两个小时内有空。

现在获取查询结果:

SELECT room_id
FROM room a
INNER JOIN student b ON a.space=b.space AND a.hour=b.hour;

这个结果只需被分成最多8个组,其中它是SQL部分的结束和另一种编程语言的时间。

该模型可以使用日期进行扩展,但是在仅使用小时和工作日时效果最佳(工作日可用性再次为 0 或 1)。

正如我所说:这是一个概念/想法,不是 100% 的解决方案,所以在使用它之前需要做一些工作......

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SQL高效调度生成算法 的相关文章

  • 在bigquery中比较两个表的有效方法

    我有兴趣比较两个表是否包含相同的数据 我可以这样做 standardSQL SELECT key1 key2 FROM SELECT table1 key1 table1 key2 table1 column1 table2 column1
  • Mysql关于重复键更新+子查询

    使用这个问题的答案 需要 MySQL INSERT SELECT 查询具有数百万条记录的表 https stackoverflow com questions 662877 need mysql insert select query fo
  • MySQL 将表的校验和存储在另一个表中

    语境 我们有包含大量表的大型数据库 他们中的大多数 99 都使用innodb 我们希望有一个日常流程来监视哪个表已被修改 当他们使用 innodb 的值时Update time from SHOW table STATUS from inf
  • 如何通过开始索引和结束索引提取子字符串?

    str HelloWorld sub substr str 3 5 echo sub prints loWor 我知道 substr 采用第一个参数 第二个参数是开始索引 而第三个参数是要提取的子字符串长度 我需要的是通过提取子字符串起始索
  • If Else 条件的 SQLite 语法

    我正在使用 SQLite 数据库 我的表有一个名为 密码 的文本列 早些时候 为了检索我用来执行简单操作的值select from myTable询问 但现在的要求是 如果Password值不是NULL那么我需要将其显示为 是 或 否 它是
  • Bing 图像搜索 API 按图像大小过滤

    我正在使用 jsonp 和 jquery ajax 来使用 Bing 图像搜索 API 我能够检索搜索结果 但我无法找到按图像大小过滤结果的方法 我在文档中找不到任何与此相关的内容 有谁知道是否有一种方法可以按图像大小过滤结果或对此进行任何
  • 模式识别算法

    过去我必须开发一个充当规则评估器的程序 你有一个先行词和一些后续词 动作 所以如果先行词评估为真 则执行的动作 当时我用的是修改版RETE算法 http en wikipedia org wiki Rete algorithm RETE 有
  • 一次用 \r\n & \n & \r 分解字符串? [复制]

    这个问题在这里已经有答案了 我想按行分割字符串 但我希望它基于所有主要使用的换行符 n r n r 并返回一个包含每一行的数组 您可以使用正则表达式和preg split http php net preg split反而 lines pr
  • 如何处理致命错误:cURL错误7:无法连接到xxxx端口443

    我有一个连接到第三方 API 的脚本 它是并且应该在 24 7 不间断循环上运行 我在重新启动循环之前在最后使用睡眠 问题是 有时第三方 API 会被拒绝 或者连接会因以下错误而中断 致命错误 未捕获的异常 GuzzleHttp Ring
  • 如何下载临时文件

    我正在尝试创建一个简短的 PHP 脚本 该脚本采用 JSON 字符串 将其转换为 CSV 格式 使用fputcsv 并使该 CSV 可作为下载的 csv 文件使用 我的想法是使用tmpfile 不用担心 cronjobs 或磁盘空间不足 但
  • 多维数组 PHP 内爆 [重复]

    这个问题在这里已经有答案了 就我的数据结构而言 我有一个 communications 数组 每个 communications id 本身包含三部分信息 id score 和 content 我想内爆这个数组以获得逗号分隔的 id 列表
  • 使用多个 WHERE 子句更新 Codeigniter 中的批次

    我查看了 CI 用户指南来了解如何处理update batch 并且它似乎只接受一个索引来匹配要更新的行 但在我的例子中 我需要指定两个索引 例如lang and id page我一起用作索引 这样的lang en id page 115是
  • Yii2 组件将数据传递给 __construct

    我有一个库 我想将其用作组件 在配置文件中我这样设置 components gt superLib gt class gt SuperLib construct gt first second Maybe Yii 2 have proper
  • 检查字符串是否编码为 UTF-8

    function seems utf8 str length strlen str for i 0 i lt length i c ord str i if c lt 0x80 n 0 0bbbbbbb elseif c 0xE0 0xC0
  • MySQL如何从多个表中获取数据

    我正在寻找 php MySQL jquery 的帮助 我有2张桌子 table1表 1 有 4 列 id 标题 desc thumb img tabel2表 2 有 3 列 id 表 id img 我只想将 2 个表与 get QS 的值进
  • Symfony VS CakePHP:哪一个最接近 PHP [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我主要是一名 Rails 开发人员 但有时 我必须使用 PHP 进行编码 因为 stackoverflow com更喜欢可以回答的问题 我想知道
  • 获取所有ios应用程序的全局列表[重复]

    这个问题在这里已经有答案了 我想对苹果应用商店进行一些全球统计 一个瓶颈是至少获取所有当前活动应用程序的 ID 这 9 位数字 有谁知道如何获取 iOS 应用商店中当前活动应用程序的所有 id 的完整列表 更好的是特定类别的所有 ID 例如
  • Yii2:无法将列值更新+1

    创建新记录时 我需要将列值更新 1 public function actionCreate model new CreateBookings if model gt load Yii app gt request gt post Yii
  • 在 Django shell 会话期间获取 SQL 查询计数

    有没有办法打印 Django ORM 在 Django shell 会话期间执行的原始 SQL 查询的数量 Django 调试工具栏已经提供了此类信息 例如 5 QUERIES in 5 83MS但如何从 shell 中获取它并不明显 您可
  • 如何获取所有mysql元组结果并转换为json

    我能够从表中获取单个数据 但是当我试图获取表上的所有数据时 我只得到一行 cnn execute sql rows cnn fetchall column t 0 for t in cnn description for row in ro

随机推荐