1.RelateDTO
package com.example.entity;
import java.io.Serializable;
/**
* 协同过滤算法
*/
public class RelateDTO implements Serializable {
private static final long serialVersionUID = 1L; // 序列化
/** 用户id */
private Integer useId;
/** 商品id */
private Integer goodsId;
/** 指数 */
private Double index;
public RelateDTO(Integer useId, Integer goodsId, Double index) {
this.useId = useId;
this.goodsId = goodsId;
this.index = index;
}
public Integer getUseId() {
return useId;
}
public void setUseId(Integer useId) {
this.useId = useId;
}
public Integer getGoodsId() {
return goodsId;
}
public void setGoodsId(Integer goodsId) {
this.goodsId = goodsId;
}
public Double getIndex() {
return index;
}
public void setIndex(Double index) {
this.index = index;
}
}
2.CoreMath
package com.example.utils.recommend;
import cn.hutool.core.collection.CollectionUtil;
import com.example.entity.RelateDTO;
import org.assertj.core.util.Lists;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.IntStream;
/**
* 协同过滤核心算法
*/
public class CoreMath {
/**
* 计算相关系数并排序
* @param key
* @param map
* @return Map<Integer,Double>
*/
public static Map<Integer, Double> computeNeighbor(Integer key, Map<Integer, List<RelateDTO>> map, int type) {
Map<Integer, Double> distMap = new TreeMap<>();
List<RelateDTO> userItems = map.get(key);
if (CollectionUtil.isNotEmpty(userItems)) {
map.forEach((k, v) -> {
//排除此用户
if (!k.equals(key)) {
//关系系数
double coefficient = relateDist(v, userItems, type);
//关系距离
/*
注:过滤算法中应该对仅有两个元素的情况做特殊处理,因为两点必成一条直线,
那么相关性计算出来的值永远都是1,但这并不代表这两个物品具有很强的相似
把计算相关性系数的结果的求绝对值的方法去掉就行
double distance = Math.abs(coefficient);
distMap.put(k, distance);
*/
distMap.put(k,coefficient);
}
});
}
return distMap;
}
/**
* 计算两个序列间的相关系数
*
* @param xList
* @param yList
* @param type 类型0基于用户推荐 1基于物品推荐
* @return double
*/
private static double relateDist(List<RelateDTO> xList, List<RelateDTO> yList, int type) {
List<Double> xs= Lists.newArrayList();
List<Double> ys= Lists.newArrayList();
xList.forEach(x -> yList.forEach(y -> {
if (type == 0) {
if (x.getGoodsId().equals(y.getGoodsId())) {
xs.add(x.getIndex());
ys.add(y.getIndex());
}
} else {
if (x.getUseId().equals(y.getUseId())) {
xs.add(x.getIndex());
ys.add(y.getIndex());
}
}
}));
return getRelate(xs, ys);
}
/**
* 方法描述: 皮尔森(pearson)相关系数计算
* @param xs x集合
* @param ys y集合
* @Return {@link double}
*/
public static double getRelate(List<Double> xs, List<Double> ys) {
int n = xs.size();
//至少有两个元素
if (n < 2) {
return 0D;
}
double Ex = xs.stream().mapToDouble(x -> x).sum();
double Ey = ys.stream().mapToDouble(y -> y).sum();
double Ex2 = xs.stream().mapToDouble(x -> Math.pow(x, 2)).sum();
double Ey2 = ys.stream().mapToDouble(y -> Math.pow(y, 2)).sum();
double Exy = IntStream.range(0, n).mapToDouble(i -> xs.get(i) * ys.get(i)).sum();
double numerator = Exy - Ex * Ey / n;
double denominator = Math.sqrt((Ex2 - Math.pow(Ex, 2) / n) * (Ey2 - Math.pow(Ey, 2) / n));
if (denominator == 0) {
return 0D;
}
return numerator / denominator;
}
}
3.UserCF
package com.example.utils.recommend;
import cn.hutool.core.collection.CollectionUtil;
import com.example.entity.RelateDTO;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class UserCF {
/**
* 方法描述: 推荐商品id列表
*
* @param userId 当前用户
* @param list 用户商品评分数据
* @return {@link List<Integer>}
*/
public static List<Integer> recommend(Integer userId, List<RelateDTO> list) {
// 按用户分组
Map<Integer, List<RelateDTO>> userMap = list.stream().collect(Collectors.groupingBy(RelateDTO::getUseId));
// 获取其他用户与当前用户的关系值
Map<Integer, Double> userDisMap = CoreMath.computeNeighbor(userId, userMap, 0);
if (CollectionUtil.isEmpty(userDisMap)) {
return Collections.emptyList();
}
// 获取关系最近的用户
double maxValue = Collections.max(userDisMap.values());
Set<Integer> userIds = userDisMap.entrySet().stream().filter(e -> e.getValue() == maxValue).map(Map.Entry::getKey).collect(Collectors.toSet());
// 取关系最近的用户
Integer nearestUserId = userIds.stream().findAny().orElse(null);
if (nearestUserId == null) {
return Collections.emptyList();
}
// 最近邻用户看过商品列表
List<Integer> neighborItems = userMap.get(nearestUserId).stream().map(RelateDTO::getGoodsId).collect(Collectors.toList());
// 指定用户看过商品列表
List<Integer> userItems = userMap.get(userId).stream().map(RelateDTO::getGoodsId).collect(Collectors.toList());
// 找到最近邻看过,但是该用户没看过的商品
neighborItems.removeAll(userItems);
return neighborItems;
}
}
4.ItemCF
package com.example.utils.recommend;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.example.entity.RelateDTO;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ItemCF {
/**
* 方法描述: 推荐商品id列表
*
* @param goodsId 当前商品id
* @param list 用户商品评分数据
* @return {@link List<Integer>}
*/
public static List<Integer> recommend(Integer goodsId, List<RelateDTO> list) {
List<Integer> result = new ArrayList<>(); // 返回的数组
//按物品分组
Map<Integer, List<RelateDTO>> itemMap = list.stream().collect(Collectors.groupingBy(RelateDTO::getGoodsId));
//获取其他物品与当前物品的关系值
Map<Integer,Double> itemDisMap = CoreMath.computeNeighbor(goodsId, itemMap,1);
//获取关系最近物品
if (CollectionUtil.isEmpty(itemDisMap)) {
return Collections.emptyList();
}
double maxValue = Collections.max(itemDisMap.values());
return itemDisMap.entrySet().stream().filter(e->e.getValue() == maxValue).map(Map.Entry::getKey).collect(Collectors.toList());
}
}
5.LFM
package com.example.utils.recommend.LFM;
import java.util.*;
import java.util.Map.Entry;
import com.example.entity.RelateDTO;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class LFM {
private double alpha = 0.02;// 梯度下降学习速率,步长
private static final int F = 100;// 隐因子数
private static final double lambda = 0.01;// 正则化参数,防止过拟合
private static final int iterations = 100;// 迭代求解次数
private Map<Integer, Double[]> userF = Maps.newHashMap();// 用户-隐因子矩阵
private Map<Integer, Double[]> itemF = Maps.newHashMap();// 物品-隐因子矩阵
public List<Integer> LFMRecommend(Integer userId,Integer count,List<RelateDTO> data){
LFM lfm = new LFM();
Map<Integer, Map<Integer, Double>> userItemRealData = lfm.buildInitData(data);
lfm.initUserItemFactorMatrix(userItemRealData);
lfm.train(userItemRealData);
return lfm.getRecommondItemByUserId(userId, count, userItemRealData);
}
/**
* 构建初始数据<br/>
* 实际开发中,应该读取日志,格式类似:userId,itemId,评分<br/>
* 本例采用随机数
*
* @return
*/
/*
public Map<Integer, Map<Integer, Double>> buildInitData() {
Map<Integer, Map<Integer, Double>> userItemRealData = Maps.newHashMap();
System.out.println("读取数据......");
String dataPath = "数据文件/ratings.csv";
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream(dataPath), "utf-8"));
String line = null;
Map<Integer, Double> itemRating = null;
while ((line = br.readLine()) != null) {
String[] dataRating = line.split(",");
Integer userID = Integer.valueOf(dataRating[0].trim());
Integer itemID = Integer.valueOf(dataRating[1].trim());
double rating = Double.parseDouble(dataRating[2]);
if (userItemRealData.containsKey(userID)) {
itemRating = userItemRealData.get(userID);
itemRating.put(itemID, rating);
} else {
itemRating = Maps.newHashMap();
itemRating.put(itemID, rating);
userItemRealData.put(userID, itemRating);
}
}
br.close();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
return userItemRealData;
}
*/
public Map<Integer, Map<Integer, Double>> buildInitData(List<RelateDTO> data) {
Map<Integer, Map<Integer, Double>> userItemRealData = Maps.newHashMap();
Map<Integer, Double> itemRating = null;
for (RelateDTO relateDTO: data) {
Integer userID = relateDTO.getUseId();
Integer itemID = relateDTO.getGoodsId();
double rating = relateDTO.getIndex();
if (userItemRealData.containsKey(userID)) {
itemRating = userItemRealData.get(userID);
itemRating.put(itemID, rating);
} else {
itemRating = Maps.newHashMap();
itemRating.put(itemID, rating);
userItemRealData.put(userID, itemRating);
}
}
return userItemRealData;
}
/**
* 初始化用户-隐因子、物品-隐因子矩阵<br/>
* 使用随机数进行填充,随机数应与1/sqrt(隐因子数量)成正比
*
* @param userItemRealData
*/
public void initUserItemFactorMatrix(Map<Integer, Map<Integer, Double>> userItemRealData) {
for (Entry<Integer, Map<Integer, Double>> userEntry : userItemRealData.entrySet()) {
// 随机填充用户-隐因子矩阵
int userId = userEntry.getKey();
Double[] randomUserValue = new Double[LFM.F];
for (int j = 0; j < LFM.F; j++) {
randomUserValue[j] = Math.random() / Math.sqrt(LFM.F);
}
this.getUserF().put(userId, randomUserValue);
// 随机填充物品-隐因子矩阵
Map<Integer, Double> itemMap = userItemRealData.get(userId);
for (Entry<Integer, Double> entry : itemMap.entrySet()) {
int itemId = entry.getKey();
if (this.getItemF().containsKey(itemId)) {
continue;// 物品-隐因子矩阵已存在,不再做处理
}
Double[] randomItemValue = new Double[LFM.F];
for (int j = 0; j < LFM.F; j++) {
randomItemValue[j] = Math.random() / Math.sqrt(LFM.F);
}
this.getItemF().put(itemId, randomItemValue);
}
}
}
/**
* 训练
* @param userItemRealData
*/
public void train(Map<Integer, Map<Integer, Double>> userItemRealData) {
for (int step = 0; step < LFM.iterations; step++) {
System.out.println("LFM第" + (step + 1) + "次迭代");
for (Entry<Integer, Map<Integer, Double>> userEntry : userItemRealData.entrySet()) {
int userId = userEntry.getKey();
Map<Integer, Double> itemMap = userItemRealData.get(userId);
for (Entry<Integer, Double> entry : itemMap.entrySet()) {
int itemId = entry.getKey();// 物品ID
double realRating = entry.getValue();// 真实偏好度
double predictRating = this.predict(userId, itemId);// 预测偏好度
double error = realRating - predictRating;// 偏好度误差
Double[] userVal = this.getUserF().get(userId);
Double[] itemVal = this.getItemF().get(itemId);
for (int j = 0; j < LFM.F; j++) {
double uv = userVal[j];
double iv = itemVal[j];
uv += this.alpha * (error * iv - LFM.lambda * uv);
iv += this.alpha * (error * uv - LFM.lambda * iv);
userVal[j] = uv;
itemVal[j] = iv;
}
}
}
this.alpha *= 0.9;// 按照随机梯度下降算法的要求,学习速率每个步骤都要进行衰减,目的是使算法尽快收敛
}
}
/**
* 获取用户对物品的预测评分
*
* @param userId
* @param itemId
* @return
*/
public double predict(Integer userId, Integer itemId) {
double predictRating = 0.0;// 预测评分
Double[] userValue = this.getUserF().get(userId);
Double[] itemValue = this.getItemF().get(itemId);
for (int i = 0; i < LFM.F; i++) {
predictRating += userValue[i] * itemValue[i];
}
return predictRating;
}
/**
* 获取用户TopN推荐
* @param userId
* @param count
* @param userItemRealData
* @return
*/
public List<Integer> getRecommondItemByUserId(int userId, int count, Map<Integer, Map<Integer, Double>> userItemRealData) {
List<Integer> itemIds = new ArrayList<>(); // 返回的推荐商品ID
Map<Integer, Double> result = Maps.newHashMap();
Map<Integer, Double> realItemVal = userItemRealData.get(userId);
Map<Integer, Double[]> predictItemVal = this.getItemF();
// double lowestVal = Double.NEGATIVE_INFINITY;// 最小偏好度,初始为负无穷大
for (Integer itemId : predictItemVal.keySet()) {
if (realItemVal.containsKey(itemId)) {
continue;// 预测偏好度的物品在真实偏好度物品中,不处理
}
double predictRating = this.predict(userId, itemId);// 预测值
if (predictRating < 0) {
continue;
}
result.put(itemId, predictRating);
}
List<Map.Entry<Integer, Double>> list = Lists.newArrayList(result.entrySet());
Collections.sort(list, new Comparator<Map.Entry<Integer, Double>>() {
// 降序
public int compare(Map.Entry<Integer, Double> o1, Map.Entry<Integer, Double> o2) {
if (o2.getValue() > o1.getValue()) {
return 1;
} else if (o2.getValue() < o1.getValue()) {
return -1;
} else {
return 0;
}
}
});
List<Map.Entry<Integer, Double>> topN = new ArrayList<>();
if(count <= list.size()){
topN = list.subList(0, count); // 若推荐结果大于count,按序选取count个推荐
}else {
topN = list; // 若推荐结果小于count,则topN全部推荐
}
System.out.println("------------LFM开始推荐------------");
System.out.println("用户ID:" + userId + " 前" + topN.size() + "个推荐结果:");
for (Entry<Integer, Double> entry : topN) {
itemIds.add(entry.getKey()); // 添加筛选推荐的商品ID
System.out.println("商品ID:"+entry.getKey() + " " + "商品指数:"+entry.getValue());
}
return itemIds;
}
public Map<Integer, Double[]> getUserF() {
return userF;
}
public void setUserF(Map<Integer, Double[]> userF) {
this.userF = userF;
}
public Map<Integer, Double[]> getItemF() {
return itemF;
}
public void setItemF(Map<Integer, Double[]> itemF) {
this.itemF = itemF;
}
}
6.其他
6.1Rec
package com.example.utils.recommend.LFM;
import java.util.Map;
public interface Rec {
public void buildRecommender(Map<String,Map<String,Double>> r);
public Double r_hat(String u, String i);
}
6.2Util
package com.example.utils.recommend.LFM;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Util {
public static void PrintVector(Double[] v) {
for(int j=0; j<v.length; j++)
System.out.print(v[j] + " ");
System.out.print("\n");
}
public static Double vecvecprod(Double[] p, Double[] q) {
Double pq = 0.0;
for(int f=0; f<p.length; f++)
pq += p[f]*q[f];
return pq;
}
public static Double[] scalarvecprod(Double a, Double[] p) {
Double[] q = new Double[p.length];
for(int f=0; f<p.length; f++)
q[f] = a*p[f];
return q;
}
public static Double[] vecvecsum(Double[] p, Double[] q) {
Double[] p_plus_q = new Double[p.length];
for(int f=0; f<p.length; f++)
p_plus_q[f] = q[f] + p[f];
return p_plus_q;
}
public static Double[] vecvecminus(Double[] p, Double[] q) {
Double[] p_plus_q = new Double[p.length];
for(int f=0; f<p.length; f++)
p_plus_q[f] = q[f] - p[f];
return p_plus_q;
}
public static Double computeMu(Map<String,Map<String,Double>> r) {
Double sum = 0.0;
int count = 0;
for(String u : r.keySet()) {
for(String i : r.get(u).keySet()) {
sum += r.get(u).get(i);
count++;
}
}
return sum/count;
}
public static Map<String,Map<String,Double>> r_u_i_TO_r_i_u(Map<String,Map<String,Double>> r) {
Map<String,Map<String,Double>> r_i_u = new HashMap<String,Map<String,Double>>();
for(String u : r.keySet()) {
for(String i : r.get(u).keySet()) {
Double rating = r.get(u).get(i);
if( !r_i_u.containsKey(i) ) {
Map<String, Double> map = new HashMap<String, Double>();
r_i_u.put(i, map);
}
r_i_u.get(i).put(u, rating);
}
}
return r_i_u;
}
public static Set<String> r_u_i_TO_i(Map<String,Map<String,Double>> r) {
Set<String> items = new HashSet<String>();
for(String u : r.keySet())
for(String i : r.get(u).keySet())
items.add(i);
return items;
}
public static Set<String> r_u_i_TO_u(Map<String,Map<String,Double>> r) {
return r.keySet();
}
public static Map<String,Map<String,Double>> copyR(Map<String,Map<String,Double>> r) {
Map<String,Map<String,Double>> res = new HashMap<String,Map<String,Double>>();
for(String u : r.keySet()) {
res.put(u, new HashMap<String,Double>());
for(String i : r.get(u).keySet()) {
res.get(u).put(i, r.get(u).get(i));
}
}
return res;
}
public static Double oneHideoutRMSE(Rec rec, Map<String,Map<String,Double>> r, boolean remove) {
Map<String,Map<String,Double>> test = extractTest(r, 100.0);
Double RMSEsum = 0.0;
int count = 0;
for(String u : test.keySet()) {
for(String i : test.get(u).keySet()) {
System.out.println("count="+count);
if( !r.get(u).containsKey(i) ) {
System.out.println("Hello, your test set contains ratings not in r!!!!");
continue;
}
double r_ui = r.get(u).get(i);
if(remove) r.get(u).remove(i);
rec.buildRecommender(r);
RMSEsum += Math.pow( r_ui - rec.r_hat(u, i), 2.0 );
count++;
//put it back
if(remove) r.get(u).put(i, r_ui);
}
}
return Math.sqrt(RMSEsum/count);
}
public static Double RMSE(Rec rec, Map<String,Map<String,Double>> r, Map<String,Map<String,Double>> test) {
System.out.println("Building the recommender...");
rec.buildRecommender(r);
System.out.println("Finished building the recommender...");
System.out.println("Computing RMSE and MAE...");
Double RMSEsum = 0.0;
int count = 0;
for(String u : test.keySet()) {
for(String i : test.get(u).keySet()) {
double r_ui = test.get(u).get(i);
double r_pred = rec.r_hat(u, i);
RMSEsum += Math.pow( r_ui - r_pred, 2.0 );
count++;
}
}
System.out.println("Done with this test set");
return Math.sqrt(RMSEsum/count);
}
public static String RMSEplus(Rec rec, Map<String,Map<String,Double>> r, Map<String,Map<String,Double>> test) {
System.out.println("Building the recommender...");
rec.buildRecommender(r);
System.out.println("Finished building the recommender...");
System.out.println("Computing RMSE and MAE...");
Double RMSEsum = 0.0;
Double MAEsum = 0.0;
Double TP = 0.0, FP = 0.0, TN = 0.0, FN = 0.0;
Double threshold = 3.5;
int count = 0;
System.out.println("=====u\ti\tr\tr_hat");
for(String u : test.keySet()) {
for(String i : test.get(u).keySet()) {
double r_ui = test.get(u).get(i);
double r_pred = rec.r_hat(u, i);
System.out.println("====="+u+'\t'+i+'\t'+r_ui+'\t'+r_pred);
RMSEsum += Math.pow( r_ui - r_pred, 2.0 );
MAEsum += Math.abs(r_ui - r_pred);
if (r_ui >= 4.0)
if (r_pred >= threshold)
TP += 1;
else
FN += 1;
else
if (r_pred >= threshold)
FP += 1;
else
TN += 1;
count++;
}
}
System.out.println("Done with this test set");
Double precision = 1.0 * TP / (TP + FP);
Double recall = 1.0 * TP / (TP + FN);
Double fmeasure = 1.0 * 2 * (precision * recall) / (precision + recall);
Double accuracy = 1.0 * (TP+TN)/(TP+TN+FP+FN);
String results = "RMSE="+Math.sqrt(RMSEsum/count)+";"
+"MAE="+MAEsum/count+";"
+"precision="+precision+";"
+"recall="+recall+";"
+"fmeasure="+fmeasure+";"
+"accuracy="+accuracy+";";
return results;
}
public static Map<String,Map<String,Double>> extractTest(Map<String,Map<String,Double>> r, Double pct) {
Map<String,Map<String,Double>> test = new HashMap<String,Map<String,Double>>();
int count=0;
for(String u : r.keySet()) {
for(String i : r.get(u).keySet()) {
if( Math.random() <= pct/100.0 ) {
if( !test.containsKey(u) )
test.put(u, new HashMap<String,Double>());
test.get(u).put(i, r.get(u).get(i));
count++;
}
}
}
System.out.println("Removing test ratings from r...");
for(String u : test.keySet()) {
for(String i : test.get(u).keySet()) {
if( !r.get(u).containsKey(i) )
continue;
r.get(u).remove(i);
}
}
System.out.println(count + " ratings were extracted for test");
return test;
}
public static Map<String,Map<String,Double>> readData(String filename) throws Exception {
Map<String,Map<String,Double>> r = new HashMap<String,Map<String,Double>>();
System.out.println("Reading file " + filename + " ...");
BufferedReader br = new BufferedReader( new FileReader(filename) );
String line;
while ( (line = br.readLine()) != null ) {
//System.out.println("Reading line: " + line);
String[] array = line.split("\t");
if (array.length == 1){
array = line.split(",");
if (array.length == 1)
continue;
}
String user = array[0];
String item = array[1];
Double rating = Double.parseDouble(array[2]);
if( !r.containsKey(user) ) r.put(user, new HashMap<String,Double>());
r.get(user).put(item,rating);
}
System.out.println("End of reading file " + filename);
return r;
}
//Checkpoint: Add a function to readData by Item
public static Map<String,Map<String,Double>> readDataItem(String filename) throws Exception {
Map<String,Map<String,Double>> r = new HashMap<String,Map<String,Double>>();
System.out.println("Reading file " + filename + " ...");
BufferedReader br = new BufferedReader( new FileReader(filename) );
String line;
while ( (line = br.readLine()) != null ) {
//System.out.println("Reading line: " + line);
String[] array = line.split("\t");
if (array.length == 1) continue;
String user = array[0];
String item = array[1];
Double rating = Double.parseDouble(array[2]);
if( !r.containsKey(item) ) r.put(item, new HashMap<String,Double>());
r.get(item).put(user,rating);
}
System.out.println("End of reading file " + filename);
return r;
}
}
6.4RecBaseline
package com.example.utils.recommend.LFM;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class RecBaseline implements Rec {
//user_item_rating_map
Map<String,Map<String,Double>> r;
Set<String> users, items;
Double mu; //mean
Map<String,Double> bu = new HashMap<String,Double>();
Map<String,Double> bi = new HashMap<String,Double>();
Double gamma = 0.005;
Double lambda_bu = 0.02;
Double lambda_bi = 0.02;
int iternum = 30;
public void buildRecommender(Map<String,Map<String,Double>> r) {
this.r = r;
initialize();
this.mu = Util.computeMu(r);
computeBuBiPuQi();
}
//predict rating
public Double r_hat(String u, String i) {
Double bu_u = 0.0, bi_i = 0.0;
if ( bu.containsKey(u) ) bu_u = bu.get(u);
if ( bi.containsKey(i) ) bi_i = bi.get(i);
return mu+bu_u+bi_i;
}
void initialize() {
this.items = Util.r_u_i_TO_i(r);
this.users = Util.r_u_i_TO_u(r);
for(String u : users) bu.put(u, 0.0);
for(String i : items) bi.put(i, 0.0);
}
//error
Double e(String u, String i) {
return r.get(u).get(i) - r_hat(u,i);
}
void computeBuBiPuQi() {
for(int iter=0; iter<this.iternum; iter++) {
for(String u : r.keySet()) {
for(String i : r.get(u).keySet()) {
Double e_ui = e(u,i);
Double b_u = bu.get(u);
Double b_i = bi.get(i);
bu.put(u, b_u + gamma*(e_ui-lambda_bu*b_u));
bi.put(i, b_i + gamma*(e_ui-lambda_bi*b_i));
}
}
}
}
Double getLambda_bu() {return lambda_bu;}
void setLambda_bu(Double lambda_bu) {this.lambda_bu = lambda_bu;}
Double getLambda_bi() {return lambda_bi;}
void setLambda_bi(Double lambda_bi) {this.lambda_bi = lambda_bi;}
int getIternum() {return iternum;}
void setIternum(int iternum) {this.iternum = iternum;}
public void outputBaselineParameters(Map<String,Map<String,Double>> test){
Double bu_u = 0.0, bi_i = 0.0;
System.out.println("=====r\tmu\tbu\tbi");
for(String u : test.keySet()) {
for(String i : test.get(u).keySet()) {
bu_u = 0.0; bi_i = 0.0;
if ( bu.containsKey(u) ) bu_u = bu.get(u);
if ( bi.containsKey(i) ) bi_i = bi.get(i);
double r_ui = test.get(u).get(i);
System.out.println("====="+r_ui+'\t'+mu+'\t'+
bu_u+'\t'+bi_i);
}
}
}
public static void main(String[] args) throws Exception {
Map<String,Map<String,Double>> r = Util.readData("D://ratings.csv");
Map<String,Map<String,Double>> test = Util.extractTest(r, 1.0); //1%
RecBaseline rec = new RecBaseline();
System.out.println("RMSE="+Util.RMSE(rec,r,test));
rec.outputBaselineParameters(test);
}
}
6.5RecMF
package com.example.utils.recommend.LFM;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class RecMF implements Rec {
//user_item_rating_map
Map<String,Map<String,Double>> r;
Set<String> users, items;
Double mu; //mean
Map<String,Double> bu = new HashMap<String,Double>();
Map<String,Double> bi = new HashMap<String,Double>();
/* original setting
int F = 20; //number of factors
*/
int F = 20; //number of factors
Map<String,Double[]> pu = new HashMap<String,Double[]>();
Map<String,Double[]> qi = new HashMap<String,Double[]>();
Double gamma = 0.005;
/* original settings
Double lambda_bu = 0.02;
Double lambda_bi = 0.02;
Double lambda_pu = 0.05;
Double lambda_qi = 0.05;
*/
Double lambda_bu = 0.02;
Double lambda_bi = 0.02;
Double lambda_pu = 0.2;
Double lambda_qi = 0.2;
int iternum = 45; //70
public void buildRecommender(Map<String,Map<String,Double>> r) {
this.r = r;
initialize();
this.mu = Util.computeMu(r); // Checkpoint: mu - 0.5
computeBuBiPuQi();
}
//predict rating
public Double r_hat(String u, String i) {
Double bu_u = 0.0, bi_i = 0.0, pu_qi=0.0;
if ( bu.containsKey(u) ) bu_u = bu.get(u);
if ( bi.containsKey(i) ) bi_i = bi.get(i);
if ( pu.containsKey(u) && qi.containsKey(i) )
pu_qi = Util.vecvecprod(pu.get(u), qi.get(i));
else
pu_qi = mu;
return pu_qi;
//return mu;
}
void initialize() {
this.items = Util.r_u_i_TO_i(r);
this.users = Util.r_u_i_TO_u(r);
for(String u : users) bu.put(u, 0.0);
for(String i : items) bi.put(i, 0.0);
for(String u : users) {
Double[] vec = new Double[F];
for(int f=0; f<getF(); f++)
vec[f] = Math.random()/1.0;
pu.put(u, vec);
}
System.out.println("Length of pu: "+pu.size());
for(String i : items) {
Double[] vec = new Double[F];
for(int f=0; f<getF(); f++)
vec[f] = Math.random()/1.0;
qi.put(i, vec);
}
System.out.println("Length of qi: "+qi.size());
}
//error
Double e(String u, String i) {
return r.get(u).get(i) - r_hat(u,i);
}
void computeBuBiPuQi() {
for(int iter=0; iter<this.iternum; iter++) {
for(String u : r.keySet()) {
for(String i : r.get(u).keySet()) {
Double e_ui = e(u,i);
Double[] p_u = pu.get(u);
Double[] q_i = qi.get(i);
pu.put(u, Util.vecvecsum(p_u, Util.scalarvecprod(gamma, Util.vecvecsum(
Util.scalarvecprod(e_ui, q_i),
Util.scalarvecprod(-lambda_pu, p_u)))));
qi.put(i, Util.vecvecsum(q_i, Util.scalarvecprod(gamma, Util.vecvecsum(
Util.scalarvecprod(e_ui, p_u),
Util.scalarvecprod(-lambda_qi, q_i)))));
}
}
}
}
int getF() {return F;}
void setF(int f) {F = f;}
Double getLambda_bu() {return lambda_bu;}
void setLambda_bu(Double lambda_bu) {this.lambda_bu = lambda_bu;}
Double getLambda_bi() {return lambda_bi;}
void setLambda_bi(Double lambda_bi) {this.lambda_bi = lambda_bi;}
Double getLambda_pu() {return lambda_pu;}
void setLambda_pu(Double lambda_pu) {this.lambda_pu = lambda_pu;}
Double getLambda_qi() {return lambda_qi;}
void setLambda_qi(Double lambda_qi) {this.lambda_qi = lambda_qi;}
int getIternum() {return iternum;}
void setIternum(int iternum) {this.iternum = iternum;}
public void outputLatentFactors(){
System.out.println("User Latent Factors");
for(String u : pu.keySet()) {
System.out.print(u);
for(int f=0; f<F; f++)
System.out.print("\t"+pu.get(u)[f]); // '\t' won't work
System.out.println();
}
System.out.println("Item Latent Factors");
for(String i : qi.keySet()) {
System.out.print(i);
for(int f=0; f<F; f++)
System.out.print("\t"+qi.get(i)[f]);
System.out.println();
}
}
public static void main(String[] args) throws Exception {
Map<String,Map<String,Double>> r = Util.readData("D://ratings.csv");
Map<String,Map<String,Double>> test = Util.extractTest(r, 1.0); //1%
RecMF rec = new RecMF();
System.out.println("RMSE="+Util.RMSE(rec,r,test));
//rec.outputLatentFactors();
}
}
6.6RecMuMF
package com.example.utils.recommend.LFM;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class RecMuMF implements Rec {
//user_item_rating_map
Map<String,Map<String,Double>> r;
Set<String> users, items;
Double mu; //mean
Map<String,Double> bu = new HashMap<String,Double>();
Map<String,Double> bi = new HashMap<String,Double>();
/* original setting
int F = 20; //number of factors
*/
int F = 20; //number of factors
Map<String,Double[]> pu = new HashMap<String,Double[]>();
Map<String,Double[]> qi = new HashMap<String,Double[]>();
Double gamma = 0.005;
/* original settings
Double lambda_bu = 0.02;
Double lambda_bi = 0.02;
Double lambda_pu = 0.05;
Double lambda_qi = 0.05;
*/
Double lambda_bu = 0.02;
Double lambda_bi = 0.02;
Double lambda_pu = 0.03;
Double lambda_qi = 0.03;
int iternum = 70;
public void buildRecommender(Map<String,Map<String,Double>> r) {
this.r = r;
initialize();
this.mu = Util.computeMu(r); // Checkpoint: mu - 0.5
computeBuBiPuQi();
}
//predict rating
public Double r_hat(String u, String i) {
Double bu_u = 0.0, bi_i = 0.0, pu_qi=0.0;
if ( bu.containsKey(u) ) bu_u = bu.get(u);
if ( bi.containsKey(i) ) bi_i = bi.get(i);
if ( pu.containsKey(u) && qi.containsKey(i) )
pu_qi = Util.vecvecprod(pu.get(u), qi.get(i));
return mu+pu_qi;
}
void initialize() {
this.items = Util.r_u_i_TO_i(r);
this.users = Util.r_u_i_TO_u(r);
for(String u : users) bu.put(u, 0.0);
for(String i : items) bi.put(i, 0.0);
for(String u : users) {
Double[] vec = new Double[F];
for(int f=0; f<getF(); f++)
vec[f] = Math.random()/100.0;
pu.put(u, vec);
}
for(String i : items) {
Double[] vec = new Double[F];
for(int f=0; f<getF(); f++)
vec[f] = Math.random()/100.0;
qi.put(i, vec);
}
}
//error
Double e(String u, String i) {
return r.get(u).get(i) - r_hat(u,i);
}
void computeBuBiPuQi() {
for(int iter=0; iter<this.iternum; iter++) {
for(String u : r.keySet()) {
for(String i : r.get(u).keySet()) {
Double e_ui = e(u,i);
Double b_u = bu.get(u);
Double b_i = bi.get(i);
Double[] p_u = pu.get(u);
Double[] q_i = qi.get(i);
bu.put(u, b_u + gamma*(e_ui-lambda_bu*b_u));
bi.put(i, b_i + gamma*(e_ui-lambda_bi*b_i));
pu.put(u, Util.vecvecsum(p_u, Util.scalarvecprod(gamma, Util.vecvecsum(
Util.scalarvecprod(e_ui, q_i),
Util.scalarvecprod(-lambda_pu, p_u)))));
qi.put(i, Util.vecvecsum(q_i, Util.scalarvecprod(gamma, Util.vecvecsum(
Util.scalarvecprod(e_ui, p_u),
Util.scalarvecprod(-lambda_qi, q_i)))));
}
}
}
}
int getF() {return F;}
void setF(int f) {F = f;}
Double getLambda_bu() {return lambda_bu;}
void setLambda_bu(Double lambda_bu) {this.lambda_bu = lambda_bu;}
Double getLambda_bi() {return lambda_bi;}
void setLambda_bi(Double lambda_bi) {this.lambda_bi = lambda_bi;}
Double getLambda_pu() {return lambda_pu;}
void setLambda_pu(Double lambda_pu) {this.lambda_pu = lambda_pu;}
Double getLambda_qi() {return lambda_qi;}
void setLambda_qi(Double lambda_qi) {this.lambda_qi = lambda_qi;}
int getIternum() {return iternum;}
void setIternum(int iternum) {this.iternum = iternum;}
public static void main(String[] args) throws Exception {
Map<String,Map<String,Double>> r = Util.readData("D://ratings.csv");
Map<String,Map<String,Double>> test = Util.extractTest(r, 1.0); //1%
RecMuMF rec = new RecMuMF();
System.out.println("RMSE="+Util.RMSE(rec,r,test));
}
}
6.7RecSVD
package com.example.utils.recommend.LFM;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class RecSVD implements Rec {
//user_item_rating_map
Map<String,Map<String,Double>> r;
Set<String> users, items;
Double mu; //mean
Map<String,Double> bu = new HashMap<String,Double>();
Map<String,Double> bi = new HashMap<String,Double>();
/* original setting
int F = 20; //number of factors
*/
int F = 20; //number of factors
Map<String,Double[]> pu = new HashMap<String,Double[]>();
Map<String,Double[]> qi = new HashMap<String,Double[]>();
Double gamma = 0.005;
/* original settings
Double lambda_bu = 0.02;
Double lambda_bi = 0.02;
Double lambda_pu = 0.05;
Double lambda_qi = 0.05;
*/
Double lambda_bu = 0.02;
Double lambda_bi = 0.02;
Double lambda_pu = 0.05;
Double lambda_qi = 0.05;
int iternum = 35;
public void buildRecommender(Map<String,Map<String,Double>> r) {
this.r = r;
initialize();
this.mu = Util.computeMu(r); // Checkpoint: mu - 0.5
computeBuBiPuQi();
}
//predict rating
public Double r_hat(String u, String i) {
Double bu_u = 0.0, bi_i = 0.0, pu_qi=0.0;
if ( bu.containsKey(u) ) bu_u = bu.get(u);
if ( bi.containsKey(i) ) bi_i = bi.get(i);
if ( pu.containsKey(u) && qi.containsKey(i) )
pu_qi = Util.vecvecprod(pu.get(u), qi.get(i));
return mu+bu_u+bi_i+pu_qi;
}
void initialize() {
this.items = Util.r_u_i_TO_i(r);
this.users = Util.r_u_i_TO_u(r);
for(String u : users) bu.put(u, 0.0);
for(String i : items) bi.put(i, 0.0);
for(String u : users) {
Double[] vec = new Double[F];
for(int f=0; f<getF(); f++)
vec[f] = Math.random()/100.0;
pu.put(u, vec);
}
for(String i : items) {
Double[] vec = new Double[F];
for(int f=0; f<getF(); f++)
vec[f] = Math.random()/100.0;
qi.put(i, vec);
}
}
//error
Double e(String u, String i) {
return r.get(u).get(i) - r_hat(u,i);
}
void computeBuBiPuQi() {
for(int iter=0; iter<this.iternum; iter++) {
for(String u : r.keySet()) {
for(String i : r.get(u).keySet()) {
Double e_ui = e(u,i);
Double b_u = bu.get(u);
Double b_i = bi.get(i);
Double[] p_u = pu.get(u);
Double[] q_i = qi.get(i);
bu.put(u, b_u + gamma*(e_ui-lambda_bu*b_u));
bi.put(i, b_i + gamma*(e_ui-lambda_bi*b_i));
pu.put(u, Util.vecvecsum(p_u, Util.scalarvecprod(gamma, Util.vecvecsum(
Util.scalarvecprod(e_ui, q_i),
Util.scalarvecprod(-lambda_pu, p_u)))));
qi.put(i, Util.vecvecsum(q_i, Util.scalarvecprod(gamma, Util.vecvecsum(
Util.scalarvecprod(e_ui, p_u),
Util.scalarvecprod(-lambda_qi, q_i)))));
}
}
}
}
int getF() {return F;}
void setF(int f) {F = f;}
Double getLambda_bu() {return lambda_bu;}
void setLambda_bu(Double lambda_bu) {this.lambda_bu = lambda_bu;}
Double getLambda_bi() {return lambda_bi;}
void setLambda_bi(Double lambda_bi) {this.lambda_bi = lambda_bi;}
Double getLambda_pu() {return lambda_pu;}
void setLambda_pu(Double lambda_pu) {this.lambda_pu = lambda_pu;}
Double getLambda_qi() {return lambda_qi;}
void setLambda_qi(Double lambda_qi) {this.lambda_qi = lambda_qi;}
int getIternum() {return iternum;}
void setIternum(int iternum) {this.iternum = iternum;}
public static void main(String[] args) throws Exception {
Map<String,Map<String,Double>> r = Util.readData("D://ratings.csv");
Map<String,Map<String,Double>> test = Util.extractTest(r, 1.0); //1%
RecSVD rec = new RecSVD();
System.out.println("RMSE="+Util.RMSE(rec,r,test));
}
}
7.service实现类实现该操作
private List<RelateDTO> GetData(){
// 定义一个存储每个商品和每个用户关系的List
List<RelateDTO> data = new ArrayList<>(); // 保存Redis缓存的数据
List<RelateDTO> data2 = new ArrayList<>(); // 保存数据库查询的数据(二选一)
// 1.构造redis的key
String key="GoodsData";
data = (List<RelateDTO>) redisTemplate.opsForValue().get(key);
if(data != null && data.size() > 0){
// 2.如果存在,直接返回,无需查询数据库
return data;
}
// 用户的哪些行为可以认为他跟商品产生了关系:收藏、加入购物车、下单、评论、评分等
// 1. 获取所有的收藏信息
List<Collect> allCollects = collectMapper.selectAll(null);
// 2. 获取所有的购物车信息
List<Cart> allCarts = cartMapper.selectAll(null);
// 3. 获取所有的已完成或者已评价的订单信息
List<Orders> allOrders = ordersMapper.selectAllOKOrders();
// 4. 获取所有的评分信息:5分制,只查询评分大于等于3的评论
List<Comment> allComments = commentMapper.selectAllOKComments();
// 5. 获取所有的用户信息
List<User> allUsers = userMapper.selectAll(null);
// 6. 获取所有的商品信息
List<Goods> allGoods = GetAllGoods();
// 开始计算每个商品和每个用户之间的关系数据
for (Goods goods : allGoods) {
Integer goodsId = goods.getId();
for (User user : allUsers) {
Integer userId = user.getId();
double index = 1.0;
// 1. 判断该用户有没有收藏该商品,收藏的权重我们给:1
Optional<Collect> collectOptional = allCollects.stream().filter(x -> x.getGoodsId().equals(goodsId) && x.getUserId().equals(userId)).findFirst();
if (collectOptional.isPresent()) {
index += 1.0;
}
// 2. 判断该用户有没有给该商品加入购物车,加入购物车的权重我们给:2
Optional<Cart> cartOptional = allCarts.stream().filter(x -> x.getGoodsId().equals(goodsId) && x.getUserId().equals(userId)).findFirst();
if (cartOptional.isPresent()) {
index += 2.0;
}
// 3. 判断该用户有没有对该商品下过单(已完成的订单),订单的权重我们给:3
Optional<Orders> ordersOptional = allOrders.stream().filter(x -> x.getGoodsId().equals(goodsId) && x.getUserId().equals(userId)).findFirst();
if (ordersOptional.isPresent()) {
index += 3.0;
}
// 4. 判断该用户有没有对该商品评分过 注:商品的评分与订单绑定删除,因此不会index减为负数
// 1分:-3;2分;-2;三分:0;4分:2;5分:3
Optional<Comment> commentOptional = allComments.stream().filter(x -> x.getGoodsId().equals(goodsId) && x.getUserId().equals(userId)).findFirst();
if (commentOptional.isPresent()) {
if (commentOptional.get().getScore() == 1){
index -= 3.0;
} else if(commentOptional.get().getScore() == 2){
index -= 2.0;
} else if(commentOptional.get().getScore() == 4){
index += 2.0;
} else if(commentOptional.get().getScore() == 5){
index += 3.0;
}
}
if (index > 1.0) {
RelateDTO relateDTO = new RelateDTO(userId, goodsId, index);
data2.add(relateDTO); // 将遍历获得的数据添加到data里面
}
}
}
// 3.如果不存在,查询数据库,将查询到的数据放到redis中
// 设置Redis缓存两个小时过期时间
redisTemplate.opsForValue().set(key,data2,2, TimeUnit.HOURS);
return data2;
}
private List<Goods> GetAllGoods(){
// 1.构造redis的key
String key="AllGoods";
List<Goods> goodsList = (List<Goods>) redisTemplate.opsForValue().get(key);
if(goodsList != null && goodsList.size() > 0){
// 2.如果存在,直接返回,无需查询数据库
return goodsList;
}
// 3.如果不存在,查询数据库,将查询到的数据放到redis中
goodsList=goodsMapper.selectAll(null);
// 设置Redis缓存两个小时过期时间
redisTemplate.opsForValue().set(key,goodsList,2, TimeUnit.HOURS);
return goodsList;
}
/**
* 基于用户的协同过滤算法实现
* num:返回的数据的个数
*/
public List<Goods> recommend(Integer num) {
System.out.println("------------(用户)基于用户的协同过滤算法------------");
// 定义一个存储每个商品和每个用户关系的List
List<RelateDTO> data = new ArrayList<>();
// 定义一个存储最后返回给前端的商品List
List<Goods> result = new ArrayList<>();
// 判断用户登录情况
Account currentUser = TokenUtils.getCurrentUser();
if (ObjectUtil.isEmpty(currentUser)) {
// 若用户未登录,随机推荐10个商品
return getRandomGoods(result, num);
}
data = GetData();
List<Goods> allGoods = GetAllGoods();
// 数据准备结束后,就把这些数据一起喂给这个推荐算法
List<Integer> goodsIds = UserCF.recommend(currentUser.getId(), data);
System.out.println("------------(用户)向用户:"+currentUser.getName()+" 推荐的全部物品:"+ Arrays.toString(goodsIds.stream().toArray())+"------------");
// 把商品id转换成商品:推荐算法获取的商品
result = goodsIds.stream().map(goodsId -> allGoods.stream()
.filter(x -> x.getId().equals(goodsId)).findFirst().orElse(null))
.limit(num).collect(Collectors.toList());
// 若推荐结果为空,随机给它推荐10个
if (CollectionUtil.isEmpty(result)) {
return getRandomGoods(result,num);
}
// 若推荐结果小于10个,随机增加几个,使个数为10个
if (result.size() < num) {
int count = num - result.size();
// 随机获取几个商品
result = getRandomGoods(result,count);
}
return result;
}
/**
* 基于物品的协同过滤算法实现
* num:返回的数据的个数
*/
public List<Goods> recommend2(Integer num) {
System.out.println("------------(物品)基于物品的协同过滤算法------------");
// 定义一个存储每个商品和每个用户关系的List
List<RelateDTO> data = new ArrayList<>();
// 定义一个存储最后返回给前端的商品List
List<Goods> result = new ArrayList<>();
// 判断用户登录情况
Account currentUser = TokenUtils.getCurrentUser();
if (ObjectUtil.isEmpty(currentUser)) {
// 若用户未登录,随机推荐10个商品
return getRandomGoods(result,num);
}
data = GetData();
List<Goods> allGoods = GetAllGoods(); // 所有的商品数据
List<Integer> goodsIds = new ArrayList<>(); // 推荐的商品id数据
// 0.查找出该用户的所有的近期的5个不重复的商品订单
List<Orders> ordersList = ordersMapper.selectByNum(currentUser.getId(),5);
if(ordersList.size() != 0){
List<Integer> goodsIdsTemp = new ArrayList<>(); // 临时存储商品id
System.out.println("------------(物品)近期5个购买的商品------------");
for (int i = ordersList.size()-1; i >= 0; i--) {
System.out.println("第"+(5-i)+"个商品:"+ordersList.get(i).getGoodsId());
// 数据准备结束后,就把这些数据一起喂给这个推荐算法
goodsIdsTemp = ItemCF.recommend(ordersList.get(i).getGoodsId(), data);
if(goodsIdsTemp.size() !=0 && ObjectUtil.isNotEmpty(goodsIdsTemp)){
goodsIds.addAll(goodsIdsTemp);
}
}
System.out.println("------------(物品)向用户:"+currentUser.getName()+" 推荐的全部物品:"+ Arrays.toString(goodsIdsTemp.toArray())+"------------");
// 1.推荐算法给出的商品id去重
goodsIds = goodsIds.stream().distinct().collect(Collectors.toList());
// 2.去重已经购买过(下单)的商品
// 获取当前登录用户所有购买过订单的商品ID,推荐算法排除这些买过的
List<Integer> allOrderGoodsIds = new ArrayList<>();
for (Orders orders : ordersList) {
if (orders.getUserId().equals(currentUser.getId())) {
allOrderGoodsIds.add(orders.getGoodsId());
}
}
System.out.println("------------(物品)已买过商品:"+Arrays.toString(allOrderGoodsIds.toArray())+"------------");
goodsIds.removeAll(allOrderGoodsIds);
System.out.println("------------(物品)移除已买过商品,进行推荐商品:"+Arrays.toString(goodsIds.toArray())+"------------");
// 3.把商品id转换成商品:推荐算法获取的商品
result = goodsIds.stream().map(goodsId -> allGoods.stream()
.filter(x -> x.getId().equals(goodsId)).findFirst().orElse(null))
.limit(num).collect(Collectors.toList());
// 4.若推荐结果小于10个,随机增加几个,使个数为10个
if (result.size() < num) {
int count = num - result.size();
// 随机获取几个商品
result = getRandomGoods(result,count);
}
}else{
result = getRandomGoods(result,num); // 没有订单,不能基于物品推荐,则随机推荐
}
return result;
}
/**
* 基于物品的协同过滤算法实现
* num:返回的数据的个数
*/
public List<Goods> recommend3(Integer num) {
// 定义一个存储每个商品和每个用户关系的List
List<RelateDTO> data = new ArrayList<>();
// 定义一个存储最后返回给前端的商品List
List<Goods> result = new ArrayList<>();
// 判断用户登录情况
Account currentUser = TokenUtils.getCurrentUser();
if (ObjectUtil.isEmpty(currentUser)) {
// 若用户未登录,随机推荐10个商品
return getRandomGoods(result,num);
}
// 1.构造redis的key
String key="LFM_"+currentUser.getId();
List<Goods> goodsList = (List<Goods>) redisTemplate.opsForValue().get(key);
if(goodsList != null && goodsList.size() > 0){
// 2.如果存在,直接返回,无需查询数据库
return goodsList;
}
data = GetData();
List<Goods> allGoods = GetAllGoods(); // 所有的商品数据
LFM lfm = new LFM();
List<Integer> recommendItemIds = lfm.LFMRecommend(currentUser.getId(), num, data);
result = recommendItemIds.stream().map(goodsId -> allGoods.stream()
.filter(x -> x.getId().equals(goodsId)).findFirst().orElse(null))
.limit(num).collect(Collectors.toList());
// 若推荐结果小于10个,随机增加几个,使个数为10个
if (result.size() < num) {
int count = num - result.size();
// 随机获取几个商品
result = getRandomGoods(result,count);
}
// 3.如果不存在,查询数据库,将查询到的数据放到redis中
// 设置Redis缓存两个小时过期时间
redisTemplate.opsForValue().set(key,result,2, TimeUnit.HOURS);
return result;
}
/**
* 若推荐结果不够,随机添加几个数据中没有的商品
*/
private List<Goods> getRandomGoods(List<Goods> result,int num) {
System.out.println("------------商品数据不足,进入随机推荐环节------------");
int length=result.size();
List<Goods> goods = goodsMapper.selectAll(null);
for (int i = 0; result.size()<length+num; i++) {
boolean flag=false;
int index = new Random().nextInt(goods.size());
for (Goods value : result) { // 排除重复
if (goods.get(index).getId().equals(value.getId())) {
flag = true; // 若随机产生的商品已存在数组中,则排除
break;
}
}
if(!flag){
result.add(goods.get(index));
}
}
return result;
}
/**
* 封装的清理Redis缓存的方法,避免代码重复
*/
private void cleanCache(String pattern){
Set keys = redisTemplate.keys(pattern);
redisTemplate.delete(keys);
}
可以点个免费的赞吗!!!