基於協同過濾算法實現選課推薦系統


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;
                }
            }
        });
    }
	
}

系統功能截圖

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM