软件工程实践2017结对项目——第二次作业

成员

练斐弘 031502518
叶己峰 031502537

Github

传送门


问题描述

作业需求

编码实现一个部门与学生的智能匹配的程序。
提供输入包括:

  • 20个部门
  • 部门编号(唯一确定值),字符;
  • 各部门需要学生数的要求的上限,单个,数值,在[10,15]内;
  • 各部门的特点标签,多个(两个以上),字符;
  • 各部门的常规活动时间段,多个(两个以上),字符。
  • 300个学生
  • 学生编号(唯一确定值),字符;
  • 学生空闲时间段,多个(两个以上),字符;
  • 兴趣标签,多个(两个以上),字符(学生的兴趣标签一定是所有部门特点标签其中的一个)
  • 每个学生有不多于5个的部门意愿(助教测试时测试数据中部门意愿可能会出现空缺,非空缺的部分一定是部门编号中的一个,并按照优先级从高到底的顺序排序)。

实现一个智能自动分配算法,根据输入信息,输出部门和学生间的匹配信息(一个学生可以确认多个他所申请的部门,一个部门可以分配少于等于其要求的学生数的学生) 及 未被分配到学生的部门 和 未被部门选中的学生。


数据生成

最好的一组数据

传送门

过程

  在编码开始前,我们原计划所有数据均采用随机生成,如学生学号、部门编号、空闲时间单,通过string数组记下,取出符合条件的使用。然而在生成编号和时间段的时候,我们发现了两个问题:
  1、大量数据不符合要求
  2、无用的耗时占比大(需要判断是否丢弃重复数据)
  参考了网上的一些资料后,再结合我们的实际情况,最后决定采用枚举法 + 随机数rand() + bool数组 生成

考虑因素

  • 数据具有随机性
  • 数据具有时效性,如部门志愿不能出现无的情况
  • 数据具有不重复性,如一个人填的时间段不能有多个相同的

枚举数据

string all_tags[10] = {"reading","programming","film","English","music","dance","basketball","chess","running","swimming"};
string freeweek[7] = {"Mon.","Tues.","Wed.","Thurs.","Fri.","Sat.","Sun."};
string freetimes[8] = {"08:00~10:00","9:00~11:00","10:00~12:00","14:00~16~00","15:00~17:00","18:00~20:00","19:00~21:00","20:00~22:00"};
string all_department_no[20] = {"D001","D002", "D003", "D004", "D005","D006","D007","D008", "D009", "D010","D011","D012","D013","D014","D015","D016", "D017", "D018","D019","D020" };

使用如下条件生成数据

学生方面:

  • 编号:参考福大数计学院计算机系学生号码,格式为031502XXX
  • 空闲时间:两个小时为一个时间块划分,避免午休和饭点,化为8:0010:00,9:0011:00,14:001600,15:0017:00,18:0020:00,19:0021:00,20:0022:00共7个时间段,按时间块随机取2~7个
  • 部门选择意愿:从部门编号中随机选取5个以内,部门编号按题设要求为"D001,D002,D003...."
  • 兴趣标签:参考部门特色,选取了英语、编程、阅读、音乐等兴趣

部门方面:

  • 部门编号:按题设要求为"D001,D002,D003...."
  • 活动时间段:同样以两个小时为一个时间块划分,避免午休和饭点,化为8:0010:00,9:0011:00,14:001600,15:0017:00,18:0020:00,19:0021:00,20:0022:00共7个时间段
  • 特色标签:从标签池中选若干个,参考部门特色
  • 人数上限:参考部门实际需求,院级学生部门的人数区间在[10,15]范围中

数据建模及匹配程序的思路及实现方式

1、程序模型

主要由三部分组成:生成数据程序、匹配算法程序、json格式输入输出程序

2、数据模型

  • 学生模型:
    • 1)学号:按顺序生成
    • 2)空闲时段:从枚举的日期和时间段中组合,用bool数组标记
    • 3)空闲时段个数
    • 4)兴趣标签:从枚举的兴趣标签中挑选,用bool数组标记
    • 5)兴趣标签个数
    • 6)部门意愿:从枚举的部门意愿中挑选,用string数组记录
    • 7)部门意愿个数
    • 8)加入的部门个数
    • 9)在所意愿的部门中综合得分:计算方法在后面匹配算法中提到
  • 部门模型:
    • 1)部门编号:按顺序生成
    • 2)招收学生数的上限
    • 3)兴趣标签:从枚举的兴趣标签中挑选,用bool数组标记
    • 4)兴趣标签个数
    • 5)空闲时段:从枚举的日期和时间段中组合,用bool数组标记
    • 6)空闲时段个数

