结对作业二
作业链接
0 结对成员
031502411 胡冰
031502324 林诗尧
1 git链接
2 生成数据
-
程序原理
-
手工设计一套标签(三组)与一套时间+随机random。
-
对于每个部分,比如生成空闲时间段标签,先随机生成的数据的个数,因为题目中对个数有一定要求,还需要对个数做出调整。确定个数后在对应数组里随机选择。
-
string freetime[6] = { "8:0010:00","10:0012:00","14:0016:00","16:0018:00","18:3020:30","20:3022:30" };
时间段生成先随机是7天中的某一天然后从数组中随机一个时间段。
- > string tag[3][8] = { { "tennis", "pingpong", "swimming", "climbing", "basketball", "soccer", "fishing", "running" },{ "movies","singing", "dancing", "acting", "reading","travel", "collect", "painting" },{ "PS","Linux","Android","iOS", "Web",".net","Java", "SQL" } }; 部门的tag先选取一个组别,然后在小组内选取,而学生tag可以在所有的标签中随机选取。
-
-
考虑因素
- 手工设计的标签中相近相容的放在一组内,部门的标签在同组标签中选择,以免太过跳脱。
- 同个对象不能生成重复的数据,随机时一定判重。
3 建模与算法
-
数学建模
- 假设学生最多有n个志愿。将匹配过程拆为n轮,每轮抽象为学生->部门一对一的匹配。分轮次充分兼顾志愿的优先级,单轮中考虑一种策略使得尽可能有多的学生至少被一个部门录取,减少unlucky数量。 -
匹配程序思路
- 匹配算法1.0
- 首先按照志愿的优先级分n轮进行。
- 单轮匹配时两种检查匹配策略。时间匹配要求学生的空闲时间段完全包含部门的常规活动时间。兴趣标签匹配按照匹配的个数优先级从高到低。学生按照这两个因素进行排序,排在前的先选择。
- 不考虑将unlucky学生调剂到他没有填报志愿的部门,不存在补录。
- 那对于收不到人的部门、进不了部门的学生我也很无奈啊。
- 存在的问题:极端情况。可能存在一些情况,该轮匹配时已经匹配多个部门的学生因为兴趣标签与部门匹配程度高,该学生先进行选择,这样不能实现让尽可能多的学生最终匹配到部门。
- 稍作改进算法2.0
- 单轮中学生先按照该轮志愿部门分组,同组内的学生根据评价函数排序。设计的评价函数考虑到三个因素,学生已经录取的部门的数量p,学生剩余可能被录取的部门的个数q和学生与该轮志愿部门标签匹配程度s。设计函数f(p,q,s)=1/(p+1)+1/(q+5)+0.15s。权值大的学生具有更高的优先选择权。
- 上面的评价函数怎么算的呢?我悄悄脑补了一下,f与p、q成反比,而我觉得q是个不确定的因素,不应该占太大的比例,0.15s是减少上面说的极端情况,尚未被部门录取的学生1/(p+1)=1,不容易被0.15*s反超,减少录取部门数量差距大的情况下因为标签匹配程度高而优先选择的可能。
- 我是将空闲时间段拆成区间来处理。version1.0只添加了将输入string数组初始转化为区间以及区间的合并,将空闲时间处理成不相交、孤立的区间处理。匹配问题转化为两坨区间是否为包含关系的判断。而其实学生被部门录取时,应动态修改学生的空闲时间,version2.0增加了区间的拆分,因为考虑一下得出减少学生空闲时间的操作最多只可能将一个区间拆成两个不相交的小区间。 -
题外话
- upd 10.12 冲锋赶了一波ddl, 顺畅写完各种调用函数后,调用编译运行gg,似乎因为用其他类的数组做成员变量的原因,改改改,最终用vector实现,改改改的过程中无意改错了变量,有的写法改变数据结构后存在问题。时间太短并没有对output_data的结果进行检查,被高匹配率所欺骗,产生了大bug而不自知。已经修复代码中存在的问题,重新封装生成数据的函数。
- 这次代码编写顺序与先前不同。先前是按着算法流程写,需要啥写啥。这次提前讨论了算法过程中需要做的事,我先封装了一些处理操作的函数,包括根据生成、去重、合并、拆分、判断匹配空闲时间段等,然后根据算法流程调用。但gg了,调用才发现错误,将常用的结构完全改变,改的匆忙写了bug。对c++的class似乎不够了解。
- 写代码时十分顺畅没啥问题,讲代码流程时却很绕,框架还好但实现有点复杂。
4 代码规范
- 函数采用驼峰法命名
void Student::getTimeslot(Student stu) {
prework.init();
stu.time_slot = prework.deal_tmie(free_size, free_time);
}
int Student::getTagMatch(int dep_tag_size,string *dep) {
return prework.tag_match_check(free_size,free_time,dep_tag_size,dep);
}
void Student::beAdmitted(int round,Timeslot dep) {
time_slot = prework.split(time_slot, dep);
++admitted_size;
is_accepted[round-1] = 1;
}
- 成员变量采用xx_xx的形式
class Student {
public:
int tag_size;
int free_size;
int dept_size;
int can_match;
int admitted_size;
int tag_match[105];
int round_dep;
double round_value;
string no;
string tag[15];
string free_time[55];
string department[7];
string addimited_dep[7];
int department_id[7];
int is_accepted[7];
Student() {
tag_size = 0, free_size = 0, dept_size = 0, admitted_size = 0, can_match = 0, round_dep = 0;
memset(tag_match, 0, sizeof tag_match);
memset(is_accepted, 0, sizeof is_accepted);
}
Timeslot time_slot;
void getTimeslot(Student stu);
int getTagMatch(int m, string *dep);
void beAdmitted(int id,Timeslot dep);
Prework prework;
};
- 宏定义,使用简洁,代码优雅
// student
#define Stu "students"
#define Sfree "free_time"
#define Sno "student_no"
#define Sdept "applications_department"
#define Stag "tags"
// department
#define Dept "departments"
#define Dsche "event_schedules"
#define Dno "department_no"
#define Dnum "member_limit"
#define Dtag "tags"
// print
#define Pstu "unlucky_student"
#define Padmt "admitted"
#define Padmt_mem "member"
#define Padmt_dept "department_no"
#define Pdept "unlucky_department"
5 结果评估
我认为结果评估与数据有很大的关系。我测算了助教随手给出的示例,匹配成功的学生接近50,而我检查数据后发现300位学生中和至少一个志愿部门时间段匹配的数量也才接近50,这么看匹配还是可以的。等待测算更多的数据。
6 结对感受
感谢队友的不杀之恩,因为各种事情,国庆前一两天解决json转换后到ddl前一两天才真正开始动手,未能正确估算解决时间,很快写完代码后却花了很长时间改正编译错误等,甚至最后代码隐带bug刚刚修复,感觉很对不起对友。debug能力需要增强,不要再二分注释代码+输出找bug。遇到奇奇怪怪的问题对友能很快查出解决方案,大概对友经常遇到奇奇怪怪的事情所以有很强的修复能力(233逃。交流间能互相弥补盲区,相互补充,发现问题迭代更新解决问题,思考更加全面。虽然我出现了失误,但还是很开心完成了一次结对。