当前位置: 首页 > 编程日记 > 正文

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

新版本教务管理系统

教务管理系统 选课功能

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 {//获取学生编号@Autowiredprivate IStudentService studentService;//获取课程信息@Autowiredprivate ICourseService courseService;//获取评分的信息@Autowiredprivate IElectiveService electiveService;@Autowiredprivate 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 stubiRecommendService.repeatRecomendCourse();}private void delectAllRecommendCourse() {// TODO Auto-generated method stubiRecommendService.delectAllRecommendCourse();}private void saveRecommendCourse(Map<Integer, List<Object>> handleRecommendCourse) {// TODO Auto-generated method stubiRecommendService.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>>() {@Overridepublic 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;}}});}}

系统功能截图

相关文章:

linux access函数判断文件存取权限

access&#xff08;判断是否具有存取文件的权限&#xff09;相关函数 stat&#xff0c;open&#xff0c;chmod&#xff0c;chown&#xff0c;setuid&#xff0c;setgid表头文件 #include<unistd.h>定义函数 int access(const char * pathname,int mode);函数说明 access(…

Python天天美味(35) - 细品lambda

lambda函数也叫匿名函数&#xff0c;即&#xff0c;函数没有具体的名称。先来看一个最简单例子&#xff1a; deff(x): returnx**2printf(4)Python中使用lambda的话&#xff0c;写成这样 g lambdax : x**2printg(4)lambda表达式在很多编程语言都有对应的实现。比如C#&#x…

jvm:分析工具

bin/jvisualvm.exe 可查看类实例数 bin/jconsole.exe 监控线程&#xff0c;堆&#xff0c;等 http://blog.csdn.net/yaowj2/article/details/7107818 https://blog.csdn.net/janekeyzheng/article/details/41075791 转载于:https://www.cnblogs.com/chen-msg/p/8275299.html

Django 路由分发

Django 路由分发 当一个url请求过来之后1、先到项目主目录下的urls内。2、由这个url做处理分发给其他app内的urls。 一级路由&#xff1a;主目录urls内引入include from django.conf.urls import url,include urlpatterns [# 指定分发的app目录名称url(r^cmdb/,include("…

NHibernate从入门到精通系列(7)——多对一关联映射

内容摘要 多对一关联映射概括 多对一关联映射插入和查询 多对一关联映配置介绍 一、多对一关联映射概括 关联关系是实体类与实体类之间的结构关系&#xff0c;分别为“多对一”、“一对一”、“多对多”。然而“多对一”是怎样描述的呢&#xff1f;让我们参考图1.1所示&#xf…

PLSQ执行同样的sql,使用mybatis进行动态拼装执行的时候非常慢的问题解决

如题&#xff0c;项目中碰到了同样的sql&#xff0c;在plsql中执行很快&#xff0c;几乎秒出&#xff0c;但在程序中使用mybatis框架时&#xff0c;却非常的慢&#xff0c;前提是动态拼装的sql。在使用写死的参数&#xff0c;不会出现很慢的效果。最后发现是使用 #{xxx} 去注入…

雨林木风爱好者GHOSTXP装机版_NTFS_SP3_2010_03

系统简介:系统下载参考地址:http://www.51ghostxp.cn/winxp/230.htm迅雷地址:thunder://QUFodHRwOi8vZG93bjUuZ2hvc3QyLmNuL0dIT1NUWFBfU1AzeWxtZmFpaGFvemhlXzIwMTBfMDNbTlRGU10uaXNvWlo雨林木风爱好者GHOSTXP装机版_NTFS_SP3_2010_03主要特点&#xff1a; 此系统采用硬盘安装…

栈和堆的区别(转 知乎)

①管理方式&#xff1a;栈由编译器自动管理&#xff1b;堆由程序员控制&#xff0c;使用方便&#xff0c;但易产生内存泄露。 ②生长方向&#xff1a;栈向低地址扩展(即”向下生长”)&#xff0c;是连续的内存区域&#xff1b;堆向高地址扩展(即”向上生长”)&#xff0c;是不连…

到这个年纪为什么我还要开始学习理解参与区块链?

【顺势而为&#xff0c;与时俱进的需要】看来我对信息的敏感程度还不够&#xff0c;最近才认真研读了相关文件&#xff1a;国务院关于印发“十三五”国家信息化规划的通知国发〔2016〕73号1信息技术创新代际周期大幅缩短&#xff0c;创新活力、集聚效应和应用潜能裂变式释放&am…

JavaScript小记

项目进行中需求变化&#xff0c;把所有的图片的呈现出渐出效果&#xff0c;我首先想到了filters&#xff1a;滤镜&#xff0c; <html><head> </head> <script language"JavaScript">function picload(){img.filters.revealTrans.apply()im…

Ant Design of Vue —— setFieldsValue方法 动态操作Switch组件

在开发中经常使用Form组件管理表单&#xff0c;这次想通过form提供的setFieldsValue()方法动态改变Switch组件状态&#xff0c;却没有生效。 加入valuePropName属性之后&#xff0c;就可以使用动态操作Switch组件了。 <a-switch v-decorator"[haveChildren, { valuePro…

打印机的大小设置

procedure SetPrinterPaper(APaperNo: Integer; APaperWidth, APaperHeight: Double); //设置当前打印机的纸张大小 //纸张号 &#xff19; A4 13 B5 //页宽和页高&#xff0c;单位mm var Device: array[0..255] of char; Driver: array[0..255] of char; Port: array[0..255]…

C# 对ListT取交集、连集及差集

實際演練 ※本文使用int為例&#xff0c;若為使用自訂之DataModel&#xff0c;需實作IEquatable<T>介面才能使用 1. 取交集 (A和B都有) List A : { 1 , 2 , 3 , 5 , 9 } List B : { 4 , 3 , 9 } var intersectedList list1.Intersect(list2);結果 : { 3 , 9 } 判断A和B…

面试前赶紧看了5道Python Web面试题,Python面试题No17

目录 本面试题题库&#xff0c;由公号&#xff1a;非本科程序员 整理发布第1题&#xff1a; Flask中的请求上下文和应用上下文是什么?第2题&#xff1a;django中间件的使用&#xff1f;第3题&#xff1a; django开发中数据做过什么优化&#xff1f;第4题&#xff1a; 解释一下…

最好的程序界面就是用户无需去阅读操作手册就知道该如何使用的界面

最好的程序界面就是用户无需去阅读操作手册就知道该如何使用的界面。 原则 1.一致性 如果你可以在一个列表的项目上双击后能 够弹出对话框&#xff0c;那么应该在任何列表中双击都能弹出对话框。要有统一的字体写号、统一的色调、统一的提示用词、窗口在统一的位置、按钮也在窗…

Ant Design Vue中a-select组件下拉列表在局部滚动时不跟随问题解决方法

问题如下&#xff1a; 修改方法如下 <a-tree-select:getPopupContainer"triggerNode > {return triggerNode.parentNode || document.body;}"style"width: 100%":dropdownStyle"{ maxHeight: 400px, overflow: auto }":treeData"tree…

ospf路由汇总的目的

ospf路由汇总的目的减少网络中lsa传输的数量&#xff0c;减少网络中的变化&#xff0c;减少链路状态数据库&#xff0c;减少路由表&#xff0c;大大提高数据包查表转发的能力&#xff0c;能减少因为链路状态数据库的变化而引起的spf算法的重计算。转载于:https://blog.51cto.co…

第九周学习笔记

聚合&#xff1a; 1.当聚集函数遇到空值时&#xff0c;除COUNT(*)外&#xff0c;都跳过空值而出处理非空值。 2.WHERE子句中不能使用聚集函数。 3.聚集函数只能用于SELECT和GROUP BY中的HAVING语句。 常用的聚合函数 1 count 2 sum 3 avg 4 max 5 min 使…

2019年5月26日

距离考核结束的时间只剩下5天了&#xff0c;一个特别大的问题就是前后端的交流问题&#xff0c;不单单是项目的进度&#xff0c;更多的是人与人之间的交流。页面的进度完成了一整个流程&#xff0c;剩下的就是和后端的进行交互&#xff0c;后端的进度有点慢&#xff0c;导致我现…

Django3.0 +Python3 连接mysql遇到django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer

使用idea 创建了一个Django项目&#xff0c;不想使用默认的数据库进行操作&#xff0c;想切换成mysql数据&#xff0c;在此遇到了一些问题特此记录。 按照网上所说使用pymysql进行数据库连接操作&#xff0c;安装并且配置完毕后&#xff0c;在执行数据迁移操作时报错 django.…

栈的push、pop序列

题目&#xff1a;输入两个整数序列。其中一个序列表示栈的push顺序&#xff0c;判断另一个序列有没有可能是对应的pop顺序。为了简单起见&#xff0c;我们假设push序列的任意两个整数都是不相等的。 比如输入的push序列是1、2、3、4、5&#xff0c;那么4、5、3、2、1就有可能是…

const常对象成员与常成员函数

#include <iostream>#include <string>using namespace std;class pt{public:   pt(int a,int b){this->xa,this->yb;}   int getX() const {return this->x;} //此处必须加const&#xff08;关键&#xff09;&#xff0c;否则A.getX()将无法正常调用…

根据CPU核数合理设置线程池大小

一般来说池中总线程数是核心池线程数量两倍&#xff0c;只要确保当核心池有线程停止时&#xff0c;核心池外能有线程进入核心池即可。 我们所需要关心的主要是核心池线程的数量该如何设置。 自定义线程池代码 package com.lc.concurrent; import java.util.concurrent.ArrayB…

云答题微信小程序 实现 前端加后台管理

1.为什么要使用微信云开发 微信云开发自己是不需要域名&#xff08;备案的域名&#xff09;&#xff0c;服务器&#xff0c;搭建数据库等。 2.使用技术 微信小程序相关云接口java &#xff08;SpringBootMaven&#xff09;后台管理使用 VUE 3.实现微信小程序效果如下 微信小…

用Windows Media Player截图的方法

视频截图方法&#xff1a; 关闭“视频加速功能即可”。 以Windows Media Player 9.0为例&#xff0c;选择菜单“工具→选项”&#xff0c;找到“性能”选项卡中的“视频加速”一栏&#xff0c;然后拖动下方的拉杆将默认的“完成”改为“无”&#xff0c;按“确定”保存设置&…

基于OHCI的USB主机 —— 结束语

从去年11月份开始连载的《基于OHCI的USB主机》系列总算告一段落了&#xff0c;到UFI命令层为止&#xff0c;所有USB主机的底层处理就结束了&#xff0c;再上面就是磁盘读写、文件系统、文件读写和应用系统了。这些上层应用基本上是与USB主机没有什么关系的&#xff0c;我的这个…

mac os x 查看网络端口情况

查看端口是否打开 使用 netstat 命令 a. netstat -nat | grep <端口号> , 如命令 netstat -nat | grep 3306 b. netstat -nat |grep LISTEN 使用 lsof 命令 # yongfu-pro at yongfu-pro.local in ~ [22:39:32] $ lsof -n -P -i TCP -s TCP:LISTEN COMMAND PID USER FD …

POJ1149-PIGS

1&#xff0c;从各个顾客到汇点各有一条边&#xff0c;容量就是各个顾客能买的数量上限。 2&#xff0c;在某一轮中&#xff0c;从该顾客打开的所有猪圈都有一条边连向该顾客&#xff0c;容量都是∞。 3&#xff0c;最后一轮除外&#xff0c;从每一轮的i号猪圈都有一条边连向下…

Center OS 离线安装Mysql5.7

1.相关资料准备 1.离线安装包下载 或者百度网盘 链接&#xff1a;https://pan.baidu.com/s/1oVP3u8GSaavxoJqKnvZPKg 提取码&#xff1a;vw88 2.libao库文件下载 链接&#xff1a;https://pan.baidu.com/s/12hrQIEF3kk2h2HlWE41G7w 提取码&#xff1a;fvt8 2.开始安装 检…

offsetTop,clientX,clientTop,clientWidth,offsetWidth 坐标,一次弄明白

这几个属性都是IE火狐完全兼容的&#xff0c;不多说&#xff0c;看我测试结果便知&#xff1a; 【源码如下】 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <ht…