• 编码实现一个部门与学生的智能匹配的程序


    编码实现一个部门与学生的智能匹配的程序


    1. 相关信息

    1.1 结对成员学号

    我:********* ***

    大佬:*******10 ***

    1.2 项目Github地址

    使用Java语言。
    https://github.com/Maple27/S-D_Match

    2. 作业需求

    详细要求点我
    编码实现一个部门与学生的智能匹配的程序。
    提供输入包括:

    * 20个部门:
        * 部门编号(唯一确定值),字符;
        * 各部门需要学生数的要求的上限,单个,数值,在[10,15]内;
        * 各部门的特点标签,多个(两个以上),字符;
        * 各部门的常规活动时间段,多个(两个以上),字符。
    * 300个学生:
        * 学生编号(唯一确定值),字符;
        * 学生空闲时间段,多个(两个以上),字符;
        * 兴趣标签,多个(两个以上),字符(学生的兴趣标签一定是所有部门特点标签其中的一个)
        * 每个学生有不多于5个的部门意愿(助教测试时测试数据中部门意愿可能会出现空缺,非空缺的部分一定是部门编号中的一个,并按照优先级从高到底的顺序排序)。
    

    实现一个智能自动分配算法,根据输入信息,输出部门和学生间的匹配信息(一个学生可以确认多个他所申请的部门,一个部门可以分配少于等于其要求的学生数的学生) 及 未被分配到学生的部门 和 未被部门选中的学生。

    3. 程序设计

    3.1 总体思路

    1.读入当前目录下input_data.txt文件。
    2.根据读入的文件解析并初始化数据(学生,部门的信息)。
    3.将部门根据tags的数量进行排序,tags少的部门优先进行匹配。
    4.统计学生在各个部门的优先级。
    5.根据学生志愿和优先级,在部门与学生之间进行匹配。
    6.构造json数据,输出到output_data.txt。

    3.2 结构设计

    输入和输出:使用了Gson。gson是泓立建议使用的,我之前对其没有任何了解,都是泓立带飞+照着模板打。我个人的理解:gson就是一个用来处理json输入输出的工具,用的时候需要建立专门的输入类和输出类。有模板就是好-v-。
    数据储存:专门建立Student、Department类,用上万能的List数组。这部分很简单。

    3.3 关键代码

    该部分取自于Match.java

    3.3.1 统计学生在各部门的分数

    思路很简单:学生一周中每有1小时free_time与部门event_schedule时间对应,优先级+1,学生每有一个兴趣tag与部门tags对应,优先级+2。
    兴趣的统计很简单。时间的统计:首先创建存储相关信息的List结构,输入数据后想办法对信息(字符串)进行分割。分割用的方法很土,分割后用来存储信息的字符串名字也很土,但是无伤大雅。最后经过一连串的if判断,进行加分。

    public void countStudentScore(){
    	for(int i=0;i<departments.size();i++){
    		for(int j=0;j<students.size();j++){
    			List<String> dTimes = departments.get(i).getActivityTime();
    			List<String> sTimes = students.get(j).getFreeTime();
    			List<String> dTags = departments.get(i).getTags();
    			List<String> sTags = students.get(j).getInterests();
    			int score = 0;
    			//时间分数统计
    			for(int m=0;m<dTimes.size();m++){
    				String str1 = dTimes.get(m);
    				String[] string2 = str1.split("\.");
    				String[] string3 = string2[1].split("~");
    				String[] string4 = string3[0].split(":");
    				String[] string5 = string3[1].split(":");
    				String day1 = string2[0];
    				int start1 = Integer.parseInt(string4[0]);
    				int end1 = Integer.parseInt(string5[0]);
    				for(int n=0;n<sTimes.size();n++){
    					String str2 = sTimes.get(n);
    					String[] string6 = str2.split("\.");
    					String[] string7 = string6[1].split("~");
    					String[] string8 = string7[0].split(":");
    					String[] string9 = string7[1].split(":");
    					String day2 = string6[0];
    					int start2 = Integer.parseInt(string8[0]);
    					int end2 = Integer.parseInt(string9[0]);
    					
    					if(day1.equals(day2)){
    						if(end1<=start2||end2<start1){
    							score += 0;
    						}
    						else if(start1<start2&&end1>start2&&end1<end2){
    							score += end1-start2;
    						}
    						else if(start2<start1&&end2>start1&&end2<end1){
    							score += end2-start1;
    						}
    						else if(start1>start2&&end1<end2){
    							score += end1-start1;
    						}
    						else if(start2>start1&&end2<end1){
    							score += end2-start2;
    						}
    					}
    				}
    			}
    			//兴趣分数统计
    			for(int p=0;p<dTags.size();p++){
    				String tag1 = dTags.get(p);
    				for(int q=0;q<sTags.size();q++){
    					String tag2 = sTags.get(q);
    					if(tag1.equals(tag2)){
    						score += 2;
    					}
    				}
    			}
    			//将此部门与学生时间匹配的分数加入学生信息中
    			students.get(j).getScores().add(score);
    		}
    	}
    }
    

    3.3.2 部门编号选学生志愿匹配算法

    思路:从tags最少的部门开始,先对学生以在该部门的优先级进行排序,再按第一志愿到最低志愿,遍历学生,符合即选中。注释说的很明白了。

    public void match_DtoS(){
    	for(int i=0;i<departments.size();i++){//部门匹配
    		int num = 0,flag = 0;
    		//每次对一个部门招募时以学生对此部门的分数对学生进行重新排列
    		sortStudent(students, 0, students.size()-1, i);
    		for(int j=0;j<5;j++){//志愿匹配
    			for(int k=0;k<students.size();k++){//学生匹配
    				//超限 下一个
    				if(num>=departments.get(i).getLimit()){
    					flag=1;
    					break;
    				}
    				//志愿不足 下一个
    				if(students.get(k).getWillsSize()<=j) continue;
    				String wills = students.get(k).getWills().get(j);
    				String no = departments.get(i).getId();
    				if(wills.equals(no)){
    					//符合志愿条件,根据符合分数进行分配
    					departments.get(i).getMembers().add(students.get(k));
    					students.get(k).setFlag(1);
    					num++;
    				}
    			}
    			if(flag==1) break;
    		}
    		departments.get(i).setNum(num);
    	}
    	for(int i=0;i<departments.size();i++){
    		for(int p=0;p<departments.get(i).getMembers().size()-1;p++){
    		    for(int j=departments.get(i).getMembers().size()-1;j>p;j--){
    		      if(departments.get(i).getMembers().get(j).equals(departments.get(i).getMembers().get(p))){
    		    	  departments.get(i).getMembers().remove(j);
    		      } 
    		    } 
    		}
    
    	} 
    }
    

    4. 数据生成

    这里是我们自认为生成“最好的数据”。
    数据生成的代码储存于DataMaker.java中。

    看了一下题干,大概能总结出数据一共只有这三类:

    1. 纯数字的。这类直接用限定范围的随机数生成就行了。嗯,学号还要考虑用过的不能再用。
    2. 单词类的,比如说部门名称、兴趣名称什么的。这种单词的个数都是有限的,直接弄一个表(数组实现),生成的时候直接从里面随机拿取数据。
    3. 时间。这个就比较麻烦了。我们把字符串各个元素拆开,先随机生成各个部分,最后再拼起来。

    想得美,做得难。做到后面我们发现其实细节上的麻烦还真不少。比如说我们要处理诸如时间不能交叉的各种问题,也要慢慢调试时间的拼接格式。

    我们考虑到如下情况:学号和部门号不能出现重复,对于同一个学生/部门标签不能重复,学号只能在限定的范围内出现,生成的时间不能交叉重叠,相邻的时间段也要考虑,不会生成奇葩的时间段(比如说凌晨3:00~4:00之类的)。

    一些问题我们没有考虑到:
    时间只能生成整点,时间不能够跨天。时间生成整点的确是硬伤,有样例数据的诱导因素在,但是做完之后不想改了-v-。时间不能跨天是我们不想做,现实中可能有些部门会进行深夜活动之类的,也会有学生熬夜的,很烦。另外,粗略看来,生成的数据还是无法反映真实世界的情况。暂时就写到这里。

    5. 代码规范

    缩进格式:Tab。
    行宽:一般不做限制,长行应该尽量少。
    注释:2行以上的使用/**/,否则使用//
    结构:可以使用形如if(cond) do();的结构。{应该挂在括号右边。
    类方法:尽量简短。
    命名:类名使用大驼峰,函数名、变量名等使用小驼峰,名称过长时可以使用下划线做分割。
    示例

    Gson gson = new GsonBuilder().setPrettyPrinting().create();
    OutputBean outputBean = new OutputBean();
    for(int i=0;i<students.size();i++){
    	if(students.get(i).getFlag()==0){
    		outputBean.getUnlucky_student().add(students.get(i).getId());
    	}
    }
    for(int j=0;j<departments.size();j++){
    	if(departments.get(j).getNum()==0){
    		outputBean.getUnlucky_department().add(departments.get(j).getId());
    	}else{
    		AdmittedBean admittedBean = new AdmittedBean();
    		for(int k=0;k<departments.get(j).getMembers().size();k++){
    			admittedBean.getMember().add(departments.get(j).getMembers().get(k).getId());
    		}
    		admittedBean.setDepartment_no(departments.get(j).getId());
    		outputBean.getAdmitted().add(admittedBean);
    	}
    }
    

    6. 结果分析

    根据生成的数据,返回的结果达到了预设的要求。但是我们的程序的问题还是显而易见的。
    首先我们构造的数据还是与真实的情况有所出入。比如说学生的课余时间一般都在周末,以及时间段的划分还跟课程表和固定活动(比如吃饭)有关,这点我们望尘莫及。从tag最少的部门排序,tag多的部门就很难找到人,这不公平。也很容易出现兴趣广泛的学生报了多个tag单一且一致的社团。优先级也有问题,部门没办法筛选出关键时间不能参加活动的学生。诸如此类。

    7. 结对感受

    第一次尝试结对编程,真的是被大佬带着飞啊。。本来想用c++做,然后泓立建议使用java,我对java的理解也就仅限于基本语法了,结果就是变成了老师-学生的模式-_-。最后嘛,Java知识的确学到了一些。大佬打代码行云流水,我还看不太懂,我就在接盘的时候问这问那。然后到我打的时候寸步难行,大佬在一旁指点。。不过我们一直觉得实际上不太适应结对这种方式,因为打代码的时候实际上人是不喜欢被突然打断的。最后一天代码文件打包上传至github。

  • 相关阅读:
    JavaScript对数组的处理(一)
    PHP数组排序函数array_multisort()函数详解(二)
    PHP数组排序函数array_multisort()函数详解(一)
    jquery html动态添加的元素绑定事件详解
    SpringBoot 通用Error设计
    SpringBoot Lombok
    VMware Big Data Extensions 安装步骤
    SpringBoot 通用Validator
    SpringBoot Mybatis keyProperty和useGeneratedKeys的作用
    Introduction to vSphere Integrated Containers
  • 原文地址:https://www.cnblogs.com/Metak/p/7643249.html
Copyright © 2020-2023  润新知