04.毕设导师智能分配

031402508 洪佳铭

031402516 黄瑞钰

项目源码猛戳此处⬅️

问题描述

编码实现一个毕设导师的智能匹配的程序。提供输入包括:30个老师(包含带学生数的要求的上限,单个数值,在[0,8]内),100个学生(包含绩点信息),每个学生有5个导师志愿(志愿的导师可以重复但不能空缺)。实现一个智能自动分配算法,根据输入信息,输出导师和学生间的匹配信息(一个学生只能有一个确认导师,一个导师可以带少于等于其要求的学生数的学生) 及 未被分配到学生的导师 和 未被导师选中的学生。

问题分析

本次算法采用的是Gale-shapley算法来解决配对的问题

  • Gale-shapley算法介绍(摘自《硕士研究生与导师的双向选择的最优匹配》——向冰,刘文君)


  • 如何实际应用Gale-shapley算法?
    我们发现Gale-shapley算法需要在信息对称且完全的情况下,按照偏好,进行相互选择。这次任务导师相对于学生信息是公开的,学生可以自主根据喜欢的导师进行志愿填写。然而这次没有让导师来选择学生,而是要自动分配学生,所有学生相对于导师信息其实是不公开的,这样就导致双方的信息是不对称的。 所以我们通过学生的绩点高低来排序(若绩点相同,就按照学号大小来排列),这样导师就按照该排序来模拟导师偏好,来进行学生分配。从而就可以满足Gale-shapley算法需要在信息对称且完全的情况下,选择的双方按照自己的偏好进行选择的条件,从而得出最优的匹配。

代码分析

  • 随机生成数据

导师的ID、希望带领的学生个数,学生的学号、绩点、五大志愿均随机生成(部分代码如下)

/**
	 * 自动生成导师数据用于测试
	 * 格式为: 导师姓名       导师ID    希望带领学生个数
	 * Demo: 导师01     7561         7
	 * @param tutorNum
	 */
	public void createTutorData(int tutorsNum) {
		try{
		
			Writer writer = new FileWriter("testData/tutorsData.txt");
	        BufferedWriter buffWriter=new BufferedWriter(writer);
	
	        buffWriter.write("导师姓名" + "     " 
			+ "教师ID" + "     " 
			+ "导师希望带领学生数");
	        buffWriter.newLine();
	
	        for(int i = 1;i <= tutorsNum;i++) {
		if(i < 10) {
			buffWriter.write("导师0" + i + "       "
					+ createRandomTutorID() + "              " 
					+ createRandomLeadNum());
		} else {
			buffWriter.write("导师" + i + "       " 
					+ createRandomTutorID() + "              " 
					+ createRandomLeadNum());
		}
		        buffWriter.newLine();
	        }
	
	        buffWriter.close();
	        writer.close();
	        System.out.println("导师测试数据写入成功!");
		} catch(IOException e) {
			System.out.println("导师测试数据文件写入错误:" + e.getMessage());
		}

	}
	
	/**
	 * 自动生成学生数据用于测试
	 * 格式为: 学生姓名           学号             绩点            第一志愿        第二志愿        第三志愿        第四志愿        第五志愿
	 * Demo: 学生01   231402001  3.21     1234     2234     1234     1234      1234
	 * @param studentsNum
	 */
	public void createStudentData(int studentsNum) {
		try{
			Writer writer = new FileWriter("testData/studentsData.txt");
	        BufferedWriter buffWriter = new BufferedWriter(writer);
	        buffWriter.write("学生姓名" + "      "
			+ "学生学号" + "        " 
			+ "学生绩点" + "        " 
			+ "第一志愿" + "        " 
			+ "第二志愿" + "        " 
			+ "第三志愿" + "        " 
			+ "第四志愿" + "        " 
			+ "第五志愿");
	        buffWriter.newLine();
	
	        for(int i = 1;i <= studentsNum;i++) {
		if(i < 10) {
			buffWriter.write("学生00" + i + "       " 
					+ createRandomStudentNum() + "       " 
					+ createRandomGradePoint() + "           " 
					+ createRandomTutor() + "            "
					+ createRandomTutor() + "            "
					+ createRandomTutor() + "            "
					+ createRandomTutor() + "            "
					+ createRandomTutor());
		
		} else if (i < 100){
			buffWriter.write("学生0" + i + "       " 
					+ createRandomStudentNum() + "       " 
					+ createRandomGradePoint() + "           "
					+ createRandomTutor() + "            "
					+ createRandomTutor() + "            "
					+ createRandomTutor() + "            "
					+ createRandomTutor() + "            "
					+ createRandomTutor());
		} else {
			buffWriter.write("学生" + i + "       " 
					+ createRandomStudentNum() + "       "
					+ createRandomGradePoint() + "           "
					+ createRandomTutor() + "            "
					+ createRandomTutor() + "            "
					+ createRandomTutor() + "            "
					+ createRandomTutor() + "            "
					+ createRandomTutor());
		}
		        buffWriter.newLine();
	        }
	
	        buffWriter.close();
	        writer.close();
	        System.out.println("学生测试数据写入成功!");
		} catch(IOException e) {
			System.out.println("学生测试数据文件写入错误:" + e.getMessage());
		}
	}
	
  • 对学生进行排序

