网页内容相似度之SimHash算法

  抓取的网页内容中,有大部分会是相似的,抓取时就要过滤掉,开始考虑用VSM算法,后来发现不对,要比较太多东西了,然后就发现了simHash算法,这个算法的解释我就懒得copy了,simhash算法对于短数据的支持不好,但是,我本来就是很长的数据,用上!

  源码实现网上也有不少,但是貌似都是同样的,里面写得不清不楚的,虽然效果基本能达到,但是不清楚的东西,我用来做啥?

  仔细研究simhash算法的说明后,把里面字符串的hash算法换成的fvn-1算法,这个在http://www.isthe.com/chongo/tech/comp/fnv/里面有说明了,具体的那些固定数值,网站上都写了。原先代码里面有些处理,和算法不符的,也换掉了。

  首先搞起IKAnalyzer,切词并计算每个词的频率:

package com.cnblogs.zxub.lucene.similarity;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.wltea.analyzer.lucene.IKAnalyzer;

public class WordsSpliter {

    public static Map<String, Integer> getSplitedWords(String str)
            throws IOException {
        // str = str.replaceAll("[0-9a-zA-Z]", "");
        Analyzer analyzer = new IKAnalyzer();
        Reader r = new StringReader(str);
        TokenStream ts = analyzer.tokenStream("searchValue", r);
        ts.addAttribute(CharTermAttribute.class);

        Map<String, Integer> result = new HashMap<String, Integer>();
        while (ts.incrementToken()) {
            CharTermAttribute ta = ts.getAttribute(CharTermAttribute.class);
            String word = ta.toString();
            if (!result.containsKey(word)) {
                result.put(word, 0);
            }
            result.put(word, result.get(word) + 1);
        }

        return result;
    }
}

  然后把SimHash的算法搞上:

package com.cnblogs.zxub.lucene.similarity;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Map;
import java.util.Set;

public class SimHash {

    private static final int HASH_BITS = 64;
    private static final BigInteger FNV_64_INIT = new BigInteger(
            "14695981039346656037");
    private static final BigInteger FNV_64_PRIME = new BigInteger(
            "1099511628211");
    private static final BigInteger MASK_64 = BigInteger.ONE.shiftLeft(
            HASH_BITS).subtract(BigInteger.ONE);

    private String hash;
    private BigInteger signature;

    public SimHash(String content) throws IOException {
        super();
        this.setFingerPrint(WordsSpliter.getSplitedWords(content));
    }

    public String getHash() {
        return this.hash;
    }

    public BigInteger getSignature() {
        return this.signature;
    }

    private void setFingerPrint(Map<String, Integer> wordInfos) {
        int[] featureVector = new int[SimHash.HASH_BITS];
        Set<String> words = wordInfos.keySet();
        for (String word : words) {
            BigInteger wordhash = this.fnv1_64_hash(word);
            for (int i = 0; i < SimHash.HASH_BITS; i++) {
                BigInteger bitmask = BigInteger.ONE.shiftLeft(SimHash.HASH_BITS
                        - i - 1);
                if (wordhash.and(bitmask).signum() != 0) {
                    featureVector[i] += wordInfos.get(word);
                } else {
                    featureVector[i] -= wordInfos.get(word);
                }
            }
        }

        BigInteger signature = BigInteger.ZERO;
        StringBuffer hashBuffer = new StringBuffer();
        for (int i = 0; i < SimHash.HASH_BITS; i++) {
            if (featureVector[i] >= 0) {
                signature = signature.add(BigInteger.ONE
                        .shiftLeft(SimHash.HASH_BITS - i - 1));
                hashBuffer.append("1");
            } else {
                hashBuffer.append("0");
            }
        }
        this.hash = hashBuffer.toString();
        this.signature = signature;
    }

    // fnv-1 hash算法,将字符串转换为64位hash值
    private BigInteger fnv1_64_hash(String str) {
        BigInteger hash = FNV_64_INIT;
        int len = str.length();
        for (int i = 0; i < len; i++) {
            hash = hash.multiply(FNV_64_PRIME);
            hash = hash.xor(BigInteger.valueOf(str.charAt(i)));
        }
        hash = hash.and(MASK_64);
        return hash;
    }

    public int getHammingDistance(BigInteger targetSignature) {
        BigInteger x = this.getSignature().xor(targetSignature);
        String s = x.toString(2);
        return s.replaceAll("0", "").length();
    }

    public int getHashDistance(String targetHash) {
        int distance;
        if (this.getHash().length() != targetHash.length()) {
            distance = -1;
        } else {
            distance = 0;
            for (int i = 0; i < this.getHash().length(); i++) {
                if (this.getHash().charAt(i) != targetHash.charAt(i)) {
                    distance++;
                }
            }
        }
        return distance;
    }
}

  数据库里面存个签名就好了,至于距离运算,本打算全部拉出来计算,后来发现oracle的bitand函数,就用它了!异或之后,转二进制字符串,把0去掉,取长度,再count一下长度小于4的,得到的结果就是很相似的内容数目了。以后再把计算改成用缓存的去,先偷个懒。

  收摊!

网页内容相似度之SimHash算法,布布扣,bubuko.com

时间: 2024-12-15 16:42:19

