创build学校时间表的algorithm
我一直在想,是否有已知的创build学校时间表algorithm的解决scheme。 基本上,这是关于为给定的class主题 – 教师协会优化“小时分散”(在教师和class级的情况下)。 我们可以假设我们有一组课程,课程主题和教师相互关联,并且时间表应该在上午8点到下午4点之间。
我想这可能没有准确的algorithm,但也许有人知道一个很好的近似或暗示开发它。
这个问题是NP-Complete !
简而言之,需要探索所有可能的组合以find可接受解决scheme的列表。 由于各个学校出现问题的情况各不相同(例如:课室是否有限制?是否有些class级在一些时间分组?),这是一个每周的时间表吗?等等)不存在与所有调度问题相对应的众所周知的问题类别。 也许, 背包问题在很大程度上与这些问题有许多相似之处。
确认这既是一个困难的问题,也是人们常年寻求解决方法的一个方法,就是检查这个(多数是商业的)软件调度工具
由于涉及到大量的variables,其中最大的来源通常是教员成员的愿望; – )…, 考虑列举所有可能的组合通常是不切实际的 。 相反,我们需要select一种访问问题/解决scheme空间子集的方法。
– 在另一个答案中引用的遗传algorithm是(或,恕我直言, 似乎 )装备精良,执行这种半导游search(问题是find一个良好的评价函数为候选人保留下一代)
– graphics重写方法也适用于这种types的组合优化问题。
我不想把注意力集中在一个自动调度生成器程序的特定实现上,而是提出一些可以在问题定义层面上应用的策略 。
一般的基本原理是,在大多数现实世界的调度问题中,需要一些妥协,而不是所有的约束,expression和暗示:将得到充分满足。 所以我们帮助自己:
- 定义和排列所有已知的约束
- 通过手动减less问题空间,提供一组额外的约束。
这可能看起来与直觉相反,例如通过提供初始的,部分填充的时间表(大约30%的时隙),以完全满足所有约束的方式,并且通过考虑这个部分时间表是不可变的,我们显着减less生成候选解决scheme所需的时间/空间。
另外一个额外的约束条件有助于例如“人为地”添加一个约束条件,这个约束条件在一周中的某些天不能教授某些科目(如果这是一个每周的时间表)。 这种约束导致减less问题/解决scheme空间,通常不排除大量的优秀候选人。 - 确保能够快速计算出问题的一些限制。 这常常与用于表示问题的数据模型的select有关; 这个想法是能够快速select(或删除)一些选项。
- 重新定义问题,并允许一些约束被破坏几次(通常是朝向图的末端节点)。 这里的想法是要么删除一些限制,以填补进度表中的最后几个时间段,要么让自动计划生成程序不再害怕完成整个计划,而是向我们提供十几个合理的列表候选人。 一个人通常能够更好地完成这个难题,正如所指出的那样,通过使用自动逻辑通常不会共享的信息(例如,“下午没有math规则”可以被打破对于“高等math和物理”课;或者“最好打破琼斯先生的一个要求,而不是史密斯女士的一个要求……–))
为了certificate这个答案,我意识到提供一个明确的答案是相当害羞的,但是它却不是那么充满实际的build议。 我希望这个帮助,毕竟是一个“难题”。
一团糟。 一个皇家烂摊子。 为了增加已经非常完整的答案,我想指出我的家庭经验。 我的母亲是一名老师,曾经参与这个过程。
结果发现有一台电脑这样做不仅难以编码,而且也很困难,因为有一些条件很难指定给预先制作的计算机程序。 例子:
- 一名教师在你的学校和另一所学院教书。 显然,如果他在10点30分结束课程,他不能在10点30分在你的宿舍开始工作,因为他需要一些时间在学院之间上下class。
- 两位老师结婚了 一般来说,不要在同一class上有两位已婚教师。 这两位老师因此必须有两个不同的class级
- 两位老师结婚了,他们的孩子参加同一所学校。 再次,你必须阻止两位老师在他们的孩子所在的具体class级任教。
- 学校有单独的设施,就像有一天class级在一个学院里,另一个class级在另一个class里。
- 学校共用实验室,但这些实验室只能在某些工作日提供(出于安全原因,例如在需要额外人员的情况下)。
- 一些老师对自由日有偏好:有些喜欢周一,周五有些,周三有些喜欢。 有些人喜欢早上来,有些人喜欢晚点来。
- 你不应该有这样一种情况:你在第一个小时,第三个小时,第三个小时,第二个小时,第二个小时,第二个小时, 对于学生,对老师来说都没有意义。
- 你应该平均分散论据。 在本周的第一天只有math,然后在本周的其余时间只有文学是没有意义的。
- 你应该给一些教师连续两个小时做评估testing。
正如你所看到的,这个问题不是NP完全的,而是NP-疯狂的。
所以他们做的是,他们有一个小的塑料插入一张大桌子,他们移动插入,直到取得满意的结果。 他们从来没有从头开始:他们通常从前一年的时间表开始调整。
国际时间表比赛2007有一个课程安排跟踪和考试安排轨道。 许多研究人员参加了这场比赛。 尝试了许多启发式algorithm和metaheuristics,但最终本地searchmetaheuristics(如禁忌search和模拟退火)显然击败其他algorithm(如遗传algorithm)。
看看一些入围者使用的2个开源框架:
- JBoss OptaPlanner (Java,开源)
- Unitime (Java,开源) – 更多的大学
我的半个学期任务之一是遗传algorithm学校一代。
整桌是一个“有机体”。 通用遗传algorithm方法有一些变化和警告:
-
“非法桌子”的规定是:同一间教室有两个class级,一个教师同时教两个class组等。这些突变立即被视为致命的,一个新的“有机体”立即发生在“死者”的位置。 最初的一个是通过一系列随机尝试来获得合法的(如果没有意义的)。 致死突变不计入迭代中的突变数。
-
“交换”突变比“修饰”突变更普遍。 只有在有意义的基因部分之间进行改变 – 不能用教室代替老师。
-
小额奖金分配在一起捆绑2小时,为同一组别分配相同的通用教室,以保持教师的工作时间和class级的负荷连续。 分配的奖金分配给正确的课堂给定的科目,保持上课时间(上午或下午)等课程时间。 大额奖金是分配正确数量的给定的主题,给老师的工作量等。
-
教师可以创build“当时想工作”,“可以继续工作”,“不喜欢工作”,“不能工作”的工作量表,分配适当的权重。 整整24小时是法定工作时间,除非夜间时间非常不受欢迎。
-
重量function…哦是的。 权重函数是庞大的,巨大的产品(如乘法)的权重分配给选定的function和属性。 这是非常陡峭的,一个属性很容易能够改变一个数量级上下,而在一个有机体有成百上千的属性。 这导致了绝对的数字作为权重,作为一个直接的结果,需要使用一个bmp库(gmp)来执行计算。 对于10个小组,10个教师和10个教室的一个小型testing用例,最初的集合从10 ^ – 200个东西开始,以10 ^ + 300个东西结束。 当它更平坦的时候,这是完全没有效率的。 此外,价值观与更大的“学校”之间的距离增长了很多。
-
从计算时间来看,长期less数人口(100人)与less数人口(10万多人口)之间几乎没有差别。 同一时间的计算产生了大致相同的质量。
-
计算(在一些1GHz的CPU上)需要1个小时才能稳定在10 ^ + 300附近,生成的时间表看起来相当不错,就10x10x10的testing用例而言。
-
这个问题很容易通过提供networking设备来实现,这种设备可以在运行计算的计算机之间交换最好的标本。
由此产生的程序从来没有看到外面的日光让我在这个学期的好成绩。 它显示了一些承诺,但我从来没有足够的动机来添加任何GUI并使其可用于普通大众。
这个问题比看起来更困难。
正如其他人所暗示的那样,这是一个NP完全问题,但让我们分析一下这意味着什么。
基本上,这意味着你必须看看所有可能的组合。
但是“看”并不能告诉你你需要做什么。
生成所有可能的组合很容易。 它可能会产生大量的数据,但是你不应该有太多的问题来理解这部分问题的概念。
第二个问题是判断一个给定的可能的组合是好是坏,还是比以前的“好”解决scheme更好。
为此,你需要的不仅仅是“这是一个可能的解决scheme”。
例如,连续X周,每周5天工作的同一位老师? 即使这是一个有效的解决scheme,但这可能不是一个比两个人交替的更好的解决scheme,每个老师每个人都要做一个星期。 哦,你没有想到这个? 请记住,这是你正在处理的人,而不仅仅是一个资源分配问题。
即使一位教师可以连续16周全职工作,与那些试图在教师之间交替的解决scheme相比,这可能是一个次优的解决scheme,而这种平衡是非常难以构build到软件中的。
总而言之,对许多人来说,为这个问题提供一个很好的解决scheme将是非常值得的。 因此,打破和解决并不容易。 准备放弃一些不是100%的目标,并称之为“足够好”。
更新:从评论…应该也有启发式的!
我会用Prolog去…然后使用Ruby或Perl或其他东西来清理你的解决scheme到一个更漂亮的forms。
teaches(Jill,math). teaches(Joe,history). involves(MA101,math). involves(SS104,history). myHeuristic(D,A,B) :- [test_case]->D='<';D='>'. createSchedule :- findall(Class,involves(Class,Subject),Classes), predsort(myHeuristic,Classes,ClassesNew), createSchedule(ClassesNew,[]). createSchedule(Classes,Scheduled) :- [the actual recursive algorithm].
我(仍然)正在做类似于这个问题的过程,但是使用与我刚刚提到的相同的path。 Prolog(作为一种function性语言)的确让解决NP-Hard问题更加容易。
遗传algorithm通常用于这种调度。
find了这个例子(使用遗传algorithm制作课程表) ,它非常符合你的要求。
以下是我find的几个链接:
学校时间表 – 列出涉及的一些问题
学校时间表的混合遗传algorithm
计划实用程序和工具
我的时间表algorithm,在FET(免费时间表软件, http : //lalescu.ro/liviu/fet/ ,一个成功的应用程序)中实现:
该algorithm是启发式的。 我把它命名为“recursion交换”。
input:一组活动A_1 … A_n和约束条件。
输出:一组时间TA_1 … TA_n(每个活动的时间段,为简单起见,这里不包括房间)。 该algorithm必须把每个活动放在一个时隙,考虑约束条件。 每个TA_i在0(T_1)和max_time_slots-1(T_m)之间。
约束:
C1)Basic:一对不能同时进行的活动(例如,A_1和A_2,因为他们有相同的老师或同一个学生);
C2)许多其他限制(为简单起见,不包括在内)。
时间表algorithm(我称之为“recursion交换”):
- sorting活动,首先是最难的。 不是关键的一步,但加速algorithm可能是10倍或更多。
-
尝试将每个活动(A_i)放置在允许的时间段内,按照以上顺序,一次一个。 searchA_i的可用插槽(T_j),其中可以根据约束来放置该活动。 如果有更多的插槽可用,请随机select一个插槽。 如果没有可用的,则执行recursion交换:
a 。 对于每个时间段T_j,考虑如果将A_i放入T_j会发生什么情况。 将会有一个不同意这个动作的其他活动列表(例如,活动A_k在同一个位置T_j,并且与A_i具有相同的老师或同一个学生)。 保留每个时间段T_j的冲突活动列表。
b 。 select冲突活动数最less的时段(T_j)。 假设这个槽中的活动列表包含3个活动:A_p,A_q,A_r。
c 。 将A_i置于T_j,并使A_p,A_q,A_r未分配。
d 。 在A_i开始时,recursion地尝试放置A_p,A_q,A_r(如果recursion的级别不太大,例如14,并且如果从步骤2开始计算的recursion调用的总数)不是太大,例如2 * n),如步骤2)。
e 。 如果成功放置A_p,A_q,A_r,则返回成功,否则尝试其他时隙(转到步骤2b)并select下一个最佳时隙。
f 。 如果所有(或合理数量的)时隙尝试失败,则返回不成功。
g 。 如果我们处于0级,并且我们没有成功放置A_i,就像在步骤2 b)和2 c)中一样,但是没有recursion。 我们现在有3 – 1 = 2个活动。 转到步骤2)(在这里使用一些避免骑车的方法)。
我devise了商业algorithm,用于课程时间表和考试时间表。 对于第一个我使用整数编程; 第二个是基于通过select时隙交换来最大化目标函数的启发式,与已经演变的原始手动过程非常相似。 他们接受这样的解决scheme的主要事情是能够代表所有的现实世界的限制; 而对于人类的时间表则无法看到改进解决scheme的方法。 algorithm部分与数据库的准备,用户界面,房间利用率,用户教育等统计报表的能力相比,algorithm部分相当简单,易于实现。
本文描述了学校的时间表问题及其对algorithm的很好的处理方法:“ SYLLABUS的发展 – 一种基于约束的互动式学校调度程序 ”[PDF]
作者告诉我SYLLABUS软件仍在使用/开发在这里: http : //www.scientia.com/uk/
我在一个广泛使用的调度引擎上工作,正是这个。 是的,这是NP-Complete; 最好的方法是寻求近似最佳的解决scheme。 当然,还有很多不同的方式可以说哪一个是“最好的”解决scheme – 例如,老师对他们的时间安排感到满意,或者学生能够进入他们所有的class级,这更重要吗?
你需要尽早解决的绝对最重要的问题是什么使得这个系统比另一个更好的安排呢? 也就是说,如果我有一个时间表,琼斯太太在8点教math,史密斯先生在9点教math,那他们两个都在10点教math吗? 琼斯太太在8点教书,琼斯先生在2点教书,是不是更好呢? 为什么?
我在这里给出的主要build议是尽可能地分解问题 – 当然也许是课程,也许是老师,也许是一个房间,然后先解决子问题。 在那里你应该有多种解决scheme可供select,并且需要select一个最有可能的最佳解决scheme。 那么,使得“较早”的子问题在考虑后来的子问题的潜在解决scheme的需求方面的工作。 然后,当你遇到“没有有效的解决scheme”的状态时,或许可以解决如何让自己摆脱被涂到angular落里的情况(假设你不能预见到前面的子问题)。
通常使用本地search优化传球来“抛光”最终答案以获得更好的结果。
请注意,通常情况下,我们正在处理高度资源有限的学校调度系统。 学校每年都有75%的空闲房间或老师坐在rest室里度过这一年。 在解决scheme丰富的环境中效果最好的方法不一定适用于学校计划。
一般来说,约束规划是这类调度问题的一个很好的方法。 在堆栈溢出和Google上search“约束规划”和调度或“基于约束的调度”将产生一些很好的参考。 这不是不可能的 – 使用传统的优化方法(如线性优化或整数优化)只是有点难以考虑。 一个输出是 – 是否存在满足所有要求的时间表? 这本身显然是有帮助的。
祝你好运 !
是的,你可以用遗传algorithm来处理它。 但是你不应该:)。 它可能太慢,参数调整可能太耗时。
还有其他的方法。 所有在开源项目中实现:
- 基于约束的方法
- 在UniTime中实现(不是真的为了学校)
- 你也可以进一步使用Integer编程。 在乌迪内大学和拜罗伊特大学(我曾参与过)使用商业软件(ILOG CPLEX)
- 基于规则的heuristisc方法 – 请参阅Drools规划师
- 不同的启发式 – FET和我自己的
看到这里的时间表软件列表
我想你应该使用遗传algorithm,因为:
- 它最适合于大型问题实例。
- 它会减less不准确答案的价格的时间复杂性(不是最好的)
- 您可以轻松指定约束条件和偏好,通过调整不符合条件的健身惩罚。
- 您可以指定程序执行的时间限制。
-
解决scheme的质量取决于你打算花费多less时间来解决这个计划。
遗传algorithm定义
遗传algorithm教程
具有GA的课程调度项目
另外看看: 一个类似的问题 , 另一个
我不知道任何人会同意这个代码,但我用我自己的algorithm的帮助下开发了这个代码,并在ruby中为我工作。希望它会帮助他们在下面的代码中search它的periodflag,dayflag subjectflag和teacherflag是具有相应的id和布尔值的标志值的散列。 任何问题与我联系…….(-_-)
periodflag.each do | k2,v2 |
if(TimetableDefinition.find(k2).period.to_i != 0) subjectflag.each do |k3,v3| if (v3 == 0) if(getflag_period(periodflag,k2)) @teachers=EmployeesSubject.where(subject_name: @subjects.find(k3).name, division_id: division.id).pluck(:employee_id) @teacherlists=Employee.find(@teachers) teacherflag=Hash[teacher_flag(@teacherlists,teacherflag,flag).to_a.shuffle] teacherflag.each do |k4,v4| if(v4 == 0) if(getflag_subject(subjectflag,k3)) subjectperiod=TimetableAssign.where("timetable_definition_id = ? AND subject_id = ?",k2,k3) if subjectperiod.blank? issubjectpresent=TimetableAssign.where("section_id = ? AND subject_id = ?",section.id,k3) if issubjectpresent.blank? isteacherpresent=TimetableAssign.where("section_id = ? AND employee_id = ?",section.id,k4) if isteacherpresent.blank? @finaltt=TimetableAssign.new @finaltt.timetable_struct_id=@timetable_struct.id @finaltt.employee_id=k4 @finaltt.section_id=section.id @finaltt.standard_id=standard.id @finaltt.division_id=division.id @finaltt.subject_id=k3 @finaltt.timetable_definition_id=k2 @finaltt.timetable_day_id=k1 set_school_id(@finaltt,current_user) if(@finaltt.save) setflag_sub(subjectflag,k3,1) setflag_period(periodflag,k2,1) setflag_teacher(teacherflag,k4,1) end end else @subjectdetail=TimetableAssign.find_by_section_id_and_subject_id(@section.id,k3) @finaltt=TimetableAssign.new @finaltt.timetable_struct_id=@subjectdetail.timetable_struct_id @finaltt.employee_id=@subjectdetail.employee_id @finaltt.section_id=section.id @finaltt.standard_id=standard.id @finaltt.division_id=division.id @finaltt.subject_id=@subjectdetail.subject_id @finaltt.timetable_definition_id=k2 @finaltt.timetable_day_id=k1 set_school_id(@finaltt,current_user) if(@finaltt.save) setflag_sub(subjectflag,k3,1) setflag_period(periodflag,k2,1) setflag_teacher(teacherflag,k4,1) end end end end end end end end end end end