结对第二次作业
结对成员
[周琪文 031502642](http://www.cnblogs.com/qiwenzhou/) [张旗 031502243](http://www.cnblogs.com/neveslalala/)
Github链接
[点击这里进入github](https://github.com/qiwenzhou/match)
最“好”数据&原理以及所考虑的因素(张旗)
最“好”数据
[github的BIN目录中哦](https://github.com/qiwenzhou/match/blob/master/BIN/input_data.txt)"数据生成"程序的原理以及所考虑的因素
生成原理
这里以生成部门标签为例,先构建一个存放所有标签的数组tags[9],存放"film", "English", "reading", "music", "dance", "basketball", "chess",部门随机生成2~5(tags_cnt = rand() % 4 + 2)个标签(需考虑去重),在for循环中随机生成tags_num = rand() % 10(去重),就是部门标签,以此类推。考虑因素
1. freetime 生成时间有两种,考虑到上课时间冲突,常规上课时间少生成一点,非上课时间多生成一点,时长都是两个小时。 2. 标签随意生成 2-5个随机,不重复,从总的标签集合(数组)中取。 3. 意向部门0-5个随机,不重复,意向部门编号从总的集合(数组)中取。 4. 部门收的人数个数memberlimit在8-16个。 5. 生成有空时间没有个数限制,生成时的规律是一定概率(上课时间为20%,非上课时间为80%)向下生成,连续生成的概率减少10%到40%。 一天二十四个小时分成12个时间片,七天生成84个片生成空闲时间片。 6. 学号按照一定规则排列,以我们学院为主(031502xxx),不重复。数据建模及匹配程序的思路及实现方式(周琪文所做)
>提供输入包括: 20个部门 部门编号(唯一确定值),字符; 各部门需要学生数的要求的上限,单个,数值,在[10,15]内; 各部门的特点标签,多个(两个以上),字符; 各部门的常规活动时间段,多个(两个以上),字符。 300个学生 学生编号(唯一确定值),字符; 学生空闲时间段,多个(两个以上),字符; 兴趣标签,多个(两个以上),字符(学生的兴趣标签一定是所有部门特点标签其中的一个) 每个学生有不多于5个的部门意愿(助教测试时测试数据中部门意愿可能会出现空缺,非空缺的部分一定是部门编号中的一个,并按照优先级从高到底的顺序排序)。 实现一个智能自动分配算法,根据输入信息,输出部门和学生间的匹配信息(一个学生可以确认多个他所申请的部门,一个部门可以分配少于等于其要求的学生数的学生) 及 未被分配到学生的部门 和 未被部门选中的学生。
建模:使用不定长向量vector。先读取文件,把文件转化成数组形式,每一个同学用同一个下标表示,并封装。
vector<vector<string>> studentsTime; //学生空闲时间
vector<vector<string>> studentsApp; //学生的申请部门
vector<vector<string>> studentsID; //学生学号
vector<vector<string>> studentsTags; //学生兴趣标签
vector<vector<string>> deptNo; //部门编号
vector<vector<string>> deptTags; // 部门标签
vector<vector<string>> deptEvent; //部门常规时间
vector<vector<int>> deptMumLimit; //部门人数限制
建三张搜索表,分别是: - 标签到部门下标的字典。 - 时间到部门下标的字典。 - 部门No到部门下标的字典。 例如,标签到部门下标的字典( map < string,set < int > > ): - map中string 是特征标签。 - set是集合,存包含标签的部门的下标,用整型表示。
tags | 含有该tag的部门 |
---|---|
film | 实践外联部 |
dance | 文化部 |
basketball | 体育部 |
在匹配时,取出学生的标签对应的部门集合,和学生申请的部门集合,进行交运算,得到set1,再把set1,和空闲时间对应的部门集合进行交运算,得到最后的集合。 找到符合个人兴趣和时间的集合后,再检查是否已加入,重复,无法加入(已加入五个部门),部门已达到人数上限等问题。
具体代码
读取文件并存储
/*读入部门数据,并存储*/
for (int i = 0; jv["departments"][i].asBool(); i++)
{
//ID
vector<string> tID;
tID.push_back(jv["departments"][i]["department_no"].asString()); //读入部门号并放入vector中
dict_no2index.insert(pair<string, int>(jv["departments"][i]["department_no"].asString(), i));//将vector放入容器中,形成二维数字
//同样的方法读入时间段,标签,和人数限制,同样用vector组成的二维数组保存起来
deptNo.push_back(tID);
//time
vector<string> tTime;
for (int j = 0; jv["departments"][i]["event_schedules"][j].asBool(); j++)
{
//string ss = "\"" + jv["departments"][i]["event_schedules"][j].asString() + "\"";
tTime.push_back(jv["departments"][i]["event_schedules"][j].asString());
}
deptEvent.push_back(tTime);
//tags
vector<string> tTags;
for (int j = 0; jv["departments"][i]["tags"][j].asBool(); j++)
{
tTags.push_back(jv["departments"][i]["tags"][j].asString());
}
deptTags.push_back(tTags);
/[表情]mit
vector<int> tLimit;
tLimit.push_back(jv["departments"][i]["member_limit"].asInt());
deptMumLimit.push_back(tLimit);
}
构造字典
map<string, set<int>> dict_time; //建造时间段->部门下标的字典
for(int i = 0;i < deptEvent.size();i++) //遍历部门
{
for(int j = 0;j < deptEvent[i].size();j++) //遍历部门的时间段
{
if(dict_time.count(deptEvent[i][j]) == 0) //如果时间段没有在这个字典中
{
set<int> frees; //添加字典项
frees.insert(i);
dict_time.insert(pair<string, set<int>>(deptEvent[i][j],frees));
}
else
{
dict_time[deptEvent[i][j]].insert(i); //如果时间段已存在,就添加到对应的集合中
}
}
}
利用集合的交运算进行匹配(截取标签匹配的部门代码,说明程序匹配的原理)
for (int i = 0; i < studentsID.size(); i++) //取出一个学生
{
set<int> apps;
set<int> apps2;
for (int j = 0; j < studentsApp[i].size(); j++) //获得该学生的意向部门集合
{
//cout << studentsApp[i][j] << " ";
//cout << dict_no2index[studentsApp[i][j]] << " " << endl;
apps.insert(dict_no2index[studentsApp[i][j]]);
}
for (int j = 0; j < studentsTags[i].size(); j++) //进行标签匹配,取出学生的一个标签
{
set<int>::iterator it;
set<int> tags = dict_tags[studentsTags[i][j]]; //根据字典找到该标签对应的社团集合
/*cout << "tags ";
for (it = tags.begin(); it != tags.end(); it++)
{
cout << *it << " ";
}
cout << endl;
cout << "apps ";
for (it = apps.begin(); it != apps.end(); it++)
{
cout << *it << " ";
}
cout << endl;*/
set<int>::iterator it1;
set<int>::iterator it2;
for (it1 = apps.begin(); it1 != apps.end(); it1++) //进行循环,完成集合交运算
{
bool remove = true;;
for (it2 = tags.begin(); it2 != tags.end(); it2++)
{
if (*it1 == *it2)
{
remove = false;
break;
}
}
if (!remove)
{
apps2.insert(*it1);
}
}
代码规范
1. 使用空行来分割逻辑,易于解读。 - ![](http://images2017.cnblogs.com/blog/885599/201710/885599-20171009191542293-1270816512.png) 2. 使用注释和花括号——上下对齐式。 - ![](http://images2017.cnblogs.com/blog/885599/201710/885599-20171009200144043-954590076.png) 3. 不使用的代码暂且注释,因为工程不大,所以这里先注释掉,并且注明注释原因。若项目庞大,则考虑使用git,来保存旧版本,便于回滚。 - ![](http://images2017.cnblogs.com/blog/885599/201710/885599-20171009191553199-525072470.png) 4. 不用中文拼音做变量名。字段名全部使用中文拼音缩写,令人费解,而且非常别扭。对变量赋予实际英语意义,便于理解。 - ![](http://images2017.cnblogs.com/blog/885599/201710/885599-20171008171526731-2053027893.png)
结果评估
根据匹配的结果,对自己生成的数据,没有学生匹配到的个数在10个左右,一般没有部门收不到人。 对这样的结果不太满意具体如下: 1. 在匹配时对个人之间的优先级没有进行约束,是先到先进。 2. 在针对时间匹配上的处理需要优化,例如,针对部门时间14:00~16:00,个人空闲时间14:00~17:00并不会匹配。 3. 在数据结构上,对数据的匹配速度上还不是很满意。 问题出在处理的规则太简单,目前想到的解决方法与优化为: 1. 可以将第一个填写的志愿部门作为第一志愿,优先级最高,以此类推,但是由于水平与时间有限,会在以后完善。 2. 可以将数字进行大小比较,目前在程序中还未实现,会在以后完善。 3. 程序还需要进一步优化。 4. 还需要学会用github回退到以前版本,这样可以减少大量冗余注释。
结对感受
###主要的分工是:张旗负责生成函数,周琪文负责匹配。 在结对编程的体验中,感觉到和之前个人编程最大的不同就是对于编程要求的冲突,这个要求是各个方面的,比如说编程的进度,对项目的不同理解,匹配的规则的处理等,在个人编程中,所有的事情虽然是一个人做,虽然繁琐,但是自由,但是在结对项目中,虽然两个人比一个人的编程能完成更大的工作量,但是也可能因为冲突而导致进度的滞后,(终于知道了为什么老师说不要和自己熟悉的人合作了)。 结对的好处显而易见,就是能做到并行的开发,加快了项目的进度,但是这样做也带来一些问题,就是在最开始两个人没有对项目的细节有同一的认识的话,到了项目整合阶段,会付出巨大的时间和精力去和对方进行协调,反而不如一个人编程来得快。因此在项目开始阶段就要做好沟通,针对本次合作,做好生成程序和匹配程序的衔接。
沟通!沟通!沟通!如果两个人没有一个同一的认识,只会事倍功半! 在结对编程的过程中,耐性也至关重要。在编程过程中,遇到bug是常有的事,如果这时变得烦躁而使得两个人之间出现了隔阂,不仅是影响了沟通,进而降低了项目的质量,而且这样的合作最终一定会以不愉快结束。