将学生按照绩点高低(若绩点相同,按照学号大小)排列

public class SortByGradePoint implements Comparator{

	
	//按绩点从高到低排列,如果绩点相同,则按照学号从小到大排
	@Override
	public int compare(Object o1, Object o2) {
		// TODO Auto-generated method stub
		if(((StudentInf)o1).getGradePoint() < ((StudentInf)o2).getGradePoint()) {
			return 1;
		} else if(((StudentInf)o1).getGradePoint() > ((StudentInf)o2).getGradePoint()) {
			return -1;
		} else if((((StudentInf)o1).getsNum().compareTo(((StudentInf)o2).getsNum())) < 0){
			return 1;
		} else {
			return -1;
		}
			
	}
	
}
  • 核心算法:分配导师
  1. 根据学生们的志愿一轮一轮的选择自己中意的导师。(如果该学生该轮志愿选的导师,其带领学生人数已经满了,则进行下一个学生的分配。该学生需要等待下一轮志愿再进行分配。)
  2. 每一轮过后都会有一些学生被分配,当五轮(五个志愿)都结束后,这个算法就结束了。
  3. 在这个算法中,100个学生共需要进行5轮选择,因此,算法最多100*5轮循环就结束了。
public static void assignmentTutor(HashMap<String,TutorInf> tutorsMap,ArrayList<StudentInf> studentsList,ArrayList<StudentInf> isAssignmentStudentsList) {
		for(int i = 0 ;i < 5;i++) {![image](http://note.youdao.com/favicon.ico)
			Iterator<StudentInf> iterator = studentsList.iterator();
			while (iterator.hasNext()) {
				StudentInf studentInfTemp = iterator.next();
				TutorInf tutorInftemp = (TutorInf)(tutorsMap.get(studentInfTemp.getTutorsWish().get(i)));
				if(tutorInftemp.getLeadedNum() < tutorInftemp.getLeadNum()) {
					
					tutorInftemp.setLeadedNum(tutorInftemp.getLeadedNum() + 1);  //导师带领人数+1
					tutorInftemp.getLeadStudents().add(studentInfTemp.getsName() + "(" + studentInfTemp.getsNum() + ")"); //学生名字、学号添加到导师带领学生列表
					studentInfTemp.setAssignment(true); //标记该学生已分配导师
					studentInfTemp.setMyTutorName(tutorInftemp.getTname());//记录该学生的导师姓名
					studentInfTemp.setMyTutorID(tutorInftemp.gettID());//记录该学生的导师ID
					isAssignmentStudentsList.add(studentInfTemp);  //将该学生添加到已将分配的学生List
					iterator.remove();
					
				} else if (tutorInftemp.getLeadedNum() == tutorInftemp.getLeadNum()) {
					continue;
				}
			}
		}
	}

代码测试结果分析

  • 自动生成的数据

  • 测试结果

  • 分析
    在选择的双方信息对称的前提下,Gale-shapley算法很好的解决了导师分配的问题。但是还是会有少数学生没有分配到导师。最优情况是所有学生都得到了分配,最坏情况则是有十多个学生未得到分配。

算法优劣分析及后期改进

从算法本身来说,Gale-shapley算法完全可以满足本次问题的要求。后期改进的话我们觉得可以从匹配双方的偏好方面进一步优化,使得分配更加合理,这次导师偏好完全只是依靠学生绩点和学号来模拟的。还有就是从随机数据的产生方面加入更多的限制条件,使得产生的随机数据更加贴近现实。

第二次结对感受

洪佳铭(031402508): 本次的结对任务说实话并不是我的强项,最终磕磕绊绊地完成了基本目标,但是还是暴露出我对编码这方面的不足,这需要我后期投入更多的时间和精力去多多接触。很感谢队友,他承担了很大一部分的任务,果然是中国好队友。

黄瑞钰(031402516):最初看到这个作业的时候,不知道怎么开始动手,前几天没什么进度。后来通过学校图书馆查了一些关于研究生导师选择的论文,发现Gale-Shapley 算法可以很好的解决分配的问题。于是又阅览了《算法的乐趣》关于Gale-Shapley算法的一些应用实例。然而当要开始写代码的时候,还是一脸懵逼。由于好久没写Java,连语法都有点忘记。脑子里已经有思路了,但是就是不知道怎么用代码来实现,然后就又拖了一两天。 后面把整个任务分解成一个一个小问题。从数据的文本输入输出,到类的排序,再到Gale-Shapley算法的实现,不断地百度啊百度,一步一步逐渐完善。 感觉前期的拖延,很大一个原因是因为对大一点、麻烦一点、不熟悉的问题,内心不愿意立马去面对。有点困难被吓到了,从而不知道如何去动手来解决。可是当你把大的问题拆解成小问题,一个一个去解决,真的就不一样了。

本次的亮点

这次最大的亮点是发现了Gale-shapley算法,这个算法很好地解决了我们的问题,为我们提供了很好的思路。

参考资料

  1. 王晓华。《算法的乐趣》第七章稳定匹配与舞伴问题
  2. 向冰,刘文君。硕士研究生与导师的双向选择的最优匹配
posted @ 2016-09-30 21:50  shamo!  阅读(1248)  评论(2编辑  收藏  举报