学生&部门智能匹配程序
Github链接
结对成员:
031502113 胡俊钦
数据生成
样例链接: 来点我啊
由于在生成数据的时候做了很多符合实际情况的限制,所以生成的数据都挺好的,就随便选了一组。
提供输入包括:
20个部门
部门编号(唯一确定值),字符;
各部门需要学生数的要求的上限,单个,数值,在[10,15]内;
各部门的特点标签,多个(两个以上),字符;
各部门的常规活动时间段,多个(两个以上),字符。
300个学生
学生编号(唯一确定值),字符;
学生空闲时间段,多个(两个以上),字符;
兴趣标签,多个(两个以上),字符(学生的兴趣标签一定是所有部门特点标签其中的一个)
每个学生有不多于5个的部门意愿(助教测试时测试数据中部门意愿可能会出现空缺,非空缺的部分一定是部门编号中的一个,并按照优先级从高到底的顺序排序)。
数据生成就是根据上面的要求一个个生成,具体如下:
- 部门部分
部门编号:部门编号先事先设定好,考虑到现实中的部门是固定的,就把部门编号固定为D001~D020,下面学生的意愿还要用到,所以用一个字符串数组存起来;
部门需要的学生数上限:由于限定范围在1015,所以就是直接随机产生1015的一个随机数;
部门的特点标签:事先设定21个标签,用数组存起来。要求至少输出两个标签,同时自己设定每个部门标签上限为4个,这是因为学生的平均兴趣标签一般是在5个以内,为了和学生标签数量相近,设定上限为4。输出时,先产生一个2~4的随机数,限定该部门标签数量,然后根据数量,随机产生标签的下标,根据下标到标签数组里面找到该标签并输出。同时,考虑到随机产生的下表可能重复,导致输出重复标签,又加了去重判断。
部门常规活动时间:这个的处理方式跟标签类似。考虑到实际情况,部门的活动时间大多是在“10:3011:30”,“16:3018:00”,“20:0021:30”三个时间段,所以我们先预设了21个时间段,一周7天,每天三个,共21个,并用数组存起来。而部门的活动时间一周也是在5个以内,所以设定部门的活动时间数为25。一样的,先产生一个随机数限定数量,然后随机产生下标,再输出随机的时间段。为避免重复,增加重复判断。
- 学生部分
学生编号: 300个学生数量多,而且现实中报名部门的学生本来就不是固定的,所以上面实现设定并存到数组的方式不再可取,就直接随机生成学生的编号。
学生空闲时间段:根据自己的课表预设“10:0012:00”,“16:0018:00”,“20:0022:00”三个空闲时间段,加上星期,一共也是21个时间段。学生的空闲时间段比部门活动时间多半个小时,这也比较符合实际情况,毕竟不可能所有空闲时间都在部门活动。然后就跟产生部门活动时间一样的方式随机产生学生的空闲时间段。至于每个学生的空闲时间段数量,上限定为21(平均水平是这样,至于胆大一周七天假的不在考虑范围内)。
学生标签:学生标签一定是部门标签里面的,所以直接用刚才的那个数组就好了,操作也一样,复制粘贴就搞定了。
部门意愿:每个学生有15个意愿,生成方法也大同小异。生成15的一个随机数限定数量,然后随机生成下标,到部门编号数组里面去找对应的部门编号进行输出。为了避免重复,加上重复判断。
- 输出格式
说到这个输出格式,简直了。一开始并不知道json,作业上的样例旁边的小小“json”字眼并没有引起我的注意,一直以为那是助教不小心写上去的。所以就简单粗暴的手动空格换行加引号。小心翼翼的写,就怕格式不对。后来看到隔壁班群有个人一直在问json的问题,我就想是不是作业有要求,点上去又看了一下作业,没看到json
的字眼(博客上面的作业没有,班级博客上的作业有“json”的字眼,orz),就理所当然的认为是隔壁班的特殊要求。同时,自己班群很安静,根本没人提到json这个词,也就心安了。直到后来,终于有人提到了,我又看了一眼作业(这回点开的是班级博客上的),终于找到了那个不起眼的“json”,就发现事情不太对。百度了一下json,才知道是怎么回事。我就觉得奇怪,这回的作业为什么输出都加了引号,顿时整个容恩都不好了。后来在群里问,说是这样手动加并不犯法,才安心了一点。(本来想改来着,但是C++好像还要加第三方库啊什么的,试了几遍失败了,时间又不是很够了,就放弃了。手动输出格式的渣渣向json大佬致敬~)
匹配算法
- 数据建模过程
如果说这是一种食品,我可以打包票,“纯手工制作,不添加任何防腐剂”。我们一开始并没有注意到json,是纯手工空格和缩进,所以数据建模过程也是纯手工模拟字符串读入。就是照着那个样例,一个个的模拟,简直是做到怀疑人生,动不动就缺点什么而导致出错,虽然耗了很多时间,但是不管怎样,最后还是完成了(都亏队友给力,orz)。实现的过程就是每次读入一个字符串,然后按照格式,去处理这个字符串,把它“剥离”出来,得到我们想要的数据。然后把部门和学生相关的信息用分别存在两个结构体中。总之就像剥茧抽丝那样,一点点抽出来然后存起来。
- 匹配过程
我们的匹配模式大体是参照高校招生的模式。我们匹配的优先顺序是:志愿、时间、兴趣标签、已经参加的部门个数、学号(学好小的人优先)。制定优先顺序解释如下:首先,部门要选学生,显然要学生有报这个部门,又要尽可能的满足学生的意愿,所以就第一志愿优先;然后,尽管学生报名了,但是时间不允许,部门的活动时间都不能参加,那也不能收。第三个考虑的就是学生本身的兴趣,最后最后就直接以学号录取(这个是考虑到到后面有一些同等条件的少数同学,以学号录取,运气也是实力的一部分\偷笑)。
匹配的过程是:先一层循环,一个个部门扫过去,把第一志愿报这个部门的学生挑出来,删除其中没时间的人。关于是否有时间,经过讨论,我们最终采取的策略是,只要该同学能够参加一半以上的活动就好,而且时间段不一定是要完全对的上,允许一点偏差。比如说,活动时间是10:0012:00,并不是说学生的空闲时间段一定要完全包含这个时间段,如果空闲时间段是在10:0011:40,也是允许的,毕竟部门活动有事提前走也是允许的,同理,适当的迟到也是允许的。当然,时间能够完全匹配显然会优先录取。然后就是把这些学生根据标签和学号(从小到大)进行排序,最后按优先级一个个录取,人满为止,不满就全部录取。但是这样会有一种情况,那就是学生空闲时间满足,但是他加入的部门之间存在相同的活动时间,一个人显然不可能在同一时间参加两个不同的活动,但是他也会被两个部门全部录取。所以后来就采取学生的时间设为动态的策略,就是学生被一个部门录取后,这个学生的空闲时间要减掉部门中的活动时间段,这样就避免了这种情况的发生。对于unlucky的学生和部门,就是排除法,先各自存在一个set里,学生被选了或部门加入了一个学生,就把他们删除,最后剩下的就输出为unlucky的。至此,整个过程就结束了。
结果评估
样例链接: 你再点一次试试
从数据结果来看,一共300个学生,20个部门,其中101个学生没有被部门选上,199个人加入了部门,部门总容量251,实际收人251,部门全部收满人,三分之二的学生被录取,达到所有部门全部收满人的预期,这也符合实际情况,对匹配的结果还是较满意的。
代码规范
对于代码规范,我们主要做了如下约定:
- 花括号另起一行,不要接在if和for语句后面。
- if和for逻辑块只有一个语句也要花括号括起来。
- 变量及函数命名采用小驼峰命名方式,除了第一个单词外,所有单词首字母大写。
- 类名采用大驼峰方式,所有单词首字母大写。
-
命名尽量能够见名知意,杜绝拼音。
-
除了临时变量外,不出现单字符变量。
-
不同逻辑块之间,函数与函数之间要留空行。
- 对函数功能和重要变量以及复杂的逻辑代码块要有注释
总结&感受
- 结对作业的感受
上一次的结对作业仅仅只是设计,并没有打代码,所以体验就是分工省时省事高效。这一次的结对需要编程实现,也就是需要打代码。那就产生了很多的不同,特别是当结对遇到国庆,就只能叫伪结对,并不是栋哥所说的那样。像栋哥说的那样,一个人在打,另一个人在旁边看着,过一段时间交换着来,这种方式没有试过,不好说感受,但我觉得打代码的时候旁边有一个人盯着应该会很尴尬吧,我怕是一个字也打不出来。就说说我们国庆的伪结对的感受吧。
遇到国庆,不知道别人怎么样,反正我是回家了,作业只能线上交流了(给QQ点个赞)。这便有一个问题,那就是可能队友发的消息,我一段时间后才能回复,然后他等回复没等到就继续做别的事情去了,等我回复的时候他也没有马上看到,如此这般,拖得时间就会比较久。按道理来说放假期间,时间会更多才对,但是回家就会完全打乱这一定律。不过还好,争做一名合格的夜猫子是我们都已经具备的良好品质,所以,晚上是讨论的好时机(白天琐事真的多,而且我在家很少把手机带在身上,经常看不到消息\流汗),但终究还是会对进度有所影响。如果是独立项目,显然就不存在这种问题。此外,结对作业还有一点很重要,那就是代码规范。这个跟独立项目很是不一样。独立项目可以随心所欲,开心就好。当然也要注意规范,但是不规范的话对于作业来说影响不大。这个就不一样了,这个没有事先商量好规范的话,就会有种两个人在对话,但是一个讲日语,一个讲韩语的感觉,影响就比较大了。总之,结对作业我觉得缺陷就是没有做独立项目来的自由,不管是时间上还是代码风格上。当然,这都是小问题,相比于结对带来的好处,简直是微不知足道。
这次的结对编程,对于“双拳难敌四手”、“三个臭皮匠顶个诸葛亮”之类的俗语,有了刻骨铭心的认识。独立项目是自由一点,但是,终究还是只有一个人,不管从量上还是质上,都处于劣势。就说找bug吧,四只眼睛总比两只看得容易吧,更何况找自己的bug更是难,特别是那些打错了之类的,更是找到怀疑人生。比如我把星期三的英文写成“wen.”,让我自己找,永远也看不出来,但是付少火眼金睛就看出啦。同时,两个人的思维肯定比一个人要严密,特别是经过讨论,这绝不是1+1=2,思维的碰撞产生的是很多小火花,特别是跟大佬合作,简直是烟花啊(先偷偷开心一下,下面再开心好几下\嘚瑟)。在匹配算法这一块,讨论出来的情况比一个人单独想要严密得多,这就是结对的好处。我想结对作业想让我们体会到的应该也是1+1>>2吧。上面讲的是结对作业的感受,但是结对作业不同的人会有不同的感受,跟不同的人结对也会有不同的感受,下面我就讲讲自己跟付少结对的感受。
- 跟大佬结对的感受
大家好,给大家介绍一下,这是我的结对队友@付逸豪。
付少,在结对过程中,给我的感觉就是一个字——稳,那种感觉就是打英雄联盟的时候眼看自己的塔就要被推掉了,但是队友就只是一句“稳住,我们能赢,”给人的感觉就是那种特别可靠特别厉害特别稳的那种。在讨论过程中,他的思维和考虑问题的角度以及严谨性,都是非常非常的棒。每一次我都觉得这应该够全面了,但是一会他就又会提出新的情况,或许,这就是大佬。打代码的时候,他惊人的手速就像是一位钢琴大师在弹奏绝世佳曲,配合着键盘的敲击声,一股大佬的气场悄然形成。从付少身上,我学到了很多。他会在代码中加入很多的测试函数,用以调试输出,这是个好习惯,我也要向他学习。想自己以前都是一股脑把整个程序写下来,然后运行时出现了bug,要回去找bug修改,却发现代码太长,以至于根本不知道从何找起,简直是大海捞针,甚是痛苦。然后我还学到了一点,就是在freopen之后,cin就是从文件中读取,并不需要用键盘输入。除此之外,还get到了“由面到点”的思想。就是说,在程序设计时,可以先把一个大的框架想好,具体细节再慢慢一点一点的去完善,并不需要一步到位,直接就从细节开始处理,这也是我以前做的不好的地方。以前,对于整个程序缺少一个大局观,都是“以点成面”,上来就直接写细节,然后拼成一个面,往往会出现很多的问题。还有就是,前面有说到,我们的输出格式不是用json库,是手动加空格。生成数据的时候还好,但是到了要建模读入数据的时候,就很麻烦,什么都要手工去做,需要极大的耐心和细心,要是没有付少,我基本是要多花好几倍的时间。当然,作为一个大佬,付少还是很接地气的,这也使合作进行得非常顺利(听到同学说自己的结对作业基本是一个人在做,就为自己感到庆幸,让我再开心好几下\调皮)。总之我给付少5星好评,这次真是捡到宝了。