From b924b78dbdfb5ec215429d3aa0863d284e96eddb Mon Sep 17 00:00:00 2001 From: tarzan Date: Tue, 7 Feb 2023 13:24:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E4=BA=8E=E7=94=A8=E6=88=B7=E5=92=8C?= =?UTF-8?q?=E5=9F=BA=E4=BA=8E=E7=89=A9=E5=93=81=E7=9A=84=E6=8E=A8=E8=8D=90?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recommend/RecommendSystemApplication.java | 7 +- .../com/tarzan/recommend/core/CoreMath.java | 72 +++++++------------ .../com/tarzan/recommend/core/ItemCF.java | 39 ++++++++++ .../com/tarzan/recommend/core/UserCF.java | 45 ++++++++++++ .../com/tarzan/recommend/dto/RelateDTO.java | 4 +- .../tarzan/recommend/service/Recommend.java | 22 +++++- 6 files changed, 137 insertions(+), 52 deletions(-) create mode 100644 src/main/java/com/tarzan/recommend/core/ItemCF.java create mode 100644 src/main/java/com/tarzan/recommend/core/UserCF.java diff --git a/src/main/java/com/tarzan/recommend/RecommendSystemApplication.java b/src/main/java/com/tarzan/recommend/RecommendSystemApplication.java index ff5d5a9..1b8fa68 100644 --- a/src/main/java/com/tarzan/recommend/RecommendSystemApplication.java +++ b/src/main/java/com/tarzan/recommend/RecommendSystemApplication.java @@ -11,9 +11,12 @@ public class RecommendSystemApplication { public static void main(String[] args) { //SpringApplication.run(RecommendSystemApplication.class, args); - List itemList= Recommend.guessUserLike(2); - System.out.println("------猜你可能喜欢---------------下列电影"); + System.out.println("------基于用户协同过滤推荐---------------下列电影"); + List itemList= Recommend.userCfRecommend(2); itemList.forEach(e-> System.out.println(e.getName())); + System.out.println("------基于物品协同过滤推荐---------------下列电影"); + List itemList1= Recommend.itemCfRecommend(2); + itemList1.forEach(e-> System.out.println(e.getName())); } } diff --git a/src/main/java/com/tarzan/recommend/core/CoreMath.java b/src/main/java/com/tarzan/recommend/core/CoreMath.java index a63c42f..c64637b 100644 --- a/src/main/java/com/tarzan/recommend/core/CoreMath.java +++ b/src/main/java/com/tarzan/recommend/core/CoreMath.java @@ -4,7 +4,6 @@ import com.tarzan.recommend.dto.RelateDTO; import org.assertj.core.util.Lists; import java.util.*; -import java.util.stream.Collectors; import java.util.stream.IntStream; /** @@ -17,72 +16,54 @@ import java.util.stream.IntStream; */ public class CoreMath { - /** - * 方法描述: 推荐电影id列表 - * - * @param userId 当前用户 - * @param list 用户电影评分数据 - * @return {@link List} - * @date 2023年02月02日 14:51:42 - */ - public List recommend(Integer userId, List list) { - //按用户分组 - Map> userMap=list.stream().collect(Collectors.groupingBy(RelateDTO::getUseId)); - //获取其他用户与当前用户的关系值 - Map userDisMap = computeNeighbor(userId, userMap); - //获取关系最近的用户 - double maxvalue=Collections.max(userDisMap.values()); - Set userIds=userDisMap.entrySet().stream().filter(e->e.getValue()==maxvalue).map(Map.Entry::getKey).collect(Collectors.toSet()); - //取关系最近的用户 - Integer nearestUserId = userIds.stream().findAny().get(); - //最近邻用户看过电影列表 - List neighborItems = userMap.get(nearestUserId).stream().map(RelateDTO::getModuleId).collect(Collectors.toList()); - //指定用户看过电影列表 - List userItems = userMap.get(userId).stream().map(RelateDTO::getModuleId).collect(Collectors.toList()); - //找到最近邻看过,但是该用户没看过的电影 - neighborItems.removeAll(userItems); - return neighborItems; - } /** - * 在给定userId的情况下,计算其他用户和它的相关系数并排序 - * @param userId 用户id - * @param userMap 用户电影评分关系mqp + * 计算相关系数并排序 + * @param key + * @param map * @return Map */ - private Map computeNeighbor(Integer userId, Map> userMap) { - Map userDisMap = new TreeMap<>(); - List userItems=userMap.get(userId); - userMap.forEach((k,v)->{ + public static Map computeNeighbor(Integer key, Map> map,int type) { + Map distMap = new TreeMap<>(); + List userItems=map.get(key); + map.forEach((k,v)->{ //排除此用户 - if(!k.equals(userId)){ + if(!k.equals(key)){ //关系系数 - double coefficient = pearsonDis(v,userItems); + double coefficient = relateDist(v,userItems,type); //关系距离 double distance=Math.abs(coefficient); - userDisMap.put(k,distance); + distMap.put(k,distance); } }); - return userDisMap; + return distMap; } /** * 计算两个序列间的相关系数 * - * @param xList 用户1喜欢的电影 - * @param yList 用户2喜欢的电影 + * @param xList + * @param yList + * @param type 类型0基于用户推荐 1基于物品推荐 * @return double */ - private double pearsonDis(List xList, List yList) { + private static double relateDist(List xList, List yList,int type) { List xs= Lists.newArrayList(); List ys= Lists.newArrayList(); xList.forEach(x->{ yList.forEach(y->{ - if(x.getModuleId().equals(y.getModuleId())){ - xs.add(x.getIndex()); - ys.add(y.getIndex()); + if(type==0){ + if(x.getItemId().equals(y.getItemId())){ + xs.add(x.getIndex()); + ys.add(y.getIndex()); + } + }else{ + if(x.getUseId().equals(y.getUseId())){ + xs.add(x.getIndex()); + ys.add(y.getIndex()); + } } }); }); @@ -100,7 +81,8 @@ public class CoreMath { */ public static double getRelate(List xs, List ys){ int n=xs.size(); - if (n==0) { + //至少有两个元素 + if (n<2) { return 0D; } double Ex= xs.stream().mapToDouble(x->x).sum(); diff --git a/src/main/java/com/tarzan/recommend/core/ItemCF.java b/src/main/java/com/tarzan/recommend/core/ItemCF.java new file mode 100644 index 0000000..dfa5e97 --- /dev/null +++ b/src/main/java/com/tarzan/recommend/core/ItemCF.java @@ -0,0 +1,39 @@ +package com.tarzan.recommend.core; + +import com.tarzan.recommend.dto.RelateDTO; +import org.assertj.core.util.Lists; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * 核心算法 + * + * @author tarzan + * @version 1.0 + * @date 2020/7/31$ 15:21$ + * @since JDK1.8 + */ +public class ItemCF { + + /** + * 方法描述: 推荐电影id列表 + * + * @param itemId 当前电影id + * @param list 用户电影评分数据 + * @return {@link List} + * @date 2023年02月02日 14:51:42 + */ + public static List recommend(Integer itemId, List list) { + //按物品分组 + Map> itemMap=list.stream().collect(Collectors.groupingBy(RelateDTO::getItemId)); + //获取其他物品与当前物品的关系值 + Map itemDisMap = CoreMath.computeNeighbor(itemId, itemMap,1); + //获取关系最近物品 + double maxValue=Collections.max(itemDisMap.values()); + return itemDisMap.entrySet().stream().filter(e->e.getValue()==maxValue).map(Map.Entry::getKey).collect(Collectors.toList()); + } + + +} diff --git a/src/main/java/com/tarzan/recommend/core/UserCF.java b/src/main/java/com/tarzan/recommend/core/UserCF.java new file mode 100644 index 0000000..ce552e4 --- /dev/null +++ b/src/main/java/com/tarzan/recommend/core/UserCF.java @@ -0,0 +1,45 @@ +package com.tarzan.recommend.core; + +import com.tarzan.recommend.dto.RelateDTO; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 核心算法 + * + * @author tarzan + * @version 1.0 + * @date 2020/7/31$ 15:21$ + * @since JDK1.8 + */ +public class UserCF { + + /** + * 方法描述: 推荐电影id列表 + * + * @param userId 当前用户 + * @param list 用户电影评分数据 + * @return {@link List} + * @date 2023年02月02日 14:51:42 + */ + public static List recommend(Integer userId, List list) { + //按用户分组 + Map> userMap=list.stream().collect(Collectors.groupingBy(RelateDTO::getUseId)); + //获取其他用户与当前用户的关系值 + Map userDisMap = CoreMath.computeNeighbor(userId, userMap,0); + //获取关系最近的用户 + double maxValue=Collections.max(userDisMap.values()); + Set userIds=userDisMap.entrySet().stream().filter(e->e.getValue()==maxValue).map(Map.Entry::getKey).collect(Collectors.toSet()); + //取关系最近的用户 + Integer nearestUserId = userIds.stream().findAny().get(); + //最近邻用户看过电影列表 + List neighborItems = userMap.get(nearestUserId).stream().map(RelateDTO::getItemId).collect(Collectors.toList()); + //指定用户看过电影列表 + List userItems = userMap.get(userId).stream().map(RelateDTO::getItemId).collect(Collectors.toList()); + //找到最近邻看过,但是该用户没看过的电影 + neighborItems.removeAll(userItems); + return neighborItems; + } + +} diff --git a/src/main/java/com/tarzan/recommend/dto/RelateDTO.java b/src/main/java/com/tarzan/recommend/dto/RelateDTO.java index f2ff728..7caeb1d 100644 --- a/src/main/java/com/tarzan/recommend/dto/RelateDTO.java +++ b/src/main/java/com/tarzan/recommend/dto/RelateDTO.java @@ -18,8 +18,8 @@ import lombok.NoArgsConstructor; public class RelateDTO { /** 用户id */ private Integer useId; - /** 业务id */ - private Integer moduleId; + /** 物品id */ + private Integer itemId; /** 指数 */ private Integer index; diff --git a/src/main/java/com/tarzan/recommend/service/Recommend.java b/src/main/java/com/tarzan/recommend/service/Recommend.java index f5e4821..c33c723 100644 --- a/src/main/java/com/tarzan/recommend/service/Recommend.java +++ b/src/main/java/com/tarzan/recommend/service/Recommend.java @@ -1,6 +1,8 @@ package com.tarzan.recommend.service; import com.tarzan.recommend.core.CoreMath; +import com.tarzan.recommend.core.ItemCF; +import com.tarzan.recommend.core.UserCF; import com.tarzan.recommend.dto.ItemDTO; import com.tarzan.recommend.dto.RelateDTO; @@ -25,10 +27,24 @@ public class Recommend{ * @author tarzan * @date 2020年07月31日 17:28:06 */ - public static List guessUserLike(int userId){ - CoreMath coreMath = new CoreMath(); + public static List userCfRecommend(int userId){ List data= FileDataSource.getData(); - List recommendations = coreMath.recommend(userId, data); + List recommendations = UserCF.recommend(userId, data); + return FileDataSource.getItemData().stream().filter(e->recommendations.contains(e.getId())).collect(Collectors.toList()); + } + + + /** + * 方法描述: 猜你喜欢 + * + * @param itemId 物品id + * @Return {@link List} + * @author tarzan + * @date 2020年07月31日 17:28:06 + */ + public static List itemCfRecommend(int itemId){ + List data= FileDataSource.getData(); + List recommendations = ItemCF.recommend(itemId, data); return FileDataSource.getItemData().stream().filter(e->recommendations.contains(e.getId())).collect(Collectors.toList()); }