两个文本相似度算法实现和对比

背景

最近做一个爬虫相关的项目,需要排除掉一些相似的链接,比如分页控件里上一页,下一页等等没什么用的链接.

编辑距离算法

编辑距离,又称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%

延展

通过相似度算法似乎也可以实现爬虫中只抓取列表页中的内容链接

posted @ 2018-05-30 14:04  陈宏博  阅读(10590)  评论(0编辑  收藏  举报