3、匹配程序的思路

    1. 先列出当初我们讨论的头脑风暴(注:由于我们算法能力不足,并没有完全解决所提出的问题)
    • a) 基于学生在每个意愿部门上的得分来进行分配,显然更加简洁明了
    • b) 计算学生对每个部门的相应得分要考虑: 这个部门在学生部门意愿优先度、 部门活动时间和学生空闲时间的重合度、部门标签和学生兴趣标签重合度、 当前学生已加入的部门个数(加入个数越多得分越低);
    • c) 当学生因为得分最低而被淘汰时,其得分全都要重新计算;
    • d) 当学生的一个空闲时间段和两个意愿部门的活动时间重叠时,则该时间段计算到意愿优先度高的部门;
    • e) 当学生的部门意愿为空或者学生一个部门意愿都没中选,考虑把学生分配给部门意愿外的部门;
    • f) 当学生的空闲时段和部门活动时段没有一个重合时,该学生的得分应该置0。
    1. 计算得分的规则:
    • 部门意愿优先度得分:第一意愿部门得50分,第二意愿部门得30分,第三、四、五意愿部门得10,7,3分。站在学生角度上,第一个意愿部门应该在得分中占较大比重。

    • 时间重叠度得分:用1小时来考虑,重叠1小时得40分,2小时得50分,3小时以上得55分,如果一次都没有分数置为0。学生必须得有时间来参加部门活动,所以时间重合度也应占较大比重,但同时学生也不要把太多时间用于部门,所以当时间重合超过3小时以上得分不变。

    • 标签重合度得分:重合一个标签相符得10分,标签越多,就越和部门志趣相投,所以成正比;

    1. 实现方法:
        该匹配算法目的为得到一个让学生和部门都尽量满意的分配方案。
        首先从学生的第一意愿部门出发,如果该部门招收学生人数未到上限,那么就中选;如果招收学生人数已达上限,那就得分和已招收的学生中最低得分比较,高于最低得分就取代最低得分的那个同学,低于最低得分那该第一意愿就废弃。
        同理,再从第二意愿出发... ...
        使用这种方式可以确保部门招收的同学都是排名前列的,同时同学的每个意愿都经过考虑不会有遗漏。
  • 4)结果分析:
      大体上看,其结果还是满足预期的。但是就是未得到分配的学生有点多,这是因为我们算法是根据学生的部门意愿来进行分配的,一旦300个人都选择热门部门,就一定会有大量的人落选,要想解决这个问题就需要改进算法,实现想法 e)考虑把学生分配给部门意愿外的部门。希望后面能有时间把想法都实现了。

有学生申请的部门的申请列表中,将学生先按时间符合排序,再按标签符合排序,取所有可取的,且少于部员数量限制的学生,加入 matched 数组,否则再将申请列表剩下的学生按时间符合排序,取所有可取的,且少于部员数量限制的学生,加入 matched 数组。


代码规范

  本次结对作业用github进行代码管理,两人统一使用叶己峰的账号进行提交。

主要遵循以下几个规范

  • 对变量名命名使用驼峰式(json数据的用下划线衔接单词),且变量名有意义,不乱取
  • if、for、do、while、case、switch、default等语句自占一行
  • 程序块采用缩进风格编写,缩进的空格数为4个
//部门结构体
struct Department
{
	string department_no;       //部门编号(唯一确定值),字符
	int member_limit;          //各部门需要学生数的要求的上限,单个,数值,在[10,15]内;
	int tags_num;			   //部门的特点标签个数 
	int tags[10];             //各部门的特点标签,多个(两个以上),字符;
	int event_schedules_num;     //各部门的常规活动时间段个数 
	int event_schedules[7][8];  //各部门的常规活动时间段,多个(两个以上),字符。	
};
//常规活动时间段
dep[i].event_schedules_num = unsigned(rand()%6) + 2;
for(int j = 0; j < dep[i].event_schedules_num; j++)
{
    int t1 = unsigned(rand()%7);
    int t2 = unsigned(rand()%8);
    if(dep[i].event_schedules[t1][t2]==1)
                j--;
         else
                dep[i].event_schedules[t1][t2] = 1;
}

结对感受

刚看到题目的时候,我意识到了这次作业的工作量远远大于前几次软工实践作业,然而完成效果却会比前几次的差很多,原因主要是国庆放假回家的缘故了,由于家里学习条件较差,主要工作都是在假期前和回到学校后完成的。我们在放假回家前先大致讨论过了思路和方案,两人的分工大致是,练斐弘负责json数据的读入和输出,叶己峰负责匹配算法,剩下的细节由两人共同讨论完成。我本以为关于json数据这部分会较轻松些,因为之前玩过前端,用javascript来处理json数据听方便的。没想到却碰上了大麻烦,在读入的问题上,卡了有两天时间,最初采用cjson来读取数据,可是编译器频频报错cjson的函数undefined,检查了几遍头文件的引入,都没找到问题,又上网找了资料,仍然无法解决,无奈弃坑,转用比较多人使用的jsoncpp,相对于cjson来说,jsoncpp函数的调用更简洁明了一些,然而在jsoncpp的配置中,还是碰到了一些坑,幸运的是,在网上找到了一份教程,也算是找到了个小捷径。这里,也要向队友表达谢意和歉意,叶己峰同学做事严谨认真,本来可以早些提交代码的,他还审核过了多次,debug了挺久,才得到我们最后更好的作品,而相比之下,我感觉我要学习和努力的还有更多。
本次结对作业虽然感觉完成情况不好,但是还是有些收获,主要如下几点

  • 看到报错信息,不要马上复制黏贴问百度,先翻译!自己解决!网上很多论坛的答案也不是正确的,盲目照改会出现很大问题
  • 编码规范还是很重要的,在没注释的情况下,看懂队友的代码是一件非常痛苦的事情,先树立规范,再编写代码,磨刀不误砍柴工。
  • 不同语言对于同样数据的处理是有很大区别的!比如json数据,不能盲目用老方法处理,否则会吃大亏。
posted @ 2017-10-09 21:41  Mr.who  阅读(305)  评论(4编辑  收藏  举报