柚见第十二期(随机匹配)

随机匹配

目的
为了帮大家更快地发现和自己兴趣相同的朋友
问题

匹配 1 个还是匹配多个?

答:匹配多个,并且按照匹配的相似度从高到低排序

怎么匹配?(根据什么匹配)

答:标签 tags
还可以根据 user_team 匹配加入相同队伍的用户
本质:找到有相似标签的用户
举例:
用户 A:[Java, 大一, 男]
用户 B:[Java, 大二, 男]
用户 C:[Python, 大二, 女]
用户 D:[Java, 大一, 女]

1. 怎么匹配
  1. 找到有共同标签最多的用户(TopN)

  2. 共同标签越多,分数越高,越排在前面

  3. 如果没有匹配的用户,随机推荐几个(降级方案)

2. 怎么对所有用户匹配,取 TOP

直接取出所有用户,依次和当前用户计算分数,取 TOP N

优化方法:

  1. 切忌不要在数据量大的时候循环输出日志(取消掉日志后 20 秒)
  2. Map 存了所有的分数信息,占用内存解决:
    维护一个固定长度的有序集合(sortedSet),只保留分数最高的几个用户(时间换空间)
    e.g : 【3, 4, 5, 6, 7】取 TOP 5,id 为 1 的用户就不用放进去了
  3. 细节:剔除自己 √
  4. 尽量只查需要的数据:
    a. 过滤掉标签为空的用户 √
    b. 根据部分标签取用户(前提是能区分出来哪个标签比较重要)
    c. 只查需要的数据(比如 id 和 tags) √(7.0s)
  5. 提前查?(定时任务)
  6. 提前把所有用户给缓存(不适用于经常更新的数据)
  7. 提前运算出来结果,缓存(针对一些重点用户,提前缓存)

大数据推荐

比如说有几亿个商品,难道要查出来所有的商品?
难道要对所有的数据计算一遍相似度?

检索 => 召回 => 粗排 => 精排 => 重排序等等

检索:尽可能多地查符合要求的数据(比如按记录查)
召回:查询可能要用到的数据(不做运算)
粗排:粗略排序,简单地运算(运算相对轻量)
精排:精细排序,确定固定排位

分表学习建议

  1. mycat框架
  2. sharding sphere 框架
  3. 一致性hash

随机匹配实现

编辑距离算法:

https://blog.csdn.net/DBC_121/article/details/104198838
最小编辑距离:字符串 str1 通过最少多少次增删改字符的操作可以变成字符串str2

! 没学过,不要紧,直接当成黑盒导入使用即可

余弦相似度算法:

https://blog.csdn.net/m0_55613022/article/details/125683937(如果需要带权重计算,比如学什么方向最重要,性别相对次要)

后端

引入工具类

新建工具类
cv 代码过来

在这里插入图片描述

简单测试一下

在这里插入图片描述

之前都是传入字符串,而实际需要比较的是两组字符数组

在这里插入图片描述

测试一下,是可以的

在这里插入图片描述

取出所有用户,依次和当前用户计算分数

bug : 笑死,为什么这里打印出来和数据库不一样

在这里插入图片描述

解决

使用这段代码成功

for (int i = 0; i <userList.size(); i++) {  
User user = userList.get(i);  
String userTags = user.getTags();  
//无标签的  
if (StringUtils.isBlank(userTags)){  
continue;  
}  
List<String> userTagList = gson.fromJson(userTags, new TypeToken<List<String>>() {  
}.getType());  
//计算分数  
int distance = AlgorithmUtils.minDistance(tagList, userTagList);  
indexDistanceMap.put(i,distance);  
}

使用这段代码失败
明明一样

for (int i = 0; i < userList.size(); i++) {  
User user=userList.get(i);  
String userTags=user.getTags();  
//判断用户是否有标签列表  
if(StringUtils.isBlank(userTags))  
{  
continue;  
}  
//将tags字符串转换为List  
List<String> userTagList=gson.fromJson(tags,new TypeToken<List<String>>(){  
}.getType());  
//计算分数(分数越低,相似度越高)  
int distance=AlgorithmUtils.minDistance(tagList,userTagList);  
indexDistanceMap.put(i,distance);  
}

在这里插入图片描述

完整代码

public List<User> matchUsers(int num, User loginUser) {  
  
QueryWrapper<User> queryWrapper = new QueryWrapper<>();  
queryWrapper.isNotNull("tags");  
queryWrapper.select("id","tags");  
//1.查询所有用户  
List<User> userList=this.list(queryWrapper);  
//2.获取当前登录用户的标签  
//todo 前端传来可能是多个标签,这里是以字符串形式传递的吗?????  
String tags=loginUser.getTags();  
Gson gson=new Gson();  
//将字符串转换为List  
List<String> tagList=gson.fromJson(tags,new TypeToken<List<String>>(){  
  
}.getType());  
System.out.println("当前登录用户的tagList : "+tagList);  
//用户列表的下标:相似度  
List<Pair<User,Integer>> list=new ArrayList<>();  
//3.遍历用户列表,获得相似度分数  
for (int i = 0; i < userList.size(); i++) {  
User user=userList.get(i);  
String userTags=user.getTags();  
//判断无标签或者当前user是登录用户  
if(StringUtils.isBlank(userTags) || loginUser.getId().equals(user.getId()))  
{  
continue;  
}  
//将tags字符串转换为List  
List<String> userTagList=gson.fromJson(userTags,new TypeToken<List<String>>(){  
}.getType());  
//计算分数(分数越低,相似度越高)  
int distance=AlgorithmUtils.minDistance(tagList,userTagList);  
list.add(new Pair<>(user,distance));  
}  
//4.按照编辑距离由小到大排序  
List<Pair<User, Integer>> topUserPairList = list.stream()  
.sorted((a, b) ->(int) (a.getValue() - b.getValue()))  
.limit(num)  
.collect(Collectors.toList());  
//有顺序的userID列表  
List<Integer> userListVo = topUserPairList.stream().map(pari -> pari.getKey().getId()).collect(Collectors.toList());  
  
//根据id查询user完整信息  
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();  
userQueryWrapper.in("id",userListVo);  
  
Map<Integer, List<User>> userIdUserListMap = this.list(userQueryWrapper)  
.stream()  
.map(user -> getSafetyUser(user))  
.collect(Collectors.groupingBy(User::getId));  
  
// 因为上面查询打乱了顺序,这里根据上面有序的userID列表赋值  
List<User> finalUserList = new ArrayList<>();  
for (Integer userId : userListVo){  
finalUserList.add(userIdUserListMap.get(userId).get(0));  
}  
return finalUserList;  
}

在这里插入图片描述

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值