遗传算法解决高密度网课排课问题

 

突如其来的疫情改变了人们生活学习的方方面面,大规模网上授课便是其中之一。

在排课问题上,网课是有一些优势的:网课是没有教室数量的限制,而且教室容量还是无限的,使得一位老师同时给几万名学生上课,成为可能(思政类课程)。

在排课问题上,网课的劣势也是显而易见的:大规模的学生在同一时段上课引起的冲突变得更加不好处理,而网课同时要求在相对短的时间段内完成多门课的排课,这会增加课程的密度,加大解决排课问题的复杂度。由于网课没有了教学资源的限制,这又引出了一个新的需求,多门课程合并上课,比如某些课程在A专业的学生来说是专业课;可在对B专业的学生是专业基础课,而课程内容95%是相同的,类似这样不同的课程代码要合并在一起上课。在数据结构上,同一位老师,在同一时刻要在同一教室上多门课,这是传统排课思路所无法理解的。

前段时间老婆就遇到了这样的问题:多轮次、高频度,大密度,大规模的网课如何进行教学安排呢?问题涉及200门课,几万名学生选课,人均选课门数5.5门。

网课的排课思路与传统课程完全不同,在给定的限制时间段内,即便经验丰富的教务员也很难排出合适的网课课表。我想起自己曾经用遗传算法做过安排考场的项目,应用场景跟排课非常相似,便自告奋勇地说要帮她写个程序来实现程序自动排课。

需求大致清楚了,下面说说解题思路:

需要整理出2份excel数据喂给程序,如下图:原始数据脱敏后,会提供下载。


第一张表是课程id与教师id对应关系,用于确保一位教师,同一时间只能上一门课。

第二张表是用于确定课程id与班级id的关系,确保同一个自然班在同一时段只有一门课要上,自然班不冲突,该班的所有学生均不会冲突。

其中999开头的课程id是虚拟id,实际数据中是不存在的,用它们表示合并后的课程。

定义课表类:

class Schedule:
    """课表类
    """
    def __init__(self, courseId, teacherId):

        self.courseId = courseId
        self.teacherId = teacherId
        self.classId = []
        self.liveTime = 0

    def random_init(self, liveTimeSize):
        """随机初始化
        参数:
            liveTimeSize: int, 可排课时段总数.
        """
        self.liveTime = np.random.randint(1, liveTimeSize + 1, 1)[0]

设计损失函数,计算冲突值并返回精英序号:

def schedule_cost(population, elite):
    """计算种群冲突

    声明:
        population: List, 种群类型为List,课表类
        elite: int, 种群中选取的精英数

    Returns:
        index of best result.
        best conflict score.
    """
    conflicts = []
    n = len(population[0])

    for p in population:
        conflict = 0
        #从第1门课开始,依次对比其后所有的课程。
        for i in range(0, n - 1):
            for j in range(i + 1, n):
                # 同一老师,同一时间,只能上一门课
                if p[i].teacherId == p[j].teacherId and p[i].liveTime == p[j].liveTime:
                    conflict += 1
                # 同一自然班,同一时间,只能上一门课
                if p[i].liveTime == p[j].liveTime:
                    #同一时间的课程安排,再做判断是否有自然班冲突。
                    """ 循环历遍效率不高
                    for cia in (p[i].classId):
                        for cib in (p[j].classId):
                            if cia == cib:
                                conflict += 1
                    """
                    # set()使两个list去重后取长,与两个list直接取长的和,做差即为冲突数。
                    conflict += len(p[i].classId)+len(p[j].classId)-len(list(set(p[i].classId+p[j].classId)))
        conflicts.append(conflict)

    index = np.array(conflicts).argsort()
    return index[: elite], conflicts[index[0]]

设计交叉函数、变异函数等就不帖了,后面提供下载。

实际结果是大概花了两三天的时间,写好的程序,又花了一个星期的时间进行各种调试和参数调优,最终排出了用6个时段完成200门课,几万名学生的课表编制工作,要知道学生的教学计划中课程最多就是六门,六个时段已经是理论上能排出的无冲突课表的极值。调试的这几天,手上的工作站辛苦了,每天晚上十几个小时都处于下面这种状态:

最后把程序和实际数据贴出来,分享一下,这是第一个版本,后期准备引入动态天灾机制和基因改造机制,使得程序能更高效地完成排课任务。具体的构想已经有了初步考虑,有机会再发一篇博客再细细道来吧。

程序源码及初始数据

https://download.csdn.net/download/xinew4712/13065633

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值