算法小练习之求稀疏相似度

140 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情

1、前言

每天一个算法小练习,本篇使用Java实现。

2、题目描述

  两个(具有不同单词的)文档的交集(intersection)中元素的个数除以并集(union)中元素的个数,就是这两个文档的相似度。例如,{1, 5, 3} 和 {1, 7, 2, 3} 的相似度是 0.4,其中,交集的元素有 2 个,并集的元素有 5 个。给定一系列的长篇文档,每个文档元素各不相同,并与一个 ID 相关联。它们的相似度非常“稀疏”,也就是说任选 2 个文档,相似度都很接近 0。请设计一个算法返回每对文档的 ID 及其相似度。只需输出相似度大于 0 的组合。请忽略空文档。为简单起见,可以假定每个文档由一个含有不同整数的数组表示。

  输入为一个二维数组 docs,docs[i] 表示 id 为 i 的文档。返回一个数组,其中每个元素是一个字符串,代表每对相似度大于 0 的文档,其格式为 {id1},{id2}: {similarity},其中 id1 为两个文档中较小的 id,similarity 为相似度,精确到小数点后 4 位。以任意顺序返回数组均可。

2.1、示例1

输入: 
[
  [14, 15, 100, 9, 3],
  [32, 1, 9, 3, 5],
  [15, 29, 2, 6, 8, 7],
  [7, 10]
]
输出:
[
  "0,1: 0.2500",
  "0,2: 0.1000",
  "2,3: 0.1429"
]

3、解题思路

先对数组进行排序,然后通过双层for循环,依次对对这些有序数组进行求交集与并集的数量,再依次算出相似度。

3.1、实现代码

public List<String> computeSimilarities(int[][] docs) {
        for(int i = 0; i < docs.length; i++) {
            //二维数组排序
            Arrays.sort(docs[i]);
        }
        //存储相似度
        List<String> list = new LinkedList<>();
        //遍历二维数组
        for(int i = 0; i < docs.length-1; i++){
            int[] arri = docs[i];
            for(int j = i+1; j < docs.length; j++){
                int[] arrj = docs[j];
                //两个数组,去求相似度
                double n = rate(arri, arrj);
                if(Double.compare(n, 0) > 0){
                    n = Math.round(n*10000) / 10000d;
                    //记录数组下标与相似度
                    list.add(i+","+j+": "+(n+"000").substring(0,6));
                }
            }
        }
        return list;
    }

    /**
     * 求两个数组的相似度
     * @param arr1
     * @param arr2
     * @return
     */
    private double rate(int[] arr1, int[] arr2){
        int index1 = 0, index2 = 0;
        //交集数量、并集数量
        int coSet = 0, inSet = 0;
        //两个数组的每个元素,都要两两比对
        while(index1 < arr1.length && index2 < arr2.length) {
            //两个元素都相等时,下标都+1
            if(arr1[index1] == arr2[index2]) {
                coSet++;
                index1++;
                index2++;
                continue;
            }
            if(arr1[index1] < arr2[index2]) {
                index1++;
                continue;
            }
            if(arr1[index1] > arr2[index2]) {
                index2++;
                continue;
            }
        }
        //并集数量 = 两个数组长度 - 交集数量
        inSet = arr1.length + arr2.length - coSet;
        return (double)coSet/(double)inSet;
    }

3.2、执行结果

image.png

好了、本期就先介绍到这里,有什么需要交流的,大家可以随时私信我。😊