第二次结对作业报告

毕设导师智能配对算法设计

结对编程者:

胡泽善031402509,coding昵称aiprogram

王智强031402524,coding昵称khhkshhk

我们的项目地址

>>>使劲戳这儿


完成课题前的准备

一拿到这个命题是,我和我的对友立刻在心中快速寻找着能解决类似需求的算法以及数据结构。我的心中最开始浮现出来的是二分图匹配算法,因为这是用来解决两个大团体单体间匹配时所使用的最常用的算法,而我恰巧了解过这一算法。但是我和对友在细致的考差了二分图的使用后发现,二分图仅仅集中在促成最多的配对,并没有涉及到如何满足双方的选择要求,而我们自认为还没有这个能力在二分图算法的基础上满足课题要求。于是我们两个人又开始在网上搜寻相关解决办法,直到我们发现了以“解决婚姻匹配问题”著名的诺贝尔奖相关算法:GaleShapley算法。我们得到UML用例图如下:

Gale-shapley算法概述:

简单来说这个算法,是指在两个给定的集合中,每个集合中的每个元素都对另外一个集合中的元素有一个偏好排序,
也称为延时接受算法,因为两个集合之间互相存在偏好排序,所以在给出最优配对选择的过程中,会根据偏好排序从优进行选择,结合其他的条件限制,如果在某一轮选择中,配对成功,则此时该选择者和该被选择者暂时配对,下一轮给那些还未配对的被选择者配对,如果出现与上一轮的选择对象存在竞争的情况,则根据选择者对被选择者的偏好情况,进行对比,选择最优。直到所有轮次结束。


在认真分析了GaleShapley的配对算法后,看上去与我们的课题“毕设导师智能匹配”风马牛不相及的婚姻匹配算法也拥有了共通性了。我们可以发现,婚姻配对时,男人女人对于各自实际上都有着喜好的顺序,而且为了保证配对的“稳定”性,我们必须要保证双方都能尽可能的满意。同理,毕设导师智能匹配时,学生拥有着五个志愿,这五个志愿我们如果假定是有顺序的话就体现了学生的喜好,而老师的喜好包括了绩点、学生技能等,这里为了方便算法设计我们可以用绩点排序简单代替。

在发现了两者之间的共通性后我们两个人就开始认真研究与思考如何把参考算法的思想用来解决我们的课题。经过我和队友连续几天在coding.net上的提交修改以后,我们的算法终于成形了。我们的算法使用Java语言利用Eclipse工具实现的,所以我们的功能模块是按照类的形式编写的,那么我就按照类的形式来介绍吧。

Outputer类

一个随机生成测试数据的类

核心函数:

public void getData(){
	int num_sum=0;
	int num;
	Random random=new Random();
	for(int i=0;i<num_teacher;i++){//随机得到教师数据
		num=random.nextInt(num_max+1);
		while(num_student-num_sum-num>num_max*(num_teacher-i-1))num=random.nextInt(num_max+1);
		teachers[i].num=num;
		num_sum+=num;
	}
	//teachers[num_teacher-1].num=num_student-num_sum;//最后一个老师凑成总数
	
	for(int i=0;i<num_student;i++){//随即得到学生数据
		students[i].grade=random.nextFloat()*2+1.5;
		for(int j=0;j<5;j++)
			students[i].want[j]=random.nextInt(num_teacher)+1;
	}
}

这里可以看到,我们利用getData()函数随机生成了包括教师:教师姓名、教师所定学生限额;学生:学生姓名、学生绩点、学生的五个志愿这些数据。其中学生的数量、老师的数量、老师的最多学生指标都是可以设置的,这里就没有浪费篇幅体现了。另外一点就是关于老师所定限额的生成,我们本来是限制为所有名额总数恰好等于学生的数量。但是这样出现的问题就是30个老师经常随机出5、6个名额为0,严重影响我们的算法效率,最终我们去掉了上限,实际总数并没有超出学生数太多。输出最终输出到data.txt这个文本文件中,输出格式说明见doc.txt

DataReader类

一个读入数据的类

核心函数:

