我们在使用各类型的软件的时候,总是能在各大app中获取到推荐信息的数据,而且会发现推荐的信息数据还比较适合个人的口味,例如说某些共同兴趣爱好的好友推荐,某些好听的音乐推荐等等。

在进行推荐系统的核心算法介绍之前,我们需要先来回顾一下以前所学过的数学知识内容。

欧几里得距离

二维的欧几里得距离:

例如下图所示,在这样的一个简单的二维空间图里面,根据对于a点的坐标和b点的坐标进行二维空间距离的计算,假设p为点a到点b的欧式距离,那么可以根据勾股定理来计算出两点之间的向量距离为:

三维空间的欧几里得距离:

除了常见的二维空间之外,常用于的计算场景还有可能是基于三维空间运算的。

在这种场景下,假设计算A点和B点之间的距离为p,那么计算可以得出p的值为:

在了解了这些基本的知识点之后,我们再结合实际的应用场景来展开应用。

例如说一个电影影评网站,需要加入一个推荐喜欢观看同类电影的好友功能。

首先模拟出一个具体的数据场景:

1对该电影进行过评价,0没有对该电影进行过评价

有了这样的一个数据统计场景之后,我们可以根据对电影是否有共同评价进行共同兴趣爱好的匹配推荐。但是这种场景下也有一定的缺陷,那就是对于电影的评价有好有坏,需要将共同喜爱同一类电影的用户进行匹配推荐,将不喜欢同一类电影的用户进行匹配推荐就属于推荐失误的场景了。

改进点

在用户评论里面加入对于电影的打分功能,我们将打分等级也进行一个分类

那么我们将这里的打分等级和上述的电影评价相互结合之后便可得出下表:

根据上述的这张表,我们再回顾到本文开始时候所说的二维和三维空间里面的欧几里得距离计算。

假设A点的坐标为A(a1,a2…),B点坐标为B(b1,b2…)

二维空间距离计算:

三维空间距离计算:

类比一维、二维、三维的表示方法,n 维空间中的某个位置,我们可以写作(X1X1,X2X2,X3X3,…,XKXK)。这种表示方法我们称之为向量。

n维空间的距离计算:

那么集合上边的具体应用场景,我们便可以展开相应的计算了:

首先罗列出每个用户的空间坐标

小明(5,-1,-1,4,-1,-1,3,-1,1)(当前用户)

小王(4,-1,3,2,5,-1,-1,5,-1)

小东(-1,5,-1,-1,2,2,-1,-1,2)

小红(2,5,-1,3,3,-1,4,5,-1)

小乔(-1,-1,-1,-1,-1,-1,-1,5,-1)

小芳(-1,4,-1,3,3,5,5,-1,4)

然后再通过计算的时候,假设当前用户是小明,那么我们再进行用户匹配推荐的时候需要计算各个点和小明的欧几里得距离:

套用以下公式:

计算出小王和各个人之间的向量差值,值越小,即表示两者之间的相似度越高。

计算出来小王相对于小明的向量差为:

小东相对于小明的向量差为:

等等….

说了这么多,还是用实际的代码案例来进行讲解会好些。

首先是 网站会员,电影信息,影评 三种基本模型

importlombok.AllArgsConstructor;importlombok.Data;/***@authoridea

* @date 2019/5/4

* @Version V1.0*/@Data

@AllArgsConstructorpublic classMemberPO {private intid;privateString memberName;

}importlombok.AllArgsConstructor;importlombok.Data;/***@authoridea

* @date 2019/5/4

* @Version V1.0*/@Data

@AllArgsConstructorpublic classMoviePO {private intid;privateString movieName;

}importlombok.AllArgsConstructor;importlombok.Data;/***@authoridea

* @date 2019/5/4

* @Version V1.0*/@Data

@AllArgsConstructorpublic classMovieReviewPO {private intmovieId;private intmemberId;private intreviewScore;

}

为了方便,这里的数据暂时用模拟的形式展示,忽略了从数据库读取的环节:

