两个文本相似度算法实现和对比
背景
最近做一个爬虫相关的项目,需要排除掉一些相似的链接,比如分页控件里上一页,下一页等等没什么用的链接.
编辑距离算法
编辑距离,又称Levenshtein距离(莱文斯坦距离也叫做Edit Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数,如果它们的距离越大,说明它们越是不同。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
这个概念是由俄罗斯科学家Vladimir Levenshtein在1965年提出来的,所以也叫 Levenshtein 距离。它可以用来做DNA分析,拼字检测,抄袭识别等等。总是比较相似的,或多或少我们可以考虑编辑距离。
如果你去搜索编辑距离算法的话可能会看到下面的例子
如果str1=”ivan”,str2=”ivan”,那么经过计算后等于 0。没有经过转换。相似度=1-0/Math.Max(str1.length,str2.length)=1
如果str1=”ivan1”,str2=”ivan2”,那么经过计算后等于1。str1的”1”转换”2”,转换了一个字符,所以距离是1,相似度=1-1/Math.Max(str1.length,str2.length)=0.8
注意算法中的1,其实是固定的值.我就是因为这个值总是算不对走了一些弯路.还有一些文章中介绍算法的时候会用矩阵计算.但是我数学功底很差,所以用了另一种方式实现.
通过算法描述中可以知道,字符串的编辑操作可以是插入,删除,修改.这三个方式的编辑操作,操作数都记为1.那么我的实现中,其实是移除了插入和删除的两个操作,全部是修改操作.实现如下,有什么问题的话还请指出.谢谢!
public static int Levenshtein(string str1, string str2)
{
var maxLen = Math.Max(str1.Length, str2.Length);
var tmp1 = str1.PadRight(maxLen);
var tmp2 = str2.PadRight(maxLen);
var interval = 0.0f;
for (int i = 0; i < maxLen; i++)
{
if (tmp1[i] != tmp2[i])
{
interval += 1;
}
}
return Convert.ToInt32((1 - interval / maxLen) * 100);
}
杰卡德相似系数
Jaccard index, 又称为Jaccard相似系数(Jaccard similarity coefficient)用于比较有限样本集之间的相似性与差异性。Jaccard系数值越大,样本相似度越高。
给定两个集合A,B,Jaccard 系数定义为A与B交集的大小与A与B并集的大小的比值
这个算法还是比较容易理解的,其实就是计算两个字符串中字符的交集和并集的比值.在实际使用时两个长度不相同的链接相似度过高的问题,而且在比对url这种场景下反斜线对于相似度的判断还是有一些影响的.经过简单的修改实现的代码如下
public static int Jaccard(string str1, string str2)
{
var tmp1 = Regex.Replace(str1, "/", "");
var tmp2 = Regex.Replace(str2, "/", "");
var intersect = tmp1.Intersect(tmp2).Count();
var union = tmp1.Union(tmp2).Count();
var abs = Math.Abs(tmp2.Length - tmp1.Length);
return Convert.ToInt32((double)intersect / (union + abs) * 100);
}
对比
分别使用两个算法计算以下两个网址的相似度
https://news.cnblogs.com/n/597903/
https://news.cnblogs.com/n/597947/
结果如下
算法 | 相似度 |
---|---|
Jaccard | 86% |
Levenshtein | 94% |
延展
通过相似度算法似乎也可以实现爬虫中只抓取列表页中的内容链接