public void readFile (){
		String tmp;
		String strarry[];
		try{
			File file=new File("data.txt");
			FileReader fileReader=new FileReader(file);
			BufferedReader bufferedReader=new BufferedReader(fileReader);
			tmp=bufferedReader.readLine();//获取数量
			strarry=tmp.split(" ");
			num_tea=Integer.parseInt(strarry[0]);
			num_stu=Integer.parseInt(strarry[1]);
			teachers=new Teacher [num_tea];
			students=new Student [num_stu];
			
			for(int i=0;i<num_tea;i++){//获得教师数据
				tmp=bufferedReader.readLine();
				strarry=tmp.split(" ");
				teachers[i]=new Teacher();
				teachers[i].id=Integer.parseInt(strarry[0]);
				teachers[i].name=strarry[1];
			teachers[i].num_max=Integer.parseInt(strarry[2]);
				teachers[i].stu=new int[teachers[i].num_max];
			}
			
			for(int i=0;i<num_stu;i++){//获得学生数据
				tmp=bufferedReader.readLine();
				strarry=tmp.split(" ");
				students[i]=new Student();
				students[i].id=Integer.parseInt(strarry[0]);
				students[i].name=strarry[1];
			students[i].grade=Double.parseDouble(strarry[2]);
				tmp=bufferedReader.readLine();
				strarry=tmp.split(" ");
				for(int k=0;k<5;k++)
			students[i].want[k]=Integer.parseInt(strarry[k]);
			}
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}

这里主要是一些对文件的读取,无需太多的解释。值得一提的是我们为了方便调试修改,并且对程序解耦合,把数据的获得写成了getXX()的接口形式。这样一来,日后如果需要添加功能,或者进行其他修改无需伤筋动骨。

Student及Teacher类

两个封装了算法所需数据结构的类

核心代码:

public class Teacher {
	public int id;
	public String name;
	public int num_max;//老师所定招收学生数
	public int num_stu=0;//老师已经招收学生数
	public int stu[];//已经招收学生列表
}
public class Student {
	public int id;
	public String name;
	public double grade;//学生绩点,暂时用作唯一排序指标
	public int want[]=new int [5];//学生志愿,有顺序
	public int tea;//学生已配对老师id
	public boolean selected=false;//学生是否配对成功
}

CoreProc类

核心算法类

核心函数:

public void match(){
	int round=0;//算法模拟的轮数
	int choice;
	double grade_min;
	int min=0;//比较时记录grade最小的学生id
	int min_pos=0;
	int stu_id_tmp;
	for(round=0;round<5;round++){//最多五轮,因为五个志愿
		for(int i=0;i<num_student;i++){
			if(students[i].selected==false){//还没有选中老师
				choice=students[i].want[round]-1;					if(teachers[choice].num_stu<teachers[choice].num_max){//导师未满					teachers[choice].stu[teachers[choice].num_stu++]=students[i].id;
					students[i].tea=teachers[choice].id;
					students[i].selected=true;
				}
				else {//导师已满
					grade_min=4;//可改,取最大grade限制
					for(int k=0;k<teachers[choice].num_stu;k++){
						stu_id_tmp=teachers[choice].stu[k]-1;
						if(students[stu_id_tmp].grade<grade_min){
							grade_min=students[stu_id_tmp].grade;
							min=stu_id_tmp;
							min_pos=k;
						}
					}
					if(grade_min<students[i].grade){
						students[min].selected=false;
						students[i].selected=true;
						students[i].tea=teachers[choice].id;									teachers[choice].stu[min_pos]=students[i].id;
					}
				}
			}
		}
	}
}

这里我就要详细的来介绍一下我们的算法步骤了。有以下几步:

  • 首先,我们把算法分为N轮,轮数等于每个学生的志愿数
  • 每一轮算法都会搜索当前还没有匹配的学生
  • 如果本轮(第n轮)学生S还没有找到匹配老师,那么我们尝试将他与他的第n个志愿的老师匹配,否则跳过
    • 如果该老师名额未满,那么S暂时加入老师的候选学生名单
    • 如果老师的名额已满,那么他就要与当前老师的候选名单中的所有学生竞争,如果S的grade大于候选学生中grade最小的学生S‘的grade,S'失败由S替代。否则S无法匹配。
  • N轮过后,产生最终的匹配结果

关于本算法的的合理性:
首先,我们的算法保证了每一次尝试的匹配都是当前学生所能使用的最靠前的志愿,这样一来,每个同学都只有在自己的更高级的志愿没有办法匹配后才会使用下一级的志愿,这样的贪心策略保证了学生的满意度。
另外,老师的选择具有特殊性,那就是老师在自己的名额没有满的时候是没有办法选择学生的,这符合命题的要求。但是一旦名额已满,新来的学生都需要竞争,留下来的学生都是最符合老师要求的学生,这样做也满足了导师的需求。

对于算法的思考:
仔细观察,我们会发现不论是GaleShapley算法还是我们结对编程所设计出来的算法,它们的过程都是很简单的,也都是模拟我们自然选择时的过程。其实当前计算机的前沿领域比如神经网络、人工智能等本质上也是在尽可能真实有效的模拟人类的思想和解决问题的流程。所以我们看上去很简单的算法,运行的时间复杂度和空间复杂度都不高,运行得到的配对结果也没有让我和队友失望。

算法存在的问题和可改进的地方:

  1. 学生评价标准

在本算法中我们为了简洁编写,并把重点放在算法的展示上,所以使得每个老师对于所有学生的评价标准都是学生的绩点。而实际情况显然更加复杂,每个老师对于每个同学的评价必然不同。这一点我们认为只要把老师的个性评价单独列成指数存入每个老师的Teacher中,并在核心算法中改为通过老师的个性评价来竞争即可,困难度不大。
2. 学生重复选择

我们的随机数据中对于学生的重复选择是没有限制的,但是在核心算法中,学生的重复选择是没有意义的,反而是浪费了自己的选择机会。所以我们设计时有考虑过是否为重复选择一个老师的同学在竞争该老师时提供加成。但是,S同学如果得到加成并且胜过了一个本来他无法胜过的同学,那么我们必然会失去一个“完美匹配”:因为另一位同学老师一定更喜欢,他所投递的志愿等级也不会低于S同学。这里面就需要我们掌握加成的度了,因为上述情况不合理,不能发生太多次。我们暂时没有想到恰当的确定方法。并且有了分级志愿后,重复选择老师也是无意义的,这与高考投递志愿类似。

算法的效率评估

本次我们采用的主要评估指数有两个:

  • 选中的学生所占的百分比
  • 学生平均在第几个志愿选中老师
    一共进行了10次随机测试,原始数据的输入无任何修改结果如下

Git使用心得:

Git是个好东西,但对于第一次尝试使用的人来讲,如何在pc上安装好git,确也是一件难事。
刚开始我参考网上的廖雪峰关于git的学习教程,并且从那里提供的链接下载git,也成功运行了git,学了几行git命令,创建了本地库,但是后来在使用的过程中总会出现一些毛病,在对友的建议和帮助下,我从git官网重新下载绿色版git。
在我看来,新手刚开始使用git,首先要设置好本地库的路径,然后学习git的操作命令。目前课程对于我们掌握git的使用程度还不是很严格,基本能够保存好文件,使用简单的添加修改就没问题。
总体感受:多琢磨、多尝试、多交流!

结对心得:

031402524王智强:

这是结对编程的第二次作业,与第一次迥然不同,这次要求比较具体,要针对实际问题考虑合适的算法,并且编程实现。
给我的感觉是解题思路很关键。合适的算法会使后面的编程实现轻松简单。我们使用java编写,这次作业对友很给力很赞,烧脑的工作主要都是对友负责的;同时我们延续了第一次作业时候培养起来的良好的交流习惯和合作默契。

031402509胡泽善:

本次作业我认为相比于上一次作业工作量减少了,但是难度却有所上升,毕竟不像上一次结对作业虽然复杂但是并没有涉及代码以及算法实现。而这一次老师给了我们设计导师智能匹配算法这一非常具体实际的任务。这一次任务不只是匹配那么简单,为了更好的体现对双方要求的满足,我和我的对友思考而且讨论了很久。后来在编码的时候,由于我的对友对于Java不够熟练,所以我先打了一个大概的框架。事实证明这样也没违背结对编程的初衷,我后来和对友深刻交流了代码细节,由他独自去理解并且修改,我辅助完成课题。

闪光点:

(1) 解决本次作业的算法(gale-sheply),本身就是一个闪光点。
(2) 随机生成数据的函数,不局限于本题要求的(30,8,100)。
(3) 在输出中,从学生和老师的角度出发各添加一个评价函数的功能;同时出于友好性,我们把落选的学生序号等信息,也在输出文本中体现。

posted @ 2016-09-29 17:07  aiprogram  阅读(253)  评论(3编辑  收藏  举报