1.结对成员:
031502340 易伟航
031502312 黄阳正
2.项目的Github链接:
第二次结对作业
3.数据和"数据生成"程序的原理以及我们所考虑的因素:
生成数据:数据
数据生成原理以及考虑的因素:
数据生成就是依次获得需要的数据,然后输出就行了。
20个部门:
编号为D001到D020,一一对应;标签:
先自己赋值一个tags数组,然后部门和学生都直接通过生成随机数决定要从tags数组中得到几个标签;部门得到的随机数是sqrt(rand(0~80))+2,学生得到的随机数是rand(0~3)+2,这样不仅可以满足获得的标签数在两个以上,而且能让部门的标签更多一些,这样学生才更容易在部门中找到和自己匹配的标签;- 部门招收学生上限:
给部门随机生成一个上限值为10+sqrt(rand(0~35)),这样获得的随机数会让部门招收的上限分布在数值更大的区域更大,尽量避免因部门人员收满了而导致有的学生没有被招收的情况; 300个学生:
编号为031502001到031502300,一一对应;各部门的常规活动时间段:
在一个星期中生成至多3段不在同一天的长度为1~3的时间段,比如Mon.8:00~10:00,Tues.15:00~16:00;结合实际情况控制部门的常规活动时间的数量以及时间段长度比较少,也尽可能多的学生自己的空余时间段能成功匹配部门的活动时间;学生空余时间段:
在一个星期中生成从8点开始任意生成几段不一定连续的长度为1~4的空余时间段, 每一天基本上都能得到至少一段的空余时间段(一天得不到一个时间段的概率是3/7的13次方)部门意愿:
在已经编号好的部门中随机选择不超过5个的部门。
4.数据建模及匹配程序的思路及实现方式:
- 数据建模:
- department:
- string department_no:部门编号
- int numOfEvents:常规活动时间段个数
- int numOfTags:标签个数
- int memberLimit:部门限制招收人数
- int numOfMember:招收到的学生个数
- int schedules[300];:部门时间段(我们在Json读取文本时将时间转化成int类型)
- string tags[100]:标签
- student member[50]:招收到的学生
- student:
- string student_no:学生编号
- int numOfTags:标签个数
- int numOfApplyDepartments:申请的部门个数
- int numOfAcceptDepartments:招收的部门个数
- int spareTime[1000]:学生空余时间段
- string tags[100]:标签
- string applicationsDepartment[10]:申请的部门
- string acceptDepartment[10]:招收的部门
- department:
- 总体思路:
20个部门中第一个部门先遍历一遍学生,找到匹配的学生,如果匹配的学生有多个就招收最匹配的那一个,下一次轮到第一个部门遍历的时候就会跳过这个学生;第一个部门遍历结束后就轮到下一个部门,直到达到各自部门的招收学生上限(上限不一样,部门遍历学生的次数就不一样)。 - 匹配的原则:
首先,学生的空余时间段一定要包含部门的常规活动时间段,学生的部门意愿里必须有你这个部门,这是最基本的,否则要么部门在活动的时候你参与不了,要么就是你都没有报我这个部门,部门也是有尊严的,肯定果断拒绝;其次就是关乎学生部门意愿的优先级,比如5个部门,你把我这个部门放在第一意愿,那么部门就会先给你一个权重值10,第二意愿就是8,第三意愿6,依此类推;最后就是确定最后最匹配的那个学生了,这个时候就取匹配得上的学生的标签和部门的标签对比,有一个标签相同就给权重值加1,权重值最高的就是最匹配的学生。 - 实现方式:
void match()
{
for (int xx = 1; xx <= 15; xx++) //最多遍历15次
{
for (int i = 0; i < m; i++)
{
if (departments[i].getNumOfMember() >=departments[i].getMemberLimit() ) continue; //一旦达到部门的招收上限就会轮到下一个部门遍历
int numOfEvent = departments[i].getNumOfEvents(), numOfTags = departments[i].getNumOfTags();
int event[300];
mptags.clear();
for (int j = 1; j <= numOfEvent; j++) event[j] = departments[i].getKthEvent(j);
for (int j = 1; j <= numOfTags; j++) mptags[departments[i].getKthTags(j)] = 1;
node best;
best.id = -1; best.score = 0;
for (int j = 0; j < n; j++)
{
//if (i==1) cout << "i=" << i << " j=" << j << endl;
int flag2 = 0, score = 0;
for (int k = 1; k <= numOfEvent; k++)
{
if (students[j].judgeSpareTime(event[k]) != 1)
{
flag2 = 1;
//if (i==1) cout << j << " " << event[k] << endl;
break;
}
}
int numOfApply = students[j].getNumOfApplyDepartments();
for (int k = 1; k <= numOfApply; k++)
{
if (students[j].getKthApplications(k) == departments[i].getDepartment_no())
{
score += (12 - 2 * k);
break;
}
}
//cout << "i=" << i << " j=" << j << " flag2=" << flag2 << " vis[i][j]=" << vis[i][j] << " score=" << score << endl;
if (flag2 == 1 || vis[i][j] == 1 || score == 0) continue;
//cout << "hello" << endl;
int numOfTags = students[j].getNumOfTags();
for (int k = 1; k <= numOfTags; k++)
{
if (mptags[students[j].getKthTags(k)] == 1) score++;
}
if (score>=best.score)
{
best.score = score;
best.id = j;
}
}
//cout << best.id << " " << best.score << endl;
if (best.id != -1)
{
departments[i].addMember(students[best.id]);
students[best.id].addAcceptDepartments(departments[i].getDepartment_no());
vis[i][best.id] = 1;
for (int j = 1; j <= departments[i].getNumOfEvents(); j++)
{
students[best.id].updateSpareTime(departments[i].getKthEvent(j));
}
}
}
}
}
5.结对团队遵循的代码规范:
其实我们写代码的习惯还是差不多的,所以也没有特意规定什么代码的规范,只有在定义变量名或者类名之类的时候都遵循直接用英文翻译得到的名称,比如:
class department
{
private:
string department_no;
int numOfEvents, numOfTags, memberLimit, numOfMember, schedules[300];
string tags[100];
student member[50];
public:
department() {}
void init(string _department_no)
{
department_no = _department_no;
numOfEvents = 0;
numOfTags = 0;
memberLimit = 15;
numOfMember = 0;
}
}
6.结果评估:
我们直接使用我们自己的数据生成程序生成的input_data.txt进行测试。
- 测试结果:
未被录取的学生:154人;
未招到人的部门:0; - 分析:
对得到的这个分配结果觉得还是挺满意的。
我们设置配对成功的两个前提条件是时间以及意愿匹配得上,我觉得在针对部门与学生之间就只有数据之间的交流的情况下是比较合理的,但是事实上我们很确定实际情况下学生的空余时间段,而且可能部门与学生之间本来是会有时间或者意愿两个有冲突,但是在不仅仅看数据的情况下也许两者还能通过实际交流进行协调。而且我们在遍历的时候采用的是通过比较权重值决定招收的学生,这对部门来说是更有利的,他们可以招收更符合他们部门定制的标准的学生,也是因为这样,对于某些学生是不幸的,因为可能这个被淘汰的学生就没能加入任何一个部门。而且实际上要求学生的时间必须包含部门的常规活动时间这个成功匹配的概率并不高,所以我们在我们的数据生成程序中会刻意地将部门时间的段数控制在比较少且时间段长度比较短的情况下,再适当地增大学生获得更多空余时间段的概率,所以事实上当测试的数据严苛一点的时候,我觉得我们能匹配得上的学生及部门数量会明显下降。
7.结对感受:
这一次的结对作业完成得还是蛮坎坷的。首先就是在生成数据时比较难的就是要怎么处理时间这一项了,因为需要考虑具体的实际情况以及更容易地使部门和学生的时间段匹配上。后来又陷入了对Json这种格式的手足无措。主要是不理解Json的功能到底是什么,要怎么操作才能实现这个功能,要操作的代码是哪里,一无所知,主要是很茫然。后来在网上查了很多关于Json的资料,只是加深了对Json基础知识的理解,其实所有的资料都已经查到了,可是可能还是想不通。后来实在没办法,太耽误项目的进度了。在询问中慢慢摸清楚了整个Json的操作过程。就这样从配置Jsoncpp,而后在查到的资料中也慢慢懂了如何从已经生成的input_data.txt文本中读取信息。这一次的结对作业是抱着队友的大腿才能有惊无险地完成的,在Json上实在是耽误了太多的时间,我也感觉很抱歉,队友很强大,自己跟不上节奏。伟航很包容,很强大,能带节奏,我很幸运能与他合作并向他学习。我终究是太菜了,自学能力需要提高,编码能力也有很大的提高空间,我会继续努力的。在这一次的结对作业中还是有学到一些东西的,至少把一些以前已经忘了的知识点重新回顾了一遍,最重要也就是大概掌握了如何使用Jsoncpp读取和输出。