qidao123.com技术社区-IT企服评测·应用市场
标题:
基于Jaccard算法的用户欣赏历史保举商品体系实战+springboot+vue源码实现
[打印本页]
作者:
瑞星
时间:
2025-5-7 23:49
标题:
基于Jaccard算法的用户欣赏历史保举商品体系实战+springboot+vue源码实现
大家好,这里是小罗毕设工作室。今天给大家带来了一套完整的保举体系: “
基于Jaccard算法的用户欣赏历史保举商品体系
”。 体系源码后端实现是springboot,前端是vue3。
视频演示
基于Jaccard算法的用户欣赏历史保举商品体系实战
图片截图
算法原理先容
假设我们有两个商品:商品A和商品B。商品A被用户{小明,小红,小张}欣赏过,商品B被用户{小红,小张,小李}欣赏过。
得到两个用户群体集合 : {小明,小红,小张} {小红,小张,小李} 。
我们通过
Jaccard算法,
将两个集合先取交集得到 {小红,小张} ,大小是2 , 在取并集得到 {小明,小红,小张,小李},大小是4。
因此Jaccard相似度=2/4=0.5,这个值表现两个商品的相似程度,值越靠近1表现两个商品越相似(被雷同用户群体欣赏),体系会优先保举相似度高的商品。
实现流程
根本数据预备
起首我们需要预备用户的行为数据表:
CREATE TABLE `user_behaviors` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '行为ID,自增主键',
`user_id` bigint NOT NULL COMMENT '用户ID',
`item_id` bigint NOT NULL COMMENT '物品ID',
`rating` decimal(3,1) DEFAULT NULL COMMENT '评分(1-5分)',
`behavior_type` varchar(20) NOT NULL COMMENT '行为类型(VIEW-浏览,LIKE-喜欢,PURCHASE-购买)',
`timestamp` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '行为发生时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='用户行为记录表';
复制代码
之以是这里有评分字段,是因为后续要扩展
基于协同过滤算法的用户评分保举体系
。
固然这里还要有用户表和商品表:
CREATE TABLE `items` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '物品ID,自增主键',
`name` varchar(200) NOT NULL COMMENT '物品名称',
`description` text COMMENT '物品描述',
`category` varchar(50) DEFAULT NULL COMMENT '物品类别',
`price` decimal(10,2) DEFAULT NULL COMMENT '物品价格',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='商品表';
CREATE TABLE `users` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID,自增主键',
`username` varchar(50) NOT NULL COMMENT '用户名',
`email` varchar(100) NOT NULL COMMENT '用户邮箱',
`password` varchar(100) NOT NULL COMMENT '用户密码',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='用户表';
复制代码
表创建好之后,我们需要写好基本的用户登录,商品列表,商品详情等根本接口,然后保证有丰富的行为数据落库,这样保举体系才能发挥作用。
这些基本的接口我就不讲了,主要讲解一下用户保举接口的实现。
完整体系源码我已经整理清除:
gitcode巅抗目/hadluo2/springboot_vue.git
复制代码
用户保举接口的实现
我将代码的流程整理成了一个流程图,起首我们需要构造一个Map结构数据:
这些数据就是查询用户行为表,将全部的行为数据,按照商品分类,每一个商品都对应了一个用户群体集合。
然后还是查询用户行为表 , 将当前用户的行为数据查询出来,进行下面流程:
关键的核心点就是:将当前保举用户买过的商品id对应其他也买过的这个商品的用户作为一个用户集合,然后取全部商品对应的用户群体集合(许多个),然后一个一个经过Jaccard算法盘算相似度,把这个相似度最为评分,最后取出评分高的商品最为保举商品。
讲到这里,你应该能懂了,下面看下关键代码实现:
/**
完整代码实现:gitcode巅抗目/hadluo2/springboot_vue.git
* 基于用户浏览历史推荐物品
* 主要步骤如下:
* 1. 获取用户最近的浏览记录,按时间倒序排列
* 2. 获取所有用户的浏览记录,构建物品-用户倒排索引
* 3. 计算用户最近浏览物品与其他物品的相似度
* 4. 考虑时间衰减因素,为每个候选物品计算最终得分
* 5. 返回得分最高的N个物品作为推荐结果
* @param userId 用户ID
* @param numRecommendations 推荐数量
* @return 推荐的物品列表
*/
public List<Item> recommendBasedOnBrowsingHistory(Long userId, int numRecommendations) {
// 1. 获取用户最近的浏览记录
// 创建查询条件:匹配用户ID和浏览行为,按时间戳降序排序
LambdaQueryWrapper<UserBehavior> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserBehavior::getUserId, userId)
.eq(UserBehavior::getBehaviorType, "VIEW")
.orderByDesc(UserBehavior::getTimestamp);
List<UserBehavior> userViews = userBehaviorRepository.selectList(wrapper);
// 如果用户没有浏览记录,返回空列表
if (CollectionUtils.isEmpty(userViews)) {
return Collections.emptyList();
}
// 2. 获取所有用户的浏览记录
// 查询所有用户的浏览行为,用于后续计算物品相似度
List<UserBehavior> allViews = userBehaviorRepository.selectList(
new LambdaQueryWrapper<UserBehavior>()
.eq(UserBehavior::getBehaviorType, "VIEW")
);
// 3. 构建物品-用户的倒排索引
// key: 物品ID, value: 浏览过该物品的用户ID集合
Map<Long, Set<Long>> itemUserMap = new HashMap<>();
for (UserBehavior view : allViews) {
// computeIfAbsent: 如果key不存在,则创建一个新的HashSet
itemUserMap.computeIfAbsent(view.getItemId(), k -> new HashSet<>())
.add(view.getUserId());
}
// 4. 计算物品相似度和推荐得分
// 存储每个候选物品的最终得分
Map<Long, Double> itemScores = new HashMap<>();
// 获取用户已浏览过的物品ID集合,用于排除推荐
Set<Long> userViewedItemIds = userViews.stream()
.map(UserBehavior::getItemId)
.collect(Collectors.toSet());
// 遍历用户的每个浏览记录
for (UserBehavior userView : userViews) {
// 获取 浏览该物品的 所有用户
Set<Long> itemUsers = itemUserMap.get(userView.getItemId());
if (itemUsers == null) continue;
// 遍历所有物品,计算与当前浏览物品的相似度
for (Map.Entry<Long, Set<Long>> entry : itemUserMap.entrySet()) {
Long otherItemId = entry.getKey();
// 该用户浏览过了这个商品id, 直接排除
if (userViewedItemIds.contains(otherItemId)) continue;
// 浏览过这个商品的 其他用户
Set<Long> otherItemUsers = entry.getValue();
// 使用Jaccard相似度计算物品相似度
// Jaccard相似度 = 两个集合的交集大小 / 并集大小
//完整代码请见:gitcode巅抗目/hadluo2/springboot_vue.git
double similarity = calculateJaccardSimilarity(itemUsers, otherItemUsers);
// 考虑时间衰减因素 相当于一个优化因子
// 浏览时间越近,权重越大(1/1, 1/2, 1/3, ...)
//完整代码请见:gitcode巅抗目/hadluo2/springboot_vue.git
// 累加相似度得分(考虑时间权重)
itemScores.merge(otherItemId, similarity * timeWeight, Double::sum);
}
}
// 5. 生成推荐结果
// 对候选物品按得分降序排序,选择前N个
return itemScores.entrySet().stream()
.sorted(Map.Entry.<Long, Double>comparingByValue().reversed())
.limit(numRecommendations)
.map(e -> itemRepository.selectById(e.getKey()))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
复制代码
代码只贴了部分,原理已经教给大家。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/)
Powered by Discuz! X3.4