importcom.sise.model.MoviePO;importorg.springframework.stereotype.Service;importjava.util.ArrayList;importjava.util.Arrays;importjava.util.List;/***@authoridea

* @date 2019/5/4

* @Version V1.0*/@Servicepublic classMovieService {public static List MOVIE_LIST = new ArrayList<>();static{

List movieNames = Arrays.asList("绿皮书", "复仇者联盟", "月光男孩", "海边的曼彻斯特","盗梦空间", "记忆碎片", "致命魔术", "流浪地球", "正义联盟");int id = 0;for(String movieName : movieNames) {

MOVIE_LIST.add(new MoviePO(id++, movieName));

}

}/*** 根据名称获取用户信息

*

*@paramname

*@return

*/

publicMoviePO getMovieByName(String name) {return MOVIE_LIST.stream().filter(moviePO ->{returnmoviePO.getMovieName().equals(name);

}).findFirst().get();

}

}importcom.sise.model.MemberPO;importorg.springframework.stereotype.Service;importjava.util.ArrayList;importjava.util.Arrays;importjava.util.List;/***@authoridea

* @date 2019/5/4

* @Version V1.0*/@Servicepublic classMemberService {public static List MEMBER_LIST = new ArrayList<>();static{

List memberNameS = Arrays.asList("小明", "小王", "小东", "小红", "小乔", "小芳");int id = 0;for(String memberName : memberNameS) {

MEMBER_LIST.add(new MemberPO(id++, memberName));

}

}/*** 根据名称获取用户信息

*

*@paramname

*@return

*/

publicMemberPO getMemberByName(String name) {return MEMBER_LIST.stream().filter(memberPO ->{returnmemberPO.getMemberName().equals(name);

}).findFirst().get();

}

}

用户对电影打分的数据是存储在了Redis里面的,这里的为了方便,所以创建了一个mock使用的测试接口:

首先需要配置好SpringBoot和RedisTemplate,这部分的配置比较简单,这里暂时就先省略了。

电影评论service

importcom.sise.model.MemberPO;importcom.sise.model.MoviePO;importcom.sise.model.MovieReviewPO;importlombok.extern.slf4j.Slf4j;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.stereotype.Service;importjavax.annotation.Resource;import java.util.*;/***@authoridea

* @date 2019/5/4

* @Version V1.0*/@Service

@Slf4jpublic classMovieReviewService {

@Resourceprivate RedisTemplateredisTemplate;public voidmockData(MemberPO memberPO, MoviePO moviePO, Integer score) {

Map scoreMap =redisTemplate.opsForHash().entries(String.valueOf(memberPO.getId()));if (scoreMap == null) {

scoreMap= new HashMap<>();

}

scoreMap.put(moviePO.getId(), score);

redisTemplate.opsForHash().putAll(String.valueOf(memberPO.getId()), scoreMap);

log.info("[MovieReviewService]保存信息成功!");

}/*** 获取到list类型的统计数目

*

*@parammemberId

*@return

*/

public List getScoreList(intmemberId) {

Map scoreMap =redisTemplate.opsForHash().entries(String.valueOf(memberId));

List result = newArrayList();

Map sortMap = new TreeMap(new Comparator() {

@Overridepublic intcompare(Integer obj1, Integer obj2) {//降序排序

returnobj2.compareTo(obj1);

}

});for(Object key : scoreMap.keySet()) {

Integer movieIndex=(Integer) key;

Integer score=(Integer) scoreMap.get(key);

sortMap.put(movieIndex, score);

}for(Object key : sortMap.keySet()) {

result.add(sortMap.get(key));

}returnresult;

}

}

然后是mock评论数据的接口

importcom.sise.model.MemberPO;importcom.sise.model.MoviePO;importcom.sise.service.MemberService;importcom.sise.service.MovieReviewService;importcom.sise.service.MovieService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;importjava.util.Arrays;importjava.util.List;importjava.util.Map;importjava.util.stream.Collectors;/***@authoridea

* @date 2019/5/4

* @Version V1.0*/@RestControllerpublic classMockDataController {

@AutowiredprivateMovieReviewService movieReviewService;

@AutowiredprivateMemberService memberService;

@AutowiredprivateMovieService movieService;

@GetMapping(value= "/mockData")publicString mockData() {

List list =MovieService.MOVIE_LIST

.stream()

.map(moviePO->moviePO.getMovieName())

.collect(Collectors.toList());//不同的用户打分程度匹配不一致

List score = Arrays.asList(-1, 4, -1, 3, 3, 5, 5, -1, 4);

String name="小芳";int index = 0;for(String movieName : list) {this.mockData(name, movieName, score.get(index));

index++;

}return "success";

}private void mockData(String memberName, String movieName, intscore) {

MemberPO memberPO=memberService.getMemberByName(memberName);

MoviePO moviePO=movieService.getMovieByName(movieName);

movieReviewService.mockData(memberPO, moviePO, score);

System.out.println(memberPO.toString()+ " " +moviePO.toString());

}

}

