根据余弦公式计算url相似度并转换为正则表达式

举个例子:/aaa/bbb/ccc/12345/kkk/45678和/aaa/bbb/ccc/512515/kkk/32143
这样的例子url中变化的参数路径参数用正则表示就是 /aaa/bbb/ccc/[^/]+/kkk/[^/]+
这边记录一下我的思路
我的一个思路是:
1、将两个url根据/拆分成两个有序字符串数组
2、统计两个数组中字符串出现的次数
3、相似度计算:
以字符串出现的次数的两个集合为两个坐标得到两个向量a和b,
根据余弦相似度计算出相似度,大于0.5则表示这两个url相似可用正则表示
4、通过比较两个url的不同位置替换为路径参数

相似度计算公式
在这里插入图片描述

相似度计算

/**
     * 计算相似度
     * @param domain1 域名1
     * @param path1 路径1
     * @param domain2 域名2
     * @param path2 路径2
     * @return
     */
    public static BigDecimal findLike(String domain1, String path1, String domain2, String path2){
        if(StringUtils.isAnyBlank(domain1, domain2, path1, path2)){
            return new BigDecimal(0);
        }
        //域名根据业务需求判断,这里判断相同
        if(!domain1.equals(domain2)){
            return new BigDecimal(0);
        }
        List<String> path1Arr = Arrays.asList(path1.split("/"));
        List<String> path2Arr = Arrays.asList(path2.split("/"));
        if(path1Arr.size() == 0 || path2Arr.size() == 0){
            return new BigDecimal(0);
        }
        //去掉拆分出来第一个为空的无用字符串
        path1Arr = path1Arr.subList(1, path1Arr.size());
        path2Arr = path2Arr.subList(1, path2Arr.size());
        //目录深度必须相同
        if(path1Arr.size() != path2Arr.size()){
            return new BigDecimal(0);
        }
        //计算每级的目录出现的频次
        Map<String, AtomicInteger> frequency1 = getFrequency(path1Arr);
        Map<String, AtomicInteger> frequency2 = getFrequency(path2Arr);
        //1取并集 去重
        List<String> allPathArr = new ArrayList<>();
        allPathArr.addAll(path1Arr);
        allPathArr.addAll(path2Arr);
        List<String> allDistinctList = allPathArr.stream().distinct().collect(Collectors.toList());
        //定义向量a 和向量b 采用余弦相似公式计算相似度 余弦相似公式:(向量a * 向量b)/向量a的绝对值 * 向量b的绝对值
        BigDecimal ab = new BigDecimal(0);// a.b
        BigDecimal aa = new BigDecimal(0);// |a|的平方
        BigDecimal bb = new BigDecimal(0);// |b|的平方
        for (String s : allDistinctList) {
            //同个目录出现的次数作为 向量的坐标
            AtomicInteger x1 = frequency1.get(s);
            AtomicInteger x2 = frequency2.get(s);
            if(x1 != null && x2 != null){
                ab = ab.add(new BigDecimal(x1.get() * x2.get()));
            }
            if(x1 != null){
                aa = aa.add(new BigDecimal(x1.get() * x1.get()));
            }
            if(x2 != null){
                bb = bb.add(new BigDecimal(x2.get() * x2.get()));
            }
        }
        double aaa = Math.sqrt(aa.doubleValue());
        double bbb = Math.sqrt(bb.doubleValue());
        BigDecimal aabb = BigDecimal.valueOf(aaa).multiply(BigDecimal.valueOf(bbb));
        return ab.divide(aabb, 2, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 统计字符串出现次数
     * @param list
     * @return
     */
    public static Map<String, AtomicInteger> getFrequency(List<String> list){
        Map<String, AtomicInteger> freq = new HashMap<>();
        list.forEach(i -> freq.computeIfAbsent(i, k -> new AtomicInteger()).incrementAndGet());
        return freq;
    }

转换正则

public static String getRegex(String path1, String path2){
        List<String> path1Arr = Arrays.asList(path1.split("/"));
        List<String> path2Arr = Arrays.asList(path2.split("/"));
        List<String> path1List = path1Arr.subList(1, path1Arr.size());
        List<String> path2List = path2Arr.subList(1, path2Arr.size());
        List<String> collect1 = path1List.stream().filter(s -> !path2List.contains(s)).collect(Collectors.toList());
        List<String> collect2 = path2List.stream().filter(s -> !path1List.contains(s)).collect(Collectors.toList());
        for (String s : collect1) {
            int i = path1Arr.indexOf(s);
            path1Arr.set(i, any_string);
        }
        for (String s : collect2) {
            int i = path2Arr.indexOf(s);
            path2Arr.set(i, any_string);
        }
        String regex1 = String.join("/", path1Arr);
        String regex2 = String.join("/", path2Arr);
        if(regex1.equals(regex2)){
            return regex1;
        }
        return "";
    }

测试结果

public static void main(String[] args) {
        String domain1 = "www.aaa.com";
        String domain2 = "www.aaa.com";
        String path1 = "/aaa/bbb/ccc/12345/kkk/45678";
        String path2 = "/aaa/bbb/ccc/45235/kkk/52545252";
        System.out.println(findLike(domain1, path1, domain2, path2).doubleValue());
        System.out.println(getRegex(path1, path2));
    }
0.67
/aaa/bbb/ccc/[^/]+/kkk/[^/]+

Process finished with exit code 0
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值