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;
}
}
});
}
}
系統功能截圖
等