zoukankan      html  css  js  c++  java
  • 结对编程作业--能不能起飞就靠这次了

    结对情况

    • cool_boy
    • 学号:437
    • 海辉
    • pig_pig
    • 学号:311
    • pig_link---press

    项目链接


    设计说明

    • 类图


    • API接口设计

    public MyC allocate 
    {
        public  void Myjson();//读入与处理json数据
        public  void FDmatching();//实现第一次分配
        public  void SDmatching();//实现第二次分配
        public  void TDmatching();//实现第三次分配
        public void  Prin();//输出
    }
    

    • 匹配算法的设计

    1. 流程图




    性能分析

    • 接下来已s5000-d100为例,先上图


    匹配思想

    • Step1:为了不让后生成的学生的优先级过低导致不能进入部门,首先以高考成绩换算成的评分(随机生成)排名,从高到低排名,给每个人一个rank排名。

    • Step2:在第一步的前提下,以第一志愿优先原则:从rank为1的学生开始,遍历其第一志愿,当第一志愿对应的部门的人数没有满的情况下,进行匹配判断,匹配判断分为以下两步:

    • Step 2.1 进行时间判断,将学生的空闲时间段与部门的基础活动时间段进行匹配,当两个时间段中有1个时间段互相匹配(同一时间段或者学生的空闲时间段大于部门的活动时间段),进行下一步判断。

    • Step 2.2 时间判断满足的情况下,进行标签匹配。将学生的兴趣标签与部门的标签进行一一对比,只要其中有两个两两匹配,即满足判断,更新学生信息与部门信息(学生:加入部门信息、加入部门数 部门:成员信息、部门人数、部门人数上限等)

    • Step3 对所有的学生,依旧按照成绩排名,从rank为1的学生开始,遍历其所有志愿,当其志愿对应的部门的人数没有满的情况下,进行匹配判断,匹配判断分为以下两步:同上

    • Step 3.1 进行时间判断,将学生的空闲时间段与部门的基础活动时间段进行匹配,当两个时间段中有1个时间段互相匹配(同一时间段或者学生的空闲时间段大于部门的活动时间段),进行下一步判断。

    • Step 3.2 时间判断满足的情况下,进行标签匹配。将学生的兴趣标签与部门的标签进行一一对比,只要其中有两个两两匹配,即满足判断,更新学生信息与部门信息(学生:加入部门信息、加入部门数 部门:成员信息、部门人数、部门人数上限等)

    • Step 4 最后一轮匹配,对所有的学生,依旧按照成绩排名,从rank为1的学生开始,遍历其所有志愿,当其志愿对应的部门的人数,且学生的已加入部门信息没有该部门没有满的情况下录取。


    测试数据如何生成

    部门部分

    • 部门名(department_name):部门名由事先设定好的字符串,通过随机数的组合进行随机生成,由于在下面学生的意向部门中需要用到部门名,因此先用一个字符串数组先存起来。

    • 人数上限(member_limit):单个,数值,在[0,15]内,这是题目要求,但是考虑到部门的实际情况,一个部门不可能纳新人数0人,而且数字比较小,也比较奇怪,因此我们加上底数限制,范围在[10,15]中,实现方法也比较简单,只要通过一个简单的随机程序,在这个区间内随意生成一个数字即可。

    • 部门特点标签(tags):首先我们预设9个标签,但是当我们数据测试的时候其实有考虑标签的匹配,如果总标签数少,这一层的匹配率相对来说太高,也就是标签匹配没有发挥作用,因此我们将其扩充到17个,经过测试,效果上有所改善,匹配率下降了2个百分点左右,表明标签匹配发挥了作用。我们的实现方法也比较简单,预取3个目标标签,但是要考虑重复部分,因此在0-16随机生成三个数,经过简单的if去重,得到三个不重复的随机数,作为数组标志,抓取标志。

    • 部门常规活动(schedule_time):最初的想法就是星期输与时间数的随机匹配,时间是控制在10点到23点。这是符合实际情况的,部门工作有一块很重要的内容就是值班,因此需要安排每个时间段都需要人值班,因此部门活动时间是一个跨度比较大的范围。实现方法也是,星期数是预存数组的随机,时间则是在10-21点随机一个数字,后一个是加上2,表示成字符串。

    学生部分

    • 学生姓名(student_name):原先采取和部门名一样的生成方法,生成一个乱码的字母组合来表示,后来可能觉得蛋疼,太蠢了!于是觉得做一点合乎常理的事,名字嘛,就该有正常的姓和名。于是做了三个姓和名的数组,全是将我们班级的人的姓,慢慢打入,还有所有名字拆成两个字,考虑到最终有5000个,因此每个数组分别20、35、38个,总数20 * 35 * 38 > 5000,即直接投入使用。当然会考虑到重复的问题,在下面的模块会简单阐述。

    • 学号(student_number):作为一个并没有很大影响的标识(因为在名字设立板块采取去重,即无重复名字,因此名字可作为唯一标识),学号这块就主要是0315+8位的随机数组成学号,0315表示数计学院15级学生,也是有良好的寓意的。

    • 兴趣标签(Interests):本块内容与部门部分的标签相似,不做累赘说明。

    • 绩点(gap):绩点这块实现难度上就不说了,比较简单,谈谈实际情况。由于福州大学今年绩点改革,因此我们决定跟上潮流,才取阶梯制加上十分制,比如87分、80分和85分的绩点都算作9,65分和60分绩点都算作6.0,也就是不再是一分一绩点,而是一段一绩点的形式。

    • 意向部门(intention_dep):这一块内容也比较简单,将部门生成名字时存入的数组进行调用,随机产生三个不同的该范围内的随机场,进行调取部门。

    • 可用时间(available_time):最初的想法和部门常规活动时间一样,最终改变的原因主要有两个,一个是匹配的问题,因为时间以段为单位,在时间生成上产生了11*7=77种可能性,再加上部门和学生的可用时间需要三个标签最终进行匹配,导致匹配率极低;第二个是实际情况,如工作日的10点-17点,很明显是属于学生的学习时间,不太可能将部门活动时间定在这个时间段,正常的可用活动时间段应该控制在工作日的晚上19点-23点,以及周末的一整天,实现方法是在部门的简单随机上,加了两个if的判断,即可实现。


    • 如何评价匹配算法

    咳咳,算法方面一定要自我检讨一下了,因为匹配算法是自己写的。算法只考虑了匹配过程的可执行性,在复杂度方面没有做过多的考虑,甚至出现了三层嵌套for循环的情况,结果就是导致在最大的5000students、100departments时,要30几秒才能完成运行,每次都等得自己要疯,队友要打人。对不起队友,对不起老师,对不起国家。。。。。。

    (attention)
    以上来自写匹配的队友,那我也评价一下,写最终的生成的时候,当时只留了一个5000的数据,每一次改完都是等待1分钟,要抓狂。但是经过问其他同学之后,也有部分同学的复杂度达到了n^3,想想心里平衡了一些。由于我们的匹配算法都是简单的暴力枚举,用最蠢的办法干最酷的事,这也是我们所想或者说能力范围内,当然很多优秀的算法仍然需要我们去学习,希望这个周期不要太长。


    关键代码解释

    • 随机生成部分
    const char CDH[] = "0123456789";
    string form_dnum()//生成部门编号
    {
    	char ch[nSIZE_CHAR + 1] = { 0 };
    	for (int i = 0; i < nSIZE_CHAR; ++i)
    	{
    		int x = rand() / (RAND_MAX / (sizeof(CDH) - 1));
    		ch[i] = CDH[x];
    	}
    	return ch;
    }
    

    这是一段普通的随机数生成程序,全是整个程序的基础,代码比较简单不做过多的复述,但是有个不同,

    int x = rand() % (sizeof(CCH) - 1); 
    

    这个方法不好, 因为许多随机数发生器的低位比特并不随机,虽然没有太理解这句话,但是听起来很厉害,于是采用上面那种方法。RAND MAX 在ANSI 里#define 在<stdlib.h>
    RAND MAX 是个常数, 它告诉你C库函数 rand() 的固定范围。
    详情点击此处

    • 学生名字生成部分
    void form_sname(int h)//生成学生姓名
    {
    	int j = 0;
    	map<string, int> mapStudent;
    	mapStudent.insert(pair<string, int>("ye haihui", 1));
    	for (int i = 2; i <= h; i++)
    	{
    		int y1 = rand() / (RAND_MAX / (20 - 1));
    		int y2 = rand() / (RAND_MAX / (35 - 1));
    		int y3 = rand() / (RAND_MAX / (38 - 1));
    		string new_name = name_part1[y1] + " " + [y2] + name_part3[y3];
    		map<int, string>::iterator it;
    		int w1 = mapStudent.count(new_name);
    		if (w1 == 0)
    		{
    			mapStudent.insert(pair<string, int>(new_name, i));
    			name[j++] = new_name;
    		}
    		else i--;
    	}
    }
    

    这部分的代码主要想说的是map类型的运用,看了很多大佬的hash相关的内容,自愧不如,本部分主要是用于名字生成的去重,先预存一个名字在map中,然后每次随机生成的名字用count进行计数,如果没有,就把这个名字存进去,否则继续随机。对于我来说是个不错的方法,其实仔细一想这个方法有很大的局限性,一个是效率问题,随着基数不断扩大,重复随机的概率将不断提高,假如只剩下一个名字,重复率将无限提升,以目前对map的运用,只能将名字库不断扩大,以增加概率,方法有些取巧,其实并没有从根本上解决这个问题。其次这个方法本质意义上也不叫去重,而是存入无重复的名字。另外曾经尝试过用find函数,可是老是不能成功,看来还需要多多努力。下次将map类型做更深入的理解,同时涉足哈希相关,小白表示压力很大。

    • 还有就是json,这个放在遇到的困难里详细谈一下。还有关于匹配算法,主要是队友完成,但是在筛选标准上两人还是进行深入的讨论。
    • 参博客考也是唯一可用的,对于我的不知道什么情况的vs2015。

    运行及测试结果展示

    • 测试500位同学,30个部门的情况
    • 测试1000位同学,50个部门的情况
    • 测试5000位同学,100个部门的情况
    • 输入输出

    *测试200位同学,20个部门的情况

    • s200-d20
      link
      本次匹配情况良好,所有部门都收到学生,并依据标签时间等严格条件筛选符合部门特色的学生,符合实际情况。


    • s500-d30
      link
    • 所有部门都收到学生,并依据标签时间等严格条件筛选符合部门特色的学生,符合实际情况。


    • s1000-d50
      link
    • 所有部门都收到学生,并依据标签时间等严格条件筛选符合部门特色的学生,符合实际情况。


    • s5000-d100
      link
    • 所有部门都收到学生,并依据标签时间等严格条件筛选符合部门特色的学生,符合实际情况。生成过程较久,但不影响匹配的成功率,匹配的算法描述上面已有描述。


    遇到的困难及解决方法

    作为生成部分,遇到的困难主要是json的安装、生成两块。

    • 困难描述
      --首先对json这个东西,以为和txt的输出一样,一直没太注意。某次无聊了百度了一下,咦?好像有那么点内容,再过了几天,准备开始做了,先动手吧,于是按着教程来,喵喵喵? 经过了两天的尝试,竟然毫无进展,当然时间是每天2-3小时的那种。主要问题是不知道问题在哪,整个毫无地方可以入手,三个头文件write、value、read竟然都无法用,一直报各种各样的问题,总之归结起来就是,json.h无法打开,即使打开也无法使用。

    • 尝试及解决--这个做了可就多了,不过后期看来并没有多少有意义,后来找到一篇听起来很有道理的博客,于是进行尝试,果真!成功了!他是采用一次性的方法,将头文件手动插入工程,然后json的头文件也是手动插入,相当于每次新建工程都需要从新配置一下,但是配置的过程并不复杂,无需太多精力,能力有限但是头脑简单的我当然选择这个办法了。

    • 关于json的理解,其实经过几段代码的实践很好理解,json的输出类似一棵树,先定义一个根节点与各节点,赋值的方式也比较直接,同时配合上数组的运用就可以彻底理解透json的输出格式,在json的读入更是符合这个思想,将json按规矩读入然后存到相应的类中。

    Json::Value partner_dep;//部门子节点;
    Json::Value root;//根节点
    Json::Value students;//学生结点
    Json::Value departments;//部门节点
    root["students"] = Json::Value(students);
    root["departments"] = Json::Value(departments);
    
    Json::Value dep;//部门子节点;	
    dep["department_num"] = Json::Value(form_dnum());//赋值
    
    root["departments"].append(dep);//数组赋值
    
    //输出到文件
    Json::StyledWriter sw;
    ofstream os("s5000-d100-in.json", ios::app);
    os << sw.write(root);
    os.close();
    

    主要的应用如上所示,相信能够很快理解。这也是这次结队编程对于我最大的收获了。同时这次编程有一个更大的收获就是发现自己在这段时间好像变了,对于代码没有原先的那种排斥的感觉,随着熟练度的上升,爱上了研究未知的类和函数,爱上了花一定量的时间专注编程,爱上了运行框上成功运行的快感,仿佛大门被打开了,现在每天都会有意识的去摸索,去编程。这次作业的雏形其实在10.8就完成了,当时以最紧的时间要求自己,后面的天本来想玩耍,但是某次看到自己的代码,真的太蠢了,毫无任何含金量,于是后面的几天开始行动了,将自己的每一部分都尽可能的完善,向舍友请教,有没有更洋气的办法,有没有更好的算法,一次次请教中,了解了map、hash等等用法,也入门级地探索了一波,仿佛有那么点的兴趣。也希望自己在一次次中,找到属于自己的那份热爱。


    对队友的评价

    (黄旭勿看,价值一顿饭)

    如果评价的时间在国庆放假内,那肯定就一个字--坑!2号就在摸索的我,5号他才来学校。来了之后他主要负责匹配的算法的设计,一天他就和我说设计好了,然后我就拿了一组数据给他跑。
    炸了!

    程序?

    不,还有vs!

    我的哥,8号下午我在陪他重装!

    心里头是******;

    某天夜里,1点了,我问他改得怎么样,他说还在改,准备睡了。

    ***我基本完成了我都还在完善,你还敢早我睡觉。

    那天我做到了两点。

    但是

    也不是都这样的不堪。10月8号以后如果要用一个字来评价,那应该是--切。

    如果用三个字--一般般。

    五个字--也算还好吧。

    后来的两天,不知道是怎的了,他仿佛醒悟了一番,进度特别快,一天后,就做出了一个成品,并且把匹配的算法自行优化了一遍。看着他努力,老夫总算叹了一口气。吾儿终成吾所愿,幸哉!

    但是,还没完。。。算了下次再说,毕竟是大作业队友嘛哈哈哈


    PSP

    PSP Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划 20 60
    · Estimate · 估计这个任务需要多少时间 600 600
    Development 开发 60 60
    · Analysis · 需求分析 (包括学习新技术) 20 200
    · Design Spec · 生成设计文档 20 30
    · Design Review · 设计复审 (和同事审核设计文档) 20 60
    · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 300
    · Design · 具体设计 120 60
    · Coding · 具体编码 500 600
    · Code Review · 代码复审 30 60
    · Test · 测试(自我测试,修改代码,提交修改) 30 100
    Reporting 报告 30 120
    · Test Report · 测试报告 30 30
    · Size Measurement · 计算工作量 40 60
    · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 20 30
    合计 1550 2070

    学习进度表

    第N周 新增代码 耗时(小时) 重要成长
    第一周 100 30 复习了vs2015,、github、回溯思想
    第二周 100 30 深入了解了github等用法
    第五周 500 30 掌握了json和map的相关用法
  • 相关阅读:
    51nod 1087 1 10 100 1000(找规律+递推+stl)
    51nod 1082 与7无关的数 (打表预处理)
    51 nod 1080 两个数的平方和
    1015 水仙花数(水题)
    51 nod 1003 阶乘后面0的数量
    51nod 1002 数塔取数问题
    51 nod 1001 数组中和等于K的数对
    51 nod 1081 子段求和
    51nod 1134 最长递增子序列 (O(nlogn)算法)
    51nod 1174 区间中最大的数(RMQ)
  • 原文地址:https://www.cnblogs.com/Dylan110/p/7673676.html
Copyright © 2011-2022 走看看