代码优化

This commit is contained in:
tarzan
2023-02-02 15:35:02 +08:00
parent 7b8fedacbd
commit 57e8f17e6e
8 changed files with 81 additions and 70 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -1,8 +1,7 @@
package com.tarzan.recommend; package com.tarzan.recommend;
import com.tarzan.recommend.Service.Recommend; import com.tarzan.recommend.service.Recommend;
import com.tarzan.recommend.dto.ItemDTO; import com.tarzan.recommend.dto.ItemDTO;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.List; import java.util.List;

View File

@@ -12,49 +12,57 @@ import java.util.stream.IntStream;
* *
* @author tarzan * @author tarzan
* @version 1.0 * @version 1.0
* @company 洛阳图联科技有限公司
* @copyright (c) 2019 LuoYang TuLian Co'Ltd Inc. All rights reserved.
* @date 2020/7/31$ 15:21$ * @date 2020/7/31$ 15:21$
* @since JDK1.8 * @since JDK1.8
*/ */
public class CoreMath { public class CoreMath {
/**
* 方法描述: 推荐电影id列表
*
* @param userId 当前用户
* @param list 用户电影评分数据
* @return {@link List<Integer>}
* @date 2023年02月02日 14:51:42
*/
public List<Integer> recommend(Integer userId, List<RelateDTO> list) { public List<Integer> recommend(Integer userId, List<RelateDTO> list) {
//找到最近邻用户id //按用户分组
Map<Double, Integer> distances = computeNearestNeighbor(userId, list);
Integer nearest = distances.values().iterator().next();
Map<Integer, List<RelateDTO>> userMap=list.stream().collect(Collectors.groupingBy(RelateDTO::getUseId)); Map<Integer, List<RelateDTO>> userMap=list.stream().collect(Collectors.groupingBy(RelateDTO::getUseId));
//获取其他用户与当前用户的关系值
Map<Double, Integer> distances = computeNeighbor(userId, userMap);
//取关系最近的用户
Integer nearestUserId = distances.values().iterator().next();
//最近邻用户看过电影列表 //最近邻用户看过电影列表
List<Integer> neighborItemList = userMap.get(nearest).stream().map(e->e.getModuleId()).collect(Collectors.toList()); List<Integer> neighborItems = userMap.get(nearestUserId).stream().map(RelateDTO::getModuleId).collect(Collectors.toList());
//指定用户看过电影列表 //指定用户看过电影列表
List<Integer> userItemList = userMap.get(userId).stream().map(e->e.getModuleId()).collect(Collectors.toList());; List<Integer> userItems = userMap.get(userId).stream().map(RelateDTO::getModuleId).collect(Collectors.toList());
//找到最近邻看过,但是该用户没看过的电影,计算推荐,放入推荐列表 //找到最近邻看过,但是该用户没看过的电影,计算推荐,放入推荐列表
List<Integer> recommendList = new ArrayList<>(); List<Integer> recommendList = new ArrayList<>();
for (Integer item : neighborItemList) { for (Integer item : neighborItems) {
if (!userItemList.contains(item)) { if (!userItems.contains(item)) {
recommendList.add(item); recommendList.add(item);
} }
} }
Collections.sort(recommendList); // Collections.sort(recommendList);
return recommendList; return recommendList;
} }
/** /**
* 在给定userId的情况下计算其他用户和它的相关系数并排序 * 在给定userId的情况下计算其他用户和它的相关系数并排序
* @param userId * @param userId 用户id
* @param list * @param userMap 用户电影评分关系mqp
* @return * @return Map<Double, Integer>
*/ */
private Map<Double, Integer> computeNearestNeighbor(Integer userId, List<RelateDTO> list) { private Map<Double, Integer> computeNeighbor(Integer userId, Map<Integer,List<RelateDTO>> userMap) {
Map<Integer, List<RelateDTO>> userMap=list.stream().collect(Collectors.groupingBy(RelateDTO::getUseId));
Map<Double, Integer> distances = new TreeMap<>(); Map<Double, Integer> distances = new TreeMap<>();
List<RelateDTO> userItems=userMap.get(userId);
userMap.forEach((k,v)->{ userMap.forEach((k,v)->{
if(k!=userId){ //排除此用户
double distance = pearson_dis(v,userMap.get(userId)); if(!k.equals(userId)){
//关系距离
double distance = pearsonDis(v,userItems);
distances.put(distance, k); distances.put(distance, k);
} }
}); });
@@ -65,16 +73,16 @@ public class CoreMath {
/** /**
* 计算两个序列间的相关系数 * 计算两个序列间的相关系数
* *
* @param xList * @param xList 用户1喜欢的电影
* @param yList * @param yList 用户2喜欢的电影
* @return * @return double
*/ */
private double pearson_dis(List<RelateDTO> xList, List<RelateDTO> yList) { private double pearsonDis(List<RelateDTO> xList, List<RelateDTO> yList) {
List<Integer> xs= Lists.newArrayList(); List<Integer> xs= Lists.newArrayList();
List<Integer> ys= Lists.newArrayList(); List<Integer> ys= Lists.newArrayList();
xList.forEach(x->{ xList.forEach(x->{
yList.forEach(y->{ yList.forEach(y->{
if(x.getModuleId()==y.getModuleId()){ if(x.getModuleId().equals(y.getModuleId())){
xs.add(x.getIndex()); xs.add(x.getIndex());
ys.add(y.getIndex()); ys.add(y.getIndex());
} }
@@ -86,14 +94,13 @@ public class CoreMath {
/** /**
* 方法描述: 皮尔森pearson相关系数计算 * 方法描述: 皮尔森pearson相关系数计算
* *
* @param xs * @param xs x集合
* @param ys * @param ys y集合
* @Return {@link Double} * @Return {@link double}
* @throws
* @author tarzan * @author tarzan
* @date 2020年07月31日 17:03:20 * @date 2020年07月31日 17:03:20
*/ */
public static Double getRelate(List<Integer> xs, List<Integer> ys){ public static double getRelate(List<Integer> xs, List<Integer> ys){
int n=xs.size(); int n=xs.size();
double Ex= xs.stream().mapToDouble(x->x).sum(); double Ex= xs.stream().mapToDouble(x->x).sum();
double Ey=ys.stream().mapToDouble(y->y).sum(); double Ey=ys.stream().mapToDouble(y->y).sum();
@@ -102,7 +109,9 @@ public class CoreMath {
double Exy= IntStream.range(0,n).mapToDouble(i->xs.get(i)*ys.get(i)).sum(); double Exy= IntStream.range(0,n).mapToDouble(i->xs.get(i)*ys.get(i)).sum();
double numerator=Exy-Ex*Ey/n; double numerator=Exy-Ex*Ey/n;
double denominator=Math.sqrt((Ex2-Math.pow(Ex,2)/n)*(Ey2-Math.pow(Ey,2)/n)); double denominator=Math.sqrt((Ex2-Math.pow(Ex,2)/n)*(Ey2-Math.pow(Ey,2)/n));
if (denominator==0) return 0.0; if (denominator==0) {
return 0D;
}
return numerator/denominator; return numerator/denominator;
} }

View File

@@ -7,10 +7,8 @@ import lombok.NoArgsConstructor;
/** /**
* 业务项 * 业务项
* *
* @author liu yapeng * @author TARZAN
* @version 1.0 * @version 1.0
* @company 洛阳图联科技有限公司
* @copyright (c) 2019 LuoYang TuLian Co'Ltd Inc. All rights reserved.
* @date 2020/7/31$ 15:02$ * @date 2020/7/31$ 15:02$
* @since JDK1.8 * @since JDK1.8
*/ */
@@ -18,9 +16,13 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ItemDTO { public class ItemDTO {
/** 主键 */
private Integer id; private Integer id;
/** 名称 */
private String name; private String name;
/** 日期 */
private String date; private String date;
/** 链接 */
private String link; private String link;
} }

View File

@@ -7,10 +7,8 @@ import lombok.NoArgsConstructor;
/** /**
* 关系数据 * 关系数据
* *
* @author liu yapeng * @author tarzan
* @version 1.0 * @version 1.0
* @company 洛阳图联科技有限公司
* @copyright (c) 2019 LuoYang TuLian Co'Ltd Inc. All rights reserved.
* @date 2020/7/31$ 14:51$ * @date 2020/7/31$ 14:51$
* @since JDK1.8 * @since JDK1.8
*/ */
@@ -18,11 +16,11 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class RelateDTO { public class RelateDTO {
//用户id /** 用户id */
private Integer useId; private Integer useId;
//业务id /** 业务id */
private Integer moduleId; private Integer moduleId;
//指数 /** 指数 */
private Integer index; private Integer index;

View File

@@ -7,7 +7,7 @@ import lombok.NoArgsConstructor;
/** /**
* 用户对象 * 用户对象
* *
* @author liu yapeng * @author TARZAN
* @version 1.0 * @version 1.0
* @company 洛阳图联科技有限公司 * @company 洛阳图联科技有限公司
* @copyright (c) 2019 LuoYang TuLian Co'Ltd Inc. All rights reserved. * @copyright (c) 2019 LuoYang TuLian Co'Ltd Inc. All rights reserved.
@@ -18,15 +18,15 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class UserDTO { public class UserDTO {
//主键 /** 主键 */
private Integer id; private Integer id;
//年纪 /** 年纪 */
private Integer age; private Integer age;
//性别 /** 性别 */
private String sex; private String sex;
//职业 /** 职业 */
private String profession; private String profession;
//邮编 /** 邮编 */
private String postcode; private String postcode;
} }

View File

@@ -1,4 +1,4 @@
package com.tarzan.recommend.Service; package com.tarzan.recommend.service;
import com.tarzan.recommend.dto.ItemDTO; import com.tarzan.recommend.dto.ItemDTO;
import com.tarzan.recommend.dto.RelateDTO; import com.tarzan.recommend.dto.RelateDTO;
@@ -10,7 +10,11 @@ import org.assertj.core.util.Lists;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Objects;
/**
* @author tarzan
*/
@Data @Data
@Slf4j @Slf4j
public class FileDataSource { public class FileDataSource {
@@ -22,20 +26,18 @@ public class FileDataSource {
/** /**
* 方法描述: 读取基础数据 * 方法描述: 读取基础数据
* *
* @param * @Return {@link List<RelateDTO>}
* @Return {@link List< RelateDTO>}
* @throws
* @author tarzan * @author tarzan
* @date 2020年07月31日 16:53:40 * @date 2020年07月31日 16:53:40
*/ */
public static List<RelateDTO> getData() { public static List<RelateDTO> getData() {
folderPath=new FileDataSource().getClass().getResource("/ml-100k").getPath(); folderPath= Objects.requireNonNull(FileDataSource.class.getResource("/ml-100k")).getPath();
List<RelateDTO> relateList = Lists.newArrayList(); List<RelateDTO> relateList = Lists.newArrayList();
try { try {
FileInputStream out = new FileInputStream(folderPath+"\\u.data"); FileInputStream out = new FileInputStream(folderPath+"\\u.data");
InputStreamReader reader = new InputStreamReader(out, StandardCharsets.UTF_8); InputStreamReader reader = new InputStreamReader(out, StandardCharsets.UTF_8);
BufferedReader in = new BufferedReader(reader); BufferedReader in = new BufferedReader(reader);
String line = null; String line;
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
String newline = line.replaceAll("[\t]", " "); String newline = line.replaceAll("[\t]", " ");
String[] ht = newline.split(" "); String[] ht = newline.split(" ");
@@ -54,20 +56,18 @@ public class FileDataSource {
/** /**
* 方法描述: 读取用户数据 * 方法描述: 读取用户数据
* *
* @param * @Return {@link List<UserDTO>}
* @Return {@link List< UserDTO>}
* @throws
* @author tarzan * @author tarzan
* @date 2020年07月31日 16:54:51 * @date 2020年07月31日 16:54:51
*/ */
public static List<UserDTO> getUserData() { public static List<UserDTO> getUserData() {
folderPath=new FileDataSource().getClass().getResource("/ml-100k").getPath(); folderPath= Objects.requireNonNull(FileDataSource.class.getResource("/ml-100k")).getPath();
List<UserDTO> userList = Lists.newArrayList(); List<UserDTO> userList = Lists.newArrayList();
try { try {
FileInputStream out = new FileInputStream(folderPath+"\\u.user"); FileInputStream out = new FileInputStream(folderPath+"\\u.user");
InputStreamReader reader = new InputStreamReader(out, StandardCharsets.UTF_8); InputStreamReader reader = new InputStreamReader(out, StandardCharsets.UTF_8);
BufferedReader in = new BufferedReader(reader); BufferedReader in = new BufferedReader(reader);
String line = null; String line;
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
String newline = line.replaceAll("[\t]", " "); String newline = line.replaceAll("[\t]", " ");
String[] ht = newline.split("\\|"); String[] ht = newline.split("\\|");
@@ -89,20 +89,18 @@ public class FileDataSource {
/** /**
* 方法描述: 读取电影数据 * 方法描述: 读取电影数据
* *
* @param * @Return {@link List<ItemDTO>}
* @Return {@link List< ItemDTO>}
* @throws
* @author tarzan * @author tarzan
* @date 2020年07月31日 16:54:22 * @date 2020年07月31日 16:54:22
*/ */
public static List<ItemDTO> getItemData() { public static List<ItemDTO> getItemData() {
folderPath=new FileDataSource().getClass().getResource("/ml-100k").getPath(); folderPath= Objects.requireNonNull(FileDataSource.class.getResource("/ml-100k")).getPath();
List<ItemDTO> itemList = Lists.newArrayList(); List<ItemDTO> itemList = Lists.newArrayList();
try { try {
FileInputStream out = new FileInputStream(folderPath+"\\u.item"); FileInputStream out = new FileInputStream(folderPath+"\\u.item");
InputStreamReader reader = new InputStreamReader(out, StandardCharsets.UTF_8); InputStreamReader reader = new InputStreamReader(out, StandardCharsets.UTF_8);
BufferedReader in = new BufferedReader(reader); BufferedReader in = new BufferedReader(reader);
String line = null; String line;
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
String newline = line.replaceAll("[\t]", " "); String newline = line.replaceAll("[\t]", " ");
String[] ht = newline.split("\\|"); String[] ht = newline.split("\\|");

View File

@@ -1,4 +1,4 @@
package com.tarzan.recommend.Service; package com.tarzan.recommend.service;
import com.tarzan.recommend.core.CoreMath; import com.tarzan.recommend.core.CoreMath;
import com.tarzan.recommend.dto.ItemDTO; import com.tarzan.recommend.dto.ItemDTO;
@@ -10,9 +10,8 @@ import java.util.stream.Collectors;
/** /**
* 推荐服务 * 推荐服务
* *
* @author liu yapeng * @author TARZAN
* @version 1.0 * @version 1.0
* @copyright (c) 2019 LuoYang TuLian Co'Ltd Inc. All rights reserved.
* @date 2020/7/31$ 16:18$ * @date 2020/7/31$ 16:18$
* @since JDK1.8 * @since JDK1.8
*/ */
@@ -21,9 +20,8 @@ public class Recommend{
/** /**
* 方法描述: 猜你喜欢 * 方法描述: 猜你喜欢
* *
* @param * @param userId 用户id
* @Return {@link List<ItemDTO>} * @Return {@link List<ItemDTO>}
* @throws
* @author tarzan * @author tarzan
* @date 2020年07月31日 17:28:06 * @date 2020年07月31日 17:28:06
*/ */
@@ -31,8 +29,7 @@ public class Recommend{
CoreMath coreMath = new CoreMath(); CoreMath coreMath = new CoreMath();
List<RelateDTO> data= FileDataSource.getData(); List<RelateDTO> data= FileDataSource.getData();
List<Integer> recommendations = coreMath.recommend(userId, data); List<Integer> recommendations = coreMath.recommend(userId, data);
List<ItemDTO> itemList= FileDataSource.getItemData().stream().filter(e->recommendations.contains(e.getId())).collect(Collectors.toList()); return FileDataSource.getItemData().stream().filter(e->recommendations.contains(e.getId())).collect(Collectors.toList());
return itemList;
} }