有了基本的测试数据之后,便可以来对核心的向量计算模块进行编写代码了:

importcom.sise.model.MemberPO;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;import java.util.*;importjava.util.stream.Collectors;/*** 推荐的核心部分

*

*@authorlinhao

* @date 2019/5/4

* @Version V1.0*/@Servicepublic classRecommendService {

@AutowiredprivateMovieReviewService movieReviewService;/*** 计算两个用户之间的爱好相似度

*

*@paramcurrentMemberId

*@paramcompareMemberId

*@returndouble degree 相似度*/

public double countSimilarityDegree(int currentMemberId, intcompareMemberId) {

List currentIndexList =movieReviewService.getScoreList(currentMemberId);

List compareMemberList =movieReviewService.getScoreList(compareMemberId);//两个人的评分统计是相同个数的

if (currentIndexList.size() ==compareMemberList.size()) {int total =MovieService.MOVIE_LIST.size();int result = 0;//计算向量的和

for (int i = 0; i < total; i++) {int x1 =currentIndexList.get(i);int x2 =compareMemberList.get(i);

result= result + (int) Math.pow((x1 - x2), 2);

}double degree =Math.sqrt(result);returndegree;

}return 0;

}/*** 计算爱好相似的用户 从高往底

*

*@paramcurrentMemberId

*@returnList*/

public List countSimilarityList(intcurrentMemberId) {

List idList =MemberService.MEMBER_LIST

.stream()

.filter(memberPO-> memberPO.getId() !=currentMemberId)

.map(MemberPO::getId)

.collect(Collectors.toList());

Map hashMap = new HashMap<>();for(Integer memberId : idList) {double degree =countSimilarityDegree(currentMemberId, memberId);

hashMap.put(memberId, degree);

}//这里将map.entrySet()转换成list

List> list = new ArrayList<>(hashMap.entrySet());//然后通过比较器来实现排序

Collections.sort(list,new Comparator>() {//升序排序

@Overridepublic int compare(Map.Entryo1,

Map.Entryo2) {returno2.getValue().compareTo(o1.getValue());

}

});returnlist;

}

}

测试所用的接口

importcom.sise.service.RecommendService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;importjava.util.List;/***@authoridea

* @date 2019/5/4

* @Version V1.0*/@RestControllerpublic classRecommendController {

@AutowiredprivateRecommendService recommendService;

@GetMapping(value= "count")public List countDegree(intcurId) {returnrecommendService.countSimilarityList(curId);

}

}

通常我们会给用户相似度设置一个阈值,当相似程度超过该阈值的时候,就会被引入到好友推荐列表中做成推荐人名单。

推荐系统这个典型案例的思路让我们明白了向量的强大之处,这也是数据结构和算法所具有的魅力,利用向量空间来计算出欧几里得距离,从而解决掉如此复杂的问题。

上述的代码案例只能说是一个简单的模型,真实生产中的实践可要比这复杂得多,比如说针对于初期应用程序的基础数据量不足的情况下,使用这类方式来做推荐功能可能会有点牵强,因此还是需要在落地实践中不断的尝试和探索。

