基于协同过滤算法实现选课推荐系统

新版本教务管理系统

教务管理系统 选课功能

1.系统功能

1、用户账户管理
2、学生个人信息的查看与修改
3、学生的网上选课与课程的评分
4、教师个人信息的查看与修改
5、教师对学生课程评价结果的查看
6、管理员对学生信息与教师信息的查看与添加
7、管理员对课程的增删改查
8、管理员对课程评价结果的统计与删除。
9、根据学生对课程评分的高低,在学生选课时进行推荐。

2、推荐算法的实现思路

欧氏距离相似性度量

在数学中,欧几里得距离或欧几里得度量是欧几里得空间中两点间“普通”(即直线)距离。使用这个距离,欧氏空间成为度量空间。相关联的范数称为欧几里得范数。
二维空间的公式

基于用户的协同过滤算法

基于一个这样的假设“跟你喜好相似的人喜欢的东西你也很有可能喜欢。”所以基于用户的协同过滤主要的任务就是找出用户的最近邻居,从而根据最近邻居的喜好做出未知项的评分预测。这种算法主要分为3个步骤:

  1. 用户评分
    可以分为显性评分和隐形评分两种。显性评分就是直接给项目评分(例如用户对电影评分),隐形评分就是通过评价或是购买的行为给项目评分 (例如淘宝上购买东西或者评论)。
  2. 寻找最近邻居
    这一步就是寻找与你距离最近的用户,测算距离一般采用以下三种算法:余弦定理相似性度量、欧氏距离相似度度量和杰卡德相似性度量。后面的demo会以欧氏距离相似度度量进行说明。
  3. 推荐
    产生了最近邻居集合后,就根据这个集合对未知项进行评分预测。把评分最高的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;
                }
            }
        });
    }
	
}

系统功能截图

 

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

古月_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值