使用MinHash算法计算两个集合的相似度

集合相似度计算是一个常见的问题。例如,已知看过芈月传的人都有哪些,还知道看过琅琊榜的人都有哪些,那么想知道同时看过两者的人群占至少看过一部的人群的占比,就是求这两个集合的相似度:

集合A = 看过芈月传的人群
集合B = 看过琅琊榜的人群
相似度 = |A∩B| / |A∪B| = 既看过芈月传又看过琅琊榜的人数 / 看过芈月传或琅琊榜的人数

当集合的元素较少时,我们可以采用逐一比较的方式来找出既在集合A出现也在集合B出现的人,统计其人数,再除以至少在集合A或集合B出现的人数,得到相似度。

然而当集合的元素较多时,比如每个集合都有上百万或上千万个元素,采用复杂度为O(n)的逐一比较方式就比较捉急了,无法胜任要求即时得到计算结果的场景。

而使用MinHash算法,可以将复杂度降为常数。

让我通过一个例子解释MinHash算法。假设你是野生动物园的管理员,负责管理一座猴山,猴山没有栅栏,所以虽然猴子大多习惯呆在一座山上,但它们也可以跑到你的管辖范围外,外面的猴子也可以到你的山头拜访。

如果你突然想知道这个月月初的猴群和月末的猴群有多少重叠,即常住“猴口”的占比是多少,就是在面对一个相似度问题。按照上文提供的逐一比较的思路,你可以在月初抓到所有的猴子,统计数量为C1,并给它们戴上号码牌后释放。到了月末,你再次上山统计所有的猴子,有号牌的猴子的数量为C2a,没号牌的猴子的数量为C2b,则你的答案就是C2a / (C1 + C2b),大功告成。

然而这种方法太劳猴伤财了,你决定换一个简单但粗略的方法。你上山观察,找到最小的猴子,抓来给它戴上号码牌后释放,并记录下它的号码,然后再找到最大的猴子,做同样的事,再找到最瘦的猴子,做同样的事,然后是最胖的猴子,尾巴最长的猴子,尾巴最短的猴子,屁股最红的猴子,以及各种最XX的猴子,得到记录如下:

特征 | 号码
最小 | 1
最大 | 2
最瘦 | 3
...
最精 | 19
最傻 | 20

到了月末,你带着记录本再次上山,找到最小的猴子,如果它有号牌且号码是1,就意味着这只猴子就是月初那一只,你就在本子上画一个圈,如果它不是月初那只,就画一个叉。然后再找到最大的猴子,做同样的事,然后再继续,直到找完所有最XX的猴子。你低头看记录本,有15个圈,5个叉,那么相似度就是15 / (15 + 5) = 75%。

为什么?

简单来说,你每次带着目的去寻找某种视角下最XX的猴子时,实际都是在为猴群建立一个索引。而当排在两个集合的索引第一位的元素相同时,就意味着这两个集合有了一定的相似度,因为它们不仅同时包含这个元素,而且它们的所有元素在某种视角下都大于等于该元素。由于每次索引的视角都不同,所以当我们比较了20次不同的索引的第一位,其中有15次都遇到了相同的元素,就可以说这两个集合相当、相当的相似了。

让我们换成数学语言。已知集合X,设每次用来索引的函数为H(X),索引中第一个元素为Hmin(X),则存在如下公式:

Pr[Hmin(A) = Hmin(B)] = |A∩B| / |A∪B|
注:Pr表示概率。

这个公式的证明我先不帖了,但可以从一种极端情况简单验证该公式的正确性。假设A∪B的元素个数为n,我们在理想情况下找到n个H(X),分别是H1(X), H2(X) ... Hi(X) ... Hn(X),且它们满足当元素k = i时,Hi(k) = i,当元素k ≠ i时,Hi(k) = ∞。由此,当集合中存在元素i时,Himin(X) = i,而当集合中不存在元素i时,Himin(X) = ∞,于是满足Himin(A) = Himin(B)的充要条件就是A和B中都存在元素i,所以满足Hmin(A) = Hmin(B)的H(X)的个数就等于|A∩B|,即:

|A∩B| / |A∪B| = 满足Hmin(A)等于Hmin(B)的H(X)的个数 / H(X)的个数 ≈ Pr[Hmin(A) = Hmin(B)]

以上就是MinHash算法的讲解。在实际应用中,当一个集合A的元素确定后,就可以调用一系列预先定义好的H(X)进行计算,得到一个数组[ H1min(A), H2min(A) ... Himin(A) ... Hnmin(A) ],将该数组作为中间数据进行存储,在每次需要即时计算任意两个集合A和B的相似度时读取每个集合对应的数组,通过定义如下随机变量r:

r = 1 if Hmin(A) = Hmin(B) else 0

得到相似度为:

∑r / n

在我这边的应用里,n = 2048。所以不管每个集合里有几百几千万个元素,都会被预先降维成包含2048个元素的数组(一律2048,通通2048,算起来不吃亏,算起来不上当^_^),后续的相似度计算的复杂度自然也成了一个常数。

