1.结对成员
- 李嘉群 061500513
- 刘双玉 031502424
2.Github链接
3.数据生成
数据输入链接
数据生成程序的原理考虑的因素:
- 预先构造时间与标签字符串数组:
- 时间:一周七天,一天6段
8:00~10:00
10:00~12:00
14:00~16:00
16:00~18:00
18:00~20:00
20:00~22:00
共分为42个时间片 - 标签:多组标签,选取可能有联系标签放入同一组,保证每组内标签不会互斥,比如,积极与消极在一组。
- 时间:一周七天,一天6段
- 学生(意愿,时间,标签无重复)
- 学号:按6个班级,每班50人,从031502101到031502650
- 部门意愿:0~5 随机从部门编号中选取
- 空闲时间:10~14 随机从42个时间片中选取10~14个
- 标签:2~5 同组内选取
- 部门(时间,标签无重复)
- 编号:D001到D020
- 纳新人数:10~15随机
- 常规活动时间:23,考虑不会在短时间内有多次活动,选取时间有12天的间隔
- 标签:2~3 同组内选取
4.建模及匹配算法
(1)建模
处理过程分为输入数据处理(Input), 匹配(Match),匹配结果输出(Output)
-
输入处理模型:
- dealInput()函数:处理json格式的数据
- dealDepartments()函数:将处理好的部门数据放入创建好的部门数组中
- dealStudents()函数:将处理好的学生数据放入创建好的学生数组中
-
输出处理模型:
- getAdmitted()函数:获取被录取的学生学号和相应的部门号
- getUnluckyDepartment()函数:获取没有学生选取的部门号
- getUnluckStudent()函数:获取没有被部门录取的学生学号
将学生与部门抽象成类
- 学生
- 学号(String no)
- 兴趣标签(String[] tags)
- 所选的部门(String[] departments)
- 空闲时间(String[] freeTime)
- 处理后的空闲时间(int[][] dateTime)(将字符串转换为数字)
- 被录取的次数(int numAdmit)
- 部门
- 部门编号(String no)
- 限制人数(int memberLimit)
- 部门标签(String[] tags)
- 部门活动时间(String[] eventSchedules)
- 处理后的活动时间(int[][] dateTime)
- 剩余名额(int numRemaining)
(2)匹配算法的实现
考虑因素(重要性从高到低)
- 学生志愿优先级:第一志愿优先,其次第二,依此类推
- 学生空余时间和部门活动时间冲突:因为学生请假超六次会被淘汰,所以时间冲突匹配是没有余地的,时间冲突,部门就不录取。
- 学生已经被录取次数:录取次数少的同学优先。
- 学生与部门兴趣标签匹配度:兴趣标签匹配度高的同学优先
算法思想
具体说明
- 排序规则:学生权值 = 0.3 * (匹配标签个数/学生总标签个数) + 1 / (被录取次数 + 1),除以总标签个数是消除学生填报过多标签带来的优势,乘以0.3是因为,录取次数少带来的优势数值最大为1,标签匹配度也可能为1,乘以0.3再次削弱影响。
- 更新学生空闲时间:从学生空闲时间中将当前录取部门例会时间去除 。
5.代码规范
(1)代码规范:
命名:类名每个单词首字母大写,方法名首字母小写,其余单词首字母大写,常量名全大写,下划线分格,变量名第一个单词的首字母小写,其后单词的首字母大写。变量名不应以下划线或美元符号开头。
缩进:eclipse切换到下一行时自动产生。
分行:每个语句占用一行,比较清晰。
注释:简单的程序没注释,复杂的程序注释放在相应语句的上方。相应变量的注释放到右边。
(2)代码示例:
private void matchProcess(int index) {
//将报名情况填入Map
for(int j = 0; j < Input.stu.length; j++) {
//如果此轮志愿不为空
if(Input.stu[j].getDeptments().length > index) {
//取得学号与部门编号,如果二者时间不冲突,则加入map
String dept = Input.stu[j].getDeptments()[index];
String no = Input.stu[j].getNo();
if(isFreeTimeConflict(j,dept) == true){
map.get(dept).add(no);
}
}
}
//对时间不冲突的学生与部门,第二次筛选
for(int i = 0; i < Input.depa.length; i++) {
String dno = Input.depa[i].getNo();
ArrayList<String> tmp = map.get(dno);//报名学生学号List
int num = Input.depa[i].getNumRemaining();//部门剩余名额
//如果部门还可以录取
if(num > 0) {
/*
* 如果部门剩余名额小于报名人数,根据优先级排序,只取出前num个学生,全部录取
* 参数为:部门下标,学生学号List,部门剩余名额
*/
tmp = filter(i,tmp,num);
result.get(dno).addAll(tmp);
//对部门剩余名额,学生录取个数,学生空闲时间进行更新
int newNum = num - tmp.size();
Input.depa[i].setNumRemaining(newNum);
updateStuStatus(i, tmp);
}
}
}
6.结果评估
理想结果:
时间冲突的学生与部门一定不匹配,对于学生:不如意值(学生不冲突志愿个数 - 被录取次数)应该尽可能平均。对于部门:录取人数在不冲突志愿与部门录取上限取较小一个。
对测试数据的思考:如果数据太小,部门不冲突志愿申请总数小于部门人数限制,测试无意义,如果数据太大,一轮志愿部门就招满人了,测试也没有意义。
测试结果:为了计算方便,去除时间冲突,将部门例会时间设为无,每个志愿均有效,然后控制前150个学生标签匹配度为1,后150个学生标签匹配度为0.最后得出不如意值方差为1.6764888888888967。觉得可以了,其他复杂数据暂时不想测试。(溜了,溜了
7.结对感受
要先吐槽一下队友:放假就无情的抛弃了我,去玩了,我一直深深以为,不靠谱的只有我一个,没想到队友比我心还大,三号都没找我交流一下作业消息,反倒是K班一个13号交作业的人丧心病狂的天天催我。然后是向队友道歉,在选择语言的时候,没有选择双方都会的语言,然后作业后期很焦躁,乱发脾气。在最后快提交作业,转成.exe程序的时候,满脑子就想甩锅走人,也的确这样做了,但是走一会儿还是回来了,然后再走。
题外话:做事情还是要早开始,不能拖,拖着拖着就没有了斗志,“快到死线效率高”是“快到死线要求低”产生的错觉。