【发布时间】:2015-12-03 12:09:12
【问题描述】:
想法
想象一下拥有分支机构的教育中心。该教育中心的课程适用于所有分支机构。
分支机构
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;
房间在每个分支中由管理员生成的每门课程。例如,管理员输入数学课程的房间数。系统生成 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 个不同的学生组。
学生 - 也按分支分组。每个学生都有每周计划 (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 年级混在一起。 11th 毕业(12th class 表示 sql 毕业)。但不是10-11日。 (将有两种模式:9-10、11-12)
- 小组最多可包含 8 名学生。
- 课程空间有限。因此,每个房间白天只能容纳 5 组
- 每个学生都必须在 1 天内参加所有(自己)选择的课程
搜索满足上述条件的group后,如果没有找到,应用必须创建并分配学生到group。然后:
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;
And here is fiddle to play with.
我做了什么
我正在尝试在知识评估期间将学生分配到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
}
问题是
理论上,我这样做的方式就像买飞机票。是无错误的,并且必须工作,但它不是有效的和最优的。必须以最有效的方式满足所有分组条件:最少组数和满足有限房间数政策。这种方法很快就会产生大量不适合可用房间时间的团体。
由于我一个一个地花费数小时的时间,(在评估过程中)获得真正有效的结果变得越来越难。由于房间限制,找不到小组并且无法创建新小组的机会随着学生数小时的增加而增加。
你建议用什么来利用每个房间的每一个小时?
更新
根据@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`)
)
)
)
使用此视图可以显示当前情况的完整场景。但我还是想不出算法来
- 将学生分组
- 将组放置在房间中
以最有效、最优化的方式创建最少的组数。
有什么建议吗?
【问题讨论】:
-
为什么房间仅限一门课程?
-
@DanBracuk 这是教育中心的规定
-
这就是赏金系统的真正用途,提升一个人的声誉,然后让其他人为我们解决系统中最复杂的广泛方面,笑声。我需要参与进来
-
您将软件开发的几个方面混为一谈:数据库设计、算法、域分析、PHP 等。我认为它太宽泛了,因为您要求的是“最有效、最佳的方式” ":你要什么证明?
-
在知识评估期间,您为什么要尝试将学生加入小组(现有小组或创建新小组)?是否可以推迟该任务?做出该决定的最后时刻/事件是什么?
标签: php mysql sql algorithm search