代码优化
This commit is contained in:
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal 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
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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("\\|");
|
||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user