• 第六次作业--结对编程第二次


    【结对情况】

    • 530雨勤 – 随机数据生成模块,json相关代码
    • 501安琪 – 匹配算法模块

    【各种传送门】

    【设计说明】

    • 接口设计(API)

    friend void student_from_json(const json& j, Student& student);
    friend void department_from_json(const json& j, Department& department);
    friend void read(string file_name, DistributeSystem& ds);
    friend void matched_student_to_json(const Student& student);
    friend void standalone_student_to_json(const Student& student);
    friend void matched_department_to_json(const Department& department);
    friend void standalone_department_to_json(const Department& department);

    • 内部实现设计(类图)

    • 代码规范

    • 命名规范

      • 类与结构体:第一个大写字母开头,后面跟随的字母全部用小写。
      • 变量:所有字母均小写,使用下划线“_”作为单词的分隔。
      • 指针: '*' 应靠近类型,而不是变量名。
      • 引用: '&' 应靠近类型,而不是变量名。
    • 排版规范

      • 方法和函数的布局 :对于有较多参数的函数的写法,如果参数较多,一行写不下,我们应该分成几行来写,并且每个参数都另起一行对齐。
      • 缩进:缩进的时候,每一层缩进4个空格;不要使用TAB,用空格。
      • 行规则:保证一行只写一条语句,一行只定义一个变量。
      • 花括号{}规则:括号的左半边与关键字同行,右边换行,并且与关键字对齐。
      • 所有的 if, while 和 do 语句,要么用单行格式,要么使用花括号格式。
      • 圆括号 () 规则:圆括号与关键字之间应放一个空格; 圆括号与函数名之间不要有空格。
      • Continue 和break: Continue 和break 实际上起到与goto一样的作用,因此,尽量少用为上。并且,Continue与break最好不要连用。
      • 运算符号的规则 : 一元操作符如(!、~ 等等)应贴近操作对象; 二元操作符如(+、*、%、== 等等)应在前后留空格;++ 和 -- 尽量使用前置运算。
      • 变量声明语句块: 变量应该是随用随声明,不要集中在函数前,例如for语句的循环变量,应只在for语句中定义。
    • 注释

      • Include 语句注释:如果有必要,#include语句也应有注释,它可以告诉我们,为什么要包含这个头文件。
      • 语句块注释:尽量用代码代替注释,如果决定用注释,避免喃喃自语或含糊不明的注释。
    • 编码要求

      • 不要忽略编译器的警告:编译器的警告,通常能够指示出编码存在的笔误或逻辑错误。因此,不能轻视编译器的任何警告,准确来说是,不允许代码在编译时产生任何警告信息。
      • 初始化所有的变量:无论如何,都要初始化所有的变量。我们无法保证编译器会给个什么样的初值。
      • 保持函数短小精悍:一般情况下,一个函数最好在一个屏幕内,不要超过三个屏幕。
    • 测试数据生成

    • 匹配算法设计(思想/流程)

      • 分配原则及思想:

        • 一看志愿。学生待分配队列先按照志愿顺序进行排序,分配时,第一志愿优于二、三、四、五志愿,以此类推;
        • 二看最低绩点要求。部门提出最低绩点要求,以此作为进入部门的敲门砖,当且仅当学生满足此条件,才考虑条件3 ——行程时间
        • 三看行程时间。学生满足最低绩点要求后,通过匹配其与志愿部门的活动时间,查看是否有冲突。当行程时间无冲突时,有两种情况,一种是该部门未满,则学生就可以入选该志愿部门;另一种是该部门已满,则考虑条件4 ——标签类似数;
        • 四看标签类似数。当部门已满,通过学生与部门间的标签类似数进行重筛选,决定去留。
        • 算法流程图:
    • 匹配算法评价

    • 该算法是我们一起商量后决定实现的,较为贴切现实情况并且简洁易于理解的。有考虑过通过各部分占比进行权衡统计再分配,但最终得出的结论是并不符合现实情况,例如现实中部门对于学生成绩的要求,一般是给个最低的标准,防止部分学生因为部门活动影响学生,因此没有必要按照绩点高低进行排序筛选。我们一致认为,在匹配中最重要就是<有志愿——绩点满足要求——时间无冲突>这三个部分,至于其他的都是在这些的基础上进行拓展,因此,我们认为我们的匹配算法还是较为合理的。但是,设想都是特别美好的,当我们开始跑时,我们发现,匹配率简直低啊,居然不到一半,考虑到有一下几个原因:第一,有部分部门的纳新人数为零;第二,有部分学生的志愿只有1个;第三,匹配算法考虑的还不够周到。虽然说匹配率并不能反映一切,但是这么低也不正常,我反思。

    【关键代码】

    /*根据分配原则进行学生-部门分配*/
    void DistributeSystem::distribute() 
    {
    	queue<Student> student_que; //待分配的学生队列
    	for (int i = 0; i < student_number; ++i) 
    		student_que.push(stu[i]); // 初始都是未分配状态,都加进队列
    
    	while (!student_que.empty()) 
    	{
        	int ss = student_que.front().student_no;
    		int sss = get_student_index(ss);
        	Student& s = stu[sss];   //访问学生队列队首元素
        	student_que.pop();
    
    		// 第一步考虑学生s的第student_cur个志愿(部门为d)
    		int ww = s.student_cur;
    		int www = s.student_wish[ww];
    		int wwww = get_department_index(www);
    		Department& d = dep[wwww];   //访问部门队列队首元素
    
    		if (s.student_score >= d.department_score_limit)
    		{
    			//第二步考虑学生与部门时间是否有冲突
    			int same_schedule_num = 0;
    			for (int i = 0; i < s.schedule_count; ++i)
    			{
    				for (int j = 0; j < d.schedule_count; ++j)
    					if (s.student_schedule[i] == d.department_schedule[j])
    						same_schedule_num++;
    			}
    >
    			if (same_schedule_num > 0)
    			{
    				s.student_cur++;
    				if (s.student_cur < s.wish_count)
    				{
    					// 如果五个志愿还没考虑完毕的话,放入队列中继续参与分配
    					student_que.push(s);
    				}
    			}
    			else  
    			{
    				//若时间无冲突
    				//第三步考虑部门是否已收满
    				if (d.department_member_limit > d.chosen_num)
    				{
    					// 如果部门d还有剩余名额,直接中选
    					int ss = 0;
    					ss = s.student_no;
    					int dd = d.chosen_num;
    					d.student_no[dd] = ss;
    					int ddd = s.chosen_num;
    					s.department_no[ddd] = d.department_no;
    					s.chosen_num++;
    					s.student_cur++;
    					d.chosen_num++;
    					if (s.student_cur < s.wish_count)
    					{
    						// 如果五个志愿还没考虑完毕的话,放入队列中继续参与分配
    						student_que.push(s);
    					}					
    				}  //未收満
    				//若已收满,第四步考虑标签类似数
    				//先找到部门d<已中选学生中>标签类似数最少的学生s'
    				//将学生s的标签类似数和s’最少标签数的进行对比
    				else     
    				{
    					//若部门无空余名额
    					int least_stu_no = -1; // 部门d<已中选学生中>标签类似数最少的学生s’编号
    					int pos = -1;  // 以及s'在部门的<已中选列表中>的下标
    					int stu_pos = -1;  //s'在<学生数组>中的下标
    					int less_stu_dep_pos = -1; //部门d在s'的<已中选部门数组>中的下标
    					int least_tag_same_num = 10;  //最少的标签类似数
    					s.now_tag_same_num = get_student_now_tag_same_num(s.student_no, d.department_no);  //学生s与部门d的标签类似数
    					for (int i = 0; i < d.chosen_num; ++i)
    					{
    						// 在部门d中查找标签类似数最少的学生编号
    						stu_pos = get_student_index(d.student_no[i]);   //部门已中选学生在<学生>数组的下标
    						Student dmp = stu[stu_pos];  //对应的已中选学生
    						less_stu_dep_pos = get_stu_department_index(stu_pos, dmp.chosen_num, d.department_no);
    
        					if (least_tag_same_num > dmp.tag_same_num[less_stu_dep_pos])
        					{
        						least_tag_same_num = dmp.tag_same_num[less_stu_dep_pos];
        						least_stu_no = dmp.student_no;
        						pos = i;
        					}
        				}
    
    					//再判断纳新人数是否为0 以及 学生s的标签类似数和最少标签类似数less_tag_same_num的大小关系
    					//若纳新人数为0 或者 学生s的标签类似数<=最少标签类似数less_tag_same_num ,考虑学生s的下一个志愿
    					if (d.department_member_limit == 0 || s.now_tag_same_num <= least_tag_same_num)
    					{
    						s.student_cur++;
    						// 如果五个志愿还没考虑完毕的话,放入队列中继续参与分配
    						if (s.student_cur < s.wish_count)
    							student_que.push(s);
    					}
        				else
        				{
        					// 否则学生s就直接替换掉标签类似数最少的那个学生
        					int least_stu_index = get_student_index(least_stu_no);  //被替换掉的学生s.在<学生数组>中的下标
        					Student& least_stu = stu[least_stu_index];
        					int least_stu_department_index = get_stu_department_index(least_stu_index, least_stu.chosen_num, d.department_no); //部门d在被替换掉的学生s.<已中选部门数组>中的下标
    		    			least_stu.department_no[least_stu_department_index] = -1;  //被替换掉的学生该部门队列
        					//此时学生s已中选,保存中选信息
        					s.department_no[s.student_cur] = d.department_no;
        					s.tag_same_num[s.student_cur] = s.now_tag_same_num;
        					s.chosen_num++;
        					d.student_no[pos] = s.student_no;
        				} //替换
        			} //若部门无空余名额
        		}//行程无冲突
        	} //绩点满足
        	else 
        	{
        		s.student_cur++;
        		// 如果五个志愿还没考虑完毕的话,放入队列中继续参与分配
        		if (s.student_cur < s.wish_count) 
        			student_que.push(s);
        	}
        }
    }
    

    【测试运行】

    学生人数 部门人数 学生未分配人数 部门未分配数量 学生匹配率 部门匹配率 耗时(s)
    200 20 128 0 36% 100% 4
    500 30 322 0 36% 100% 6
    1000 50 722 0 28% 100% 10
    5000 100 3626 0 27% 100% 21
    • 效能分析报告



    【困难与解决】

    • json学习——雨勤

    • 使用了助教在微信群中推荐的json,这个json的方法和网上最广泛流传的jsoncpp不大一样,没有Value、Reader和Writer。找资料的能力真的比较欠缺,其实网上有相应的中文博客解读,只是我一直没找对,于是很吃力地翻译阅读github上的文档。
    • 时间——雨勤

      • 问题:我在假期中主要进行了对json的学习,开假后就去漳州比赛(明明是公费旅游),白天课也很多,留给结对作业的时间十分有限。
      • 解决:本周,无论在外地、在宿舍,夜夜修仙。
      • 效果:伤身。
    • 交流——雨勤

      • 问题:一开始进行了讨论,制定了基本的原则与方案,但在后来代码实现的过程中,由于算法实现的一些原因,对最初的设计做出了修改,但两人之间并没有进行良好的沟通反馈,以至于后来在咨询队友一些变量细节问题时,突然发现对大原则变了……
      • 解决:向匹配模块的大佬低头,有什么需要您说的算:)
    • 规范——雨勤

      • 问题:两人各有自己的代码习惯,虽然制定了代码规范,但好像没能严格的执行。
      • 解决:是时候事后诸葛了,改。
    • 计划——安琪

      • 问题:实现初期赶着时间,想赶紧搭个框架出来好和小伙伴对接,蜜汁自信连流程图都没画就直接上手,导致算法设计模糊混乱,浪费了相当一部分时间。
      • 解决:冷静分析后觉得还是不能本末倒置,于是老老实实把流程图画了下,觉得思路瞬间清晰了不少。
    • 不符预期——安琪

      • 问题:算法实现过程中发现之前和队友小伙伴讨论的原则存在一些问题,并不是很合理,且实现起来也相当麻烦。
      • 解决:改、删。(勤勤我对不起你)

    【对安琪的评价】

    • 关键词:效率 投入 配合
    • 没有队友我大概已经上天了。
    • 这么好的大腿我是怎么抱到的?
    • 队友安琪是一个进取心很强的girl,她会要求自己的工作一定在计划内完成,不会把时间浪费在其他繁琐的事情上。做决定的时候很果断,当我还在纠结题目表述不清,我们该这样还是该这样的时候,安琪已经“我们就这样做”了。

    【对雨勤的评价】

    • 关键词: 认真 负责 包容
    • 什么,居然还要写队友缺点?
    • 那大概就是太可爱了,导致我不能专心敲代码了_
    • 因为一些不可抗拒因素,在实现阶段不能和雨勤进行很好的沟通交流,有部分细节擅作主张改完之后才跟她说,虽然她选择了原谅(给大佬递帽子),但还是很对不起她,害她要熬夜返工orz。面对json这种我看了两遍还是不大懂是什么鬼的东西,雨勤也不在怕的,很好的完成了,要好好亲亲她了O(∩_∩)O。

    【PSP】

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

    【学习进度条】

    第N周 本周学习消耗时(小时) 累计学习消耗时(小时) 重要成长
    2 4 4 阅读《构建之法》,了解结对编程,学习NABCD竞争性需求分析的框架
    4 5.25 9.25 JSON相关知识学习与实战演练;c++基础知识复习
    5 6 15.25 计算机视觉基础知识、OpenCV巩固;目标检测算法
  • 相关阅读:
    Thymeleaf学习记录(1)--启动模板及建立Demo
    Redis教程基本命令
    错误:Attempted to load applicationConfig: [classpath:/application.yml] but snakeyaml was not found on the classpath
    备注
    MYSQL建表问题(转)
    Class.forName("com.mysql.jdbc.Driver")找不到类
    mySql连接报错
    电脑启动过程
    C++输入输出流--<iostream>详解
    类内部实例化自身可行吗?
  • 原文地址:https://www.cnblogs.com/wyq0808/p/7674123.html
Copyright © 2020-2023  润新知