java实现推荐算法实例_从技术角度分析推荐系统案例相关推荐

  1. java实现lz77算法实例_数据压缩算法---LZ77算法 的分析与实现

    LZ77简介 Ziv和Lempel于1977年发表题为"顺序数据压缩的一个通用算法(A Universal Algorithm for Sequential Data Compression ...

  2. 从技术角度分析推荐系统案例

    我们在使用各类型的软件的时候,总是能在各大app中获取到推荐信息的数据,而且会发现推荐的信息数据还比较适合个人的口味,例如说某些共同兴趣爱好的好友推荐,某些好听的音乐推荐等等. 在进行推荐系统的核心算 ...

  3. 主要推荐系统算法总结及Youtube深度学习推荐算法实例概括

    主要推荐系统算法总结及Youtube深度学习推荐算法实例概括 By ZhuZhiboSmith2017年7月09日 17:00 现如今,许多公司使用大数据来做超级相关推荐,并以此来增加收益.在海量推荐 ...

  4. 算法实例_线性表 By:比方

    算法实例_线性表 By:比方 什么是线性表? 从线性表的功能逻辑上来看,线性表就是由n(n>=0)个数据元素的排序组合,数据由x1,x2,x3,...,xn结构有序的顺序排列. 线性表的结构和特 ...

  5. 基于用户的协同过滤推荐算法原理和实现分析

    本文转载自nieson  基于用户的协同过滤推荐算法原理和实现 在推荐系统众多方法中,基于用户的协同过滤推荐算法是最早诞生的,原理也较为简单.该算法1992年提出并用于邮件过滤系统,两年后1994年被 ...

  6. 推荐算法炼丹笔记:序列化推荐系统

    作者:一元 公众号:炼丹笔记 特约审稿:杰少 背景 序列推荐系统(SRS)不同于传统推荐系统(RSs)包括协同过滤和基于内容的过滤,SRSs试图理解和建模用户的连续行为.用户与物品之间的交互作用以及用 ...

  7. java 好友推荐 算法_基于jsp的好友推荐-JavaEE实现好友推荐 - java项目源码

    基于jsp+servlet+pojo+mysql实现一个javaee/javaweb的好友推荐, 该项目可用各类java课程设计大作业中, 好友推荐的系统架构分为前后台两部分, 最终实现在线上进行好友 ...

  8. 阿里:Java工程师,算法工程师,数据挖掘分析工程师、测试开发工程师

    特别说明:1. 此信息长期有效!!!                        2. 直接走内推通道,优先安排建立评估及面试,快人一步!!!                        3. 大 ...

  9. mahout java api_Mahout推荐算法API详解

    前言 用Mahout来构建推荐系统,是一件既简单又困难的事情.简单是因为Mahout完整地封装了"协同过滤"算法,并实现了并行化,提供非常简单的API接口:困难是因为我们不了解算法 ...

  10. 推荐算法之用户行为分析

    一.分析用户行为的必要性 用户行为分析是很多算法设计的基础,早期的如统计热门排序,虽然操作简单但是很受大多数用户的喜欢,因为这可以很容易节省用户发现自己喜爱事物的时间.后来的如推荐算法是对用户行为更加 ...

最新文章

  1. WebForm服务器验证控件与前端js自定义验证共同使用
  2. Python的模块化编程
  3. java里的daosupport_HibernateDaoSupport与JdbcDaoSupport
  4. 想说爱你不容易 | 使用最小 WEB API 实现文件上传
  5. hiveql函数笔记(二)
  6. Java、Android引用类型
  7. 什么是super?如何使用super调用超类构造函数?
  8. 浏览器地址栏传中文乱码
  9. MFC 窗口过程理解
  10. 40多套 在线教育平台系统网站源码 知识付费源程序 网上教育
  11. JavaScript基础视频教程总结(001-010章)
  12. 怎么让电脑微信安装到别的盘路径
  13. PMP知识点总结—计算题汇总
  14. Office软件Android无法登录,office mobile(安卓版)无法使用
  15. linphone源码下载及下载失败解决方法
  16. AI换脸-简单换脸、人脸对齐、关键点定位与画图
  17. 光模块价格由带宽还是距离决定_佛山单模光模块价格
  18. react中使用构建缓存_如何使用React构建Chatbot
  19. 搜狗联盟开展“全民捉虫计划”打造诚信联盟
  20. Android项目实战记录

热门文章

  1. 高山仰止,景行行止。虽不能至,然心向往之。
  2. jieba分词原理 ‖ 关键词抽取
  3. mt4虚拟服务器账户无效,外汇登录显示无效账户怎么办?
  4. python数据分析案例简单实战项目(二)--疫情数据分析
  5. 软件测试PC端web通用测试用例
  6. 主打产品“火力不足”致使发行人持续盈利能力下降,这公司创业板IPO被终止
  7. mac备忘录html,MAC 使用备忘录
  8. ubuntu 16 打开 dmg 文件
  9. Flash鼠标拖尾效果——粒子
  10. 实例99:使用AEGAN对MNIST数据集压缩特征及重建