基于用户和物品的协同过滤算法及隐语义模型LFM

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

[机器学习]LFM梯度下降算法-CSDN博客

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);
}

可以点个免费的赞吗!!!   

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值