网页内容相似度之SimHash算法的相关文章

simhash算法实现--查找文件相似度

一.Simhash简介 SimHash是用来网页去重最常用的hash方法,速度很快.Google采用这种算法来解决万亿级别的网页去重任务. SimHash算法的主要思想是降维.将高维的特征向量映射成一个低维的特征向量,通过两个向量的Hamming Distance来确定文章是否重复或者高度近似. 在simhash的发明人Charikar的论文中并没有给出具体的simhash算法和证明,"量子图灵"得出的证明simhash是由随机超平面hash算法演变而来的. 参考文献:<Dete

基于局部敏感哈希的协同过滤算法之simHash算法

搜集了快一个月的资料,虽然不完全懂,但还是先慢慢写着吧,说不定就有思路了呢. 开源的最大好处是会让作者对脏乱臭的代码有羞耻感. 当一个做推荐系统的部门开始重视[数据清理,数据标柱,效果评测,数据统计,数据分析]这些所谓的脏活累活,这样的推荐系统才会有救. 求教GitHub的使用. 简单不等于傻逼. 我为什么说累:我又是一个习惯在聊天中思考前因后果的人,所以整个大脑高负荷运转.不过这样真不好,学习学成傻逼了. 研一的最大收获是让我明白原来以前仰慕的各种国家自然基金项目,原来都是可以浑水摸鱼忽悠过去

MLlearning(2)——simHash算法

这篇文章主要讲simHash算法.这是一种LSH(Locality-Sensitive Hashing,局部敏感哈希)的简单实现.它是广泛用于数据去重的算法,可以用于相似网站.图片的检索.而且当两个样本差别并不大时,算法仍能起效.值得一提的是,该算法的时空复杂度不存在与维度有关的项,所以不会遭遇维度灾难,也可以在维数较高时优化kNN算法. 特征 此算法(LSH)具有双重性,它们似乎是相悖的: 对于几组不同的特征,hash相同(即冲突)的可能性要尽可能小.这也是hash基本的特征. 对于几组相似的

海量数据去重之SimHash算法简介和应用

SimHash是什么 SimHash是Google在2007年发表的论文<Detecting Near-Duplicates for Web Crawling >中提到的一种指纹生成算法或者叫指纹提取算法,被Google广泛应用在亿级的网页去重的Job中,作为locality sensitive hash(局部敏感哈希)的一种,其主要思想是降维,什么是降维? 举个通俗点的例子,一篇若干数量的文本内容,经过simhash降维后,可能仅仅得到一个长度为32或64位的二进制由01组成的字符串,这一点

(转)simhash算法原理及实现

simhash是google用来处理海量文本去重的算法. google出品,你懂的. simhash最牛逼的一点就是将一个文档,最后转换成一个64位的字节,暂且称之为特征字,然后判断重复只需要判断他们的特征字的距离是不是<n(根据经验这个n一般取值为3),就可以判断两个文档是否相似. 原理 simhash值的生成图解如下: 大概花三分钟看懂这个图就差不多怎么实现这个simhash算法了.特别简单.谷歌出品嘛,简单实用. 算法过程大概如下: 将Doc进行关键词抽取(其中包括分词和计算权重),抽取出

simhash算法:海量千万级的数据去重

simhash算法:海量千万级的数据去重 simhash算法及原理参考: 简单易懂讲解simhash算法 hash 哈希:https://blog.csdn.net/le_le_name/article/details/51615931 simhash算法及原理简介:https://blog.csdn.net/lengye7/article/details/79789206 使用SimHash进行海量文本去重:https://www.cnblogs.com/maybe2030/p/5203186

SimHash算法

短文本合并重复(去重)的简单有效做法 - 旁观者 - 博客园 短文本合并重复(去重)的简单有效做法 SimHash算法 - ACdreamer - 博客频道 - CSDN.NET SimHash算法SimHash算法,布布扣,bubuko.com

相似文本文档分析之SimHash算法

SimHash算法: simhash算法的输入是一个向量,输出是一个 f 位的签名值.为了陈述方便,假设输入的是一个文档的特征集合,每个特征有一定的权重.比如特征可以是文档中的词,其权重可以是这个词出现的次数. simhash 算法如下:1,将一个 f 维的向量 V 初始化为 0 : f 位的二进制数 S 初始化为 0 :2,对每一个特征:用传统的 hash 算法对该特征产生一个 f 位的签名 b .对 i=1 到 f :如果b 的第 i 位为 1 ,则 V 的第 i 个元素加上该特征的权重:否

SQL Server对比两字段的相似度(函数算法)

原文:SQL Server对比两字段的相似度(函数算法) 相似度函数 概述    比较两个字段的相似度    最近有人问到关于两个字段求相似度的函数,所以就写了一篇关于相似度的函数,分别是“简单的模糊匹配”,“顺序匹配”,“一对一位置匹配”.在平时的这种函数可能会需要用到,可能业务需求不一样,这里只给出参照,实际情况可以相对修改. 本文所有的两个字段比较都是除以比较字段本身,例如A与B比较,找出的长度除以A的长度,因为考虑如果A的长度大于B的长度,相似度会超100%,例如‘abbc’,'ab'.