参考:
https://en.wikipedia.org/wiki/MinHash

时间: 2024-12-13 04:49:55

使用MinHash算法计算两个集合的相似度的相关文章

计算两个集合的差集——第六期 Power8 算法挑战赛

第六期Power8大赛 1.1 比赛题目 题目: 计算两个集合的差集: 详细说明: 分别有集合A和B两个大数集合,求解集合A与B的差集(A中有,但B中无的元素),并将结果保存在集合C中,要求集合C中的元素升序. 输入为两个文件,分别为A.txt,B.txt,一行一个值,并且是无序的.结果输出到C.txt,即输入文件的差集,一行一个值,并且要求结果升序排列. 考量点: (1) 大数集合求差集: (2) 大数据集合排序: 题目实例: 例如,若集合A={5,20,10,15,25,30},集合B={1

计算两个集合的交集数字(java)

循环判断2个数组 将相同的公共元素复制到新数组中即可 1 2 3 import java.util.Arrays; 4 5 public class count_same_number { 6 7 public static int[] join(int[] a,int[] b) 8 { 9 int count=0; 10 int new_target[]=new int[Math.max(a.length, b.length)];//新数组 11 int index=0; 12 for(int

比较字符串总结:如果计算两个字符串的相似度或者距离

有关字符串相关比较问题,一般处理方法都选择动态规划处理方法.或者是单个循环,或者是多个循环:dp 遇到这种两个串的问题,很容易想到DP.但是这道题的递推关系不明显.可以先尝试做一个二维的表int[][] dp,用来记录匹配子序列的个数(以S="rabbbit",T = "rabbit"为例): r a b b b i t 1 1 1 1 1 1 1 1 r 0 1 1 1 1 1 1 1 a 0 0 1 1 1 1 1 1 b 0 0 0 1 2 3 3 3 b 0

编程之美3.3 计算两个字符串的相似度

      假如有两个字符串分别是:abcd,bbcd,那么,这两个字符串不相同的字符个数是1,即第一个字符时不相同的,定义字符串的相似度为 1 / (x + 1),其中,x 就是不相同的字符个数.       我们可以有三种方法比较两个字符串中不相同字符的个数:       1.去掉第一个字符串中不相同的那个字符,并同时再去比较下一个字符       2.去掉第二个字符串中不相同的那个字符,并同时再去比较下一个字符       3.同时去掉字符串中不相同的那个字符,并同时再去比较下一个字符  

C#通过编辑距离计算两个字符串的相似度的代码

将开发过程中较好的一些代码段备份一下,下面的代码是关于C#通过编辑距离计算两个字符串的相似度的代码,应该能对码农们有些帮助. using System; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Levenshtein { public delegate void AnalyzerCompletedHander(double sim); public class Levenshtei

计算两个字符串的相似度---动态规划实现

问题描述:把两个字符串变成相同的基本操作定义如下:1.     修改一个字符(如把 a 变成 b)2.     增加一个字符 (如 abed 变成 abedd)3.     删除一个字符(如 jackbllog 变成 jackblog)针对于 jackbllog到jackblog 只需要删除一个或增加一个 l 就可以把两个字符串变为相同.把这种操作需要的次数定义为两个字符串的距离 L, 则相似度定义为1/(L+1) 即距离加一的倒数.那么jackbllog和jackblog的相似度为 1/1+1

matlab中怎样计算两个集合的差集?-setdiff函数

1 >> a=randperm(10); 2 >> a 3 4 a = 5 6 4 10 7 5 2 6 8 9 1 3 7 8 >> b=a(4:6); 9 >> b 10 11 b = 12 13 5 2 6 14 15 >> c=setdiff(a,b); 16 >> c 17 18 c = 19 20 1 3 4 7 8 9 10 上面例子中c=setdiff(a,b);表示计算a集合中元素减去b集合中的元素;

MinHash算法

MinHash是用于快速检测两个集合的相似性的方法.改方法由Andrei Broder(1997)发明,并最初用于搜索引擎AltaVista中来检测重复的网页的算法.它同样可以用于推荐系统和大规模文档聚类中. 我们先介绍Jaccard相似度量.对于两个集合A与B,Jaccard相似性系数可以定义为: 容易知道,Jaccard系数是0-1之间的值.当两个集合越接近,那么该值越接近1:反之跟接近0. 假设h是一个hash function,将A与B的元素映射成一个整数,定义:是集合S中具有最小哈希值

文本去重之MinHash算法

1.概述 跟SimHash一样,MinHash也是LSH的一种,可以用来快速估算两个集合的相似度.MinHash由Andrei Broder提出,最初用于在搜索引擎中检测重复网页.它也可以应用于大规模聚类问题. 2.Jaccard index 在介绍MinHash之前,我们先介绍下Jaccard index. Jaccard index是用来计算相似性,也就是距离的一种度量标准.假如有集合A.B,那么, 也就是说,集合A,B的Jaccard系数等于A,B中共同拥有的元素数与A,B总共拥有的元素数