基于协同过滤算法实现选课推荐系统
1.系统功能
1、用户账户管理
2、学生个人信息的查看与修改
3、学生的网上选课与课程的评分
4、教师个人信息的查看与修改
5、教师对学生课程评价结果的查看
6、管理员对学生信息与教师信息的查看与添加
7、管理员对课程的增删改查
8、管理员对课程评价结果的统计与删除。
9、根据学生对课程评分的高低,在学生选课时进行推荐。
2、推荐算法的实现思路
欧氏距离相似性度量
在数学中,欧几里得距离或欧几里得度量是欧几里得空间中两点间“普通”(即直线)距离。使用这个距离,欧氏空间成为度量空间。相关联的范数称为欧几里得范数。
二维空间的公式
基于用户的协同过滤算法
基于一个这样的假设“跟你喜好相似的人喜欢的东西你也很有可能喜欢。”所以基于用户的协同过滤主要的任务就是找出用户的最近邻居,从而根据最近邻居的喜好做出未知项的评分预测。这种算法主要分为3个步骤:
- 用户评分
可以分为显性评分和隐形评分两种。显性评分就是直接给项目评分(例如用户对电影评分),隐形评分就是通过评价或是购买的行为给项目评分 (例如淘宝上购买东西或者评论)。 - 寻找最近邻居
这一步就是寻找与你距离最近的用户,测算距离一般采用以下三种算法:余弦定理相似性度量、欧氏距离相似度度量和杰卡德相似性度量。后面的demo会以欧氏距离相似度度量进行说明。 - 推荐
产生了最近邻居集合后,就根据这个集合对未知项进行评分预测。把评分最高的N个项推荐给用户。
这种算法存在性能上的瓶颈,当用户数越来越多的时候,寻找最近邻居的复杂度也会大幅度的增长。
参考:https://www.jianshu.com/p/d0df3ead55a1
package cn.ltysyn.task;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import cn.ltysyn.bean.Course;
import cn.ltysyn.bean.Student;
import cn.ltysyn.service.ICourseService;
import cn.ltysyn.service.IElectiveService;
import cn.ltysyn.service.IRecommendService;
import cn.ltysyn.service.IStudentService;
@Component
public class MyCFRecomment {
//获取学生编号
@Autowired
private IStudentService studentService;
//获取课程信息
@Autowired
private ICourseService courseService;
//获取评分的信息
@Autowired
private IElectiveService electiveService;
@Autowired
private IRecommendService iRecommendService;
// //创建用户信息
// //private List<Integer> stuList = new ArrayList<Integer>();
// private static int[] stuArr = {1,2,3};
// //创建课程信息
// private static int[] couArr = {10,20,30};
// //创建评分的集合 (学生 id , 分数集合)
// private static Map<Integer,List<CourtsGoal>> goalMap = new HashMap<Integer, List<CourtsGoal>>();
@Scheduled(cron = "0 0/10 * * * ?")
public void recommend() {
//获取到所有的学生
List<Student> selectAllStudent = studentService.selectAllStudent();
//获取所有的课程
//获取评分信息 根据学生id 和课程id 获取评分信息
if(selectAllStudent.size()!=0) {
Map<Integer,List<CourtsGoal>> goalMap = new HashMap<Integer, List<CourtsGoal>>();
List<Integer> stuList = new ArrayList<Integer>();
List<Course> selectAllCourse = (List<Course>) courseService.selectAllCourse();
for(Student stu:selectAllStudent) {
List<CourtsGoal> courtsGoals = new ArrayList<CourtsGoal>();
for(Course cou:selectAllCourse) {
CourtsGoal courtsGoal = new CourtsGoal();
Integer goal = electiveService.selectByStuAndCourseId(stu.getStuId(),cou.getCourseId());
courtsGoal.setCourtsId(cou.getCourseId());
courtsGoal.setGoal(goal);
courtsGoals.add(courtsGoal);
}
//获取到学生与课程评分的关系数据
goalMap.put(stu.getStuId(), courtsGoals);
stuList.add(stu.getStuId());
}
System.out.println(goalMap);
//System.out.println(selectAllCourse);
//计算用户相似度
Map<Integer,List<List<Object>>> dataMap = calcUserSimilarity(stuList.toArray(),goalMap);
//计算课程的推荐度
Map<Integer, List<Object>> recommendCourse = calcRecommendCourse(dataMap,goalMap);
//处理推荐电影列表
Map<Integer, List<Object>> handleRecommendCourse = handleRecommendCourse(recommendCourse,goalMap);
//删除所有推荐列表信息
delectAllRecommendCourse();
//保存推荐列表信息
saveRecommendCourse(handleRecommendCourse);
//删除重复的推荐信息
//repeatRecomendCourse();
}else {
}
}
private void repeatRecomendCourse() {
// TODO Auto-generated method stub
iRecommendService.repeatRecomendCourse();
}
private void delectAllRecommendCourse() {
// TODO Auto-generated method stub
iRecommendService.delectAllRecommendCourse();
}
private void saveRecommendCourse(Map<Integer, List<Object>> handleRecommendCourse) {
// TODO Auto-generated method stub
iRecommendService.saveRecommendCourse(handleRecommendCourse);
}
/*
* public static void main(String[] args) { System.out.println(goalMap);
* //计算用户相似度 Map<Integer,List<List<Object>>> dataMap =
* calcUserSimilarity(stuArr,goalMap); //计算课程的推荐度 Map<Integer, List<Object>>
* recommendCourse = calcRecommendCourse(dataMap,goalMap); //处理推荐电影列表
* handleRecommendCourse(recommendCourse,goalMap); }
*/
private static Map<Integer, List<Object>> handleRecommendCourse(Map<Integer, List<Object>> recommendCourse,Map<Integer,List<CourtsGoal>> goalMap) {
Map<Integer, List<Object>> handleRecommendCourse = new HashMap<Integer, List<Object>>();
for(Map.Entry<Integer,List<Object>> reco:recommendCourse.entrySet()) {
//拿到推荐列表
List<Object> re_l = reco.getValue();
List<Object> handleCourse = new ArrayList<Object>();
for(Object obj:re_l) {
List<CourtsGoal> list = goalMap.get(reco.getKey());
for(CourtsGoal c_goal:list) {
if(Integer.parseInt(obj.toString())==c_goal.getCourtsId()) {
if(c_goal.getGoal()==0) {
handleCourse.add(c_goal.getCourtsId());
}
}
}
}
handleRecommendCourse.put(reco.getKey(), handleCourse);
}
System.out.println("最终推荐列表"+handleRecommendCourse);
return handleRecommendCourse;
}
/*
* 计算用户相似度
* 返回最相近的两个
*/
public static Map<Integer,List<List<Object>>> calcUserSimilarity(Object[] stuArr_p,Map<Integer,List<CourtsGoal>> goalMap_p) {
//similarityUsers=new ArrayList();
//遍历学生 求出当前学生与其他学生的相似度
//相似用户集合
Map<Integer,List<List<Object>>> dataMap = new HashMap<Integer, List<List<Object>>>();
for(Object stu:stuArr_p) {
//取两个相似的
List<List<Object>> similarityUsers= new ArrayList();
List<List<Object>> userSimilaritys=new ArrayList<List<Object>>();
//遍历goalMap_p
for(Map.Entry<Integer,List<CourtsGoal>> goal:goalMap_p.entrySet()) {
//如果当前的学生 和 存储的 key相等 则跳过
if(stu.toString().equals(goal.getKey().toString())) {
continue;
}
List<Object> userSimilarity=new ArrayList<Object>();
//记录当前的学生编号
userSimilarity.add(goal.getKey());
userSimilarity.add(calcTwoUserSimilarity(goal.getValue(),goalMap_p.get((Integer)stu)));
userSimilaritys.add(userSimilarity);
}
sortCollection(userSimilaritys);
System.out.println("与"+stu+"的相似度为:"+userSimilaritys);
similarityUsers.add(userSimilaritys.get(0));
similarityUsers.add(userSimilaritys.get(1));
dataMap.put((Integer)stu, similarityUsers);
}
System.out.println(dataMap);
//表示该学生 与其他两个学生的相似度为多少
return dataMap;
}
/**
* 获取全部推荐课程,计算平均课程推荐度
*/
private static Map<Integer,List<Object>> calcRecommendCourse(Map<Integer,List<List<Object>>> dataMap,Map<Integer,List<CourtsGoal>> goalMap){
Map<Integer,List<List<Object>>> cf_map = new HashMap<Integer, List<List<Object>>>();
//存储没有课程的总的推荐分数
Map<Integer,Double> cf_sumRate = new HashMap<Integer, Double>();
//遍历dataMap 分别拿到不同的学生 推荐的课程
for(Map.Entry<Integer,List<List<Object>>> data:dataMap.entrySet()) {
double recommdRate=0,sumRate=0;
//拿到的是哪个用户 第一个
//data.getValue().get(0).get(0);
//拿到该用户的相识度值 第一个
double xs_1 = Double.parseDouble(data.getValue().get(0).get(1).toString());
//拿到的是哪个用户 第二个
//data.getValue().get(1).get(0);
//拿到该用户的相识度值 第二个
double xs_2 = Double.parseDouble(data.getValue().get(1).get(1).toString());
List<CourtsGoal> list_1 = goalMap.get(data.getValue().get(0).get(0));
List<CourtsGoal> list_2 = goalMap.get(data.getValue().get(1).get(0));
if(list_1.size()==list_2.size()) {
List<List<Object>> recommendCourts = new ArrayList<List<Object>>();
for(int i=0;i<list_1.size();i++) {
List<Object> recommendCourt=new ArrayList();
recommdRate = list_1.get(i).getGoal() * xs_1 + list_2.get(i).getGoal() * xs_2;
//添加课程
recommendCourt.add(list_1.get(i).getCourtsId());
//添加该课程推荐度
recommendCourt.add(recommdRate);
//被推荐的用户 、课程、课程的推荐度
//System.err.println("用户"+data.getKey()+"课程"+list_1.get(i)+":"+recommdRate);
recommendCourts.add(recommendCourt);
sumRate+=recommdRate;
}
cf_map.put(data.getKey(), recommendCourts);
cf_sumRate.put(data.getKey(), sumRate);
}
//for(CourtsGoal cGoal:list_1) {
//System.out.println("给用户"+data.getKey()+"推荐的用户是:"+data.getValue().get(0).get(0)+"相似值是:"+data.getValue().get(0).get(1)+"课程信息"+cGoal.getCourtsId()+"评分"+cGoal.getGoal());
//}
}
System.err.println(cf_map);
System.out.println(cf_sumRate);
//当前集合存放的是 给 key 推荐的课程集合
Map<Integer,List<Object>> target_map = new HashMap<Integer, List<Object>>();
for(Map.Entry<Integer,List<List<Object>>> cf_d:cf_map.entrySet()) {
List<Object> targetRecommendCourts = new ArrayList<Object>();
for(List<Object> obj:cf_d.getValue()) {
if(Double.parseDouble(obj.get(1).toString()) > cf_sumRate.get(cf_d.getKey())/cf_d.getValue().size()){ //大于平均推荐度的商品才有可能被推荐
targetRecommendCourts.add(obj.get(0));
}
}
target_map.put(cf_d.getKey(), targetRecommendCourts);
}
System.out.println("最终:"+target_map);
return target_map;
}
/**
* 根据用户数据,计算用户相似度(欧氏距离)
* @param user1Stars 其他用户评价分数
* @param user2Starts 当前用户评价的分数
* @return
*/
private static double calcTwoUserSimilarity(List<CourtsGoal> user1Stars,List<CourtsGoal> user2Starts){
float sum=0;
for(int i=0;i<user1Stars.size();i++){
sum+=Math.pow(user1Stars.get(i).getGoal()-user2Starts.get(i).getGoal(),2);//平方
}
return Math.sqrt(sum);//开方
}
/**
* 集合排序
* @param list
*/
private static void sortCollection(List<List<Object>> list){
Collections.sort(list, new Comparator<List<Object>>() {
@Override
public int compare(List<Object> o1, List<Object> o2) {
if(Double.valueOf(o1.get(1).toString()) > Double.valueOf(o2.get(1).toString())){
return 1;
}else if(Double.valueOf(o1.get(1).toString()) < Double.valueOf(o2.get(1).toString())){
return -1;
}else{
return 0;
}
}
});
}
}
系统功能截图
等
微信可以查看这个视频的演示