Java—SimHash原理与实现

SimHash 原理

原理链接

SimHash 实现

package GetSimilar;import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;import org.ansj.domain.Result;
import org.ansj.domain.Term;
import org.ansj.recognition.impl.StopRecognition;
import org.ansj.splitWord.analysis.ToAnalysis;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;/*** 完成SimHash的计算,将每个词向量的权重由idf来决定* * @author Ove**/
public class SimHash2 {private int hashbits = 64; // 分词后的hash数;public Map<String, Double> IDF;public SimHash2(){IDF = getIDF();}public SimHash2(int hashbits) {this.hashbits = hashbits;}// 读取本地文件,获得对应词的idf值public Map<String, Double> getIDF(){Map<String, Double> result = new HashMap<String, Double>();try {// 打开文件FileInputStream fis = new FileInputStream("D:/Document/data/Similar/idf.txt");BufferedReader br = new BufferedReader(new InputStreamReader(fis, "UTF-8"));String line = "";while ((line = br.readLine()) != null) {String[] split = line.split("\t");result.put(split[0], Double.valueOf(split[1]));}// 关闭文件br.close();fis.close();} catch (Exception e) {e.printStackTrace();}return result;}/*** 清除html标签* * @param content* @return*/private String cleanResume(String content) {// 若输入为HTML,下面会过滤掉所有的HTML的tagcontent = Jsoup.clean(content, Whitelist.none());content = StringUtils.lowerCase(content);String[] strings = { " ", "\n", "\r", "\t", "\\r", "\\n", "\\t", "&nbsp;" };for (String s : strings) {content = content.replaceAll(s, "");}return content;}/*** 这个是对整个字符串进行hash计算* * @return*/private BigInteger simHash(String tokens) {tokens = cleanResume(tokens); // cleanResume 删除一些特殊字符int[] v = new int[this.hashbits];Result ansjList = wordAnalyzer(tokens);// System.out.println(ansjList);// 标识该文档中每个词出现的次数Map<String, Integer> wordCount = new HashMap<String, Integer>();Integer count = 0;for (Term term : ansjList) {count = wordCount.get(term.getName());if (count == null) {wordCount.put(term.getName(), 1);} else {wordCount.put(term.getName(), count + 1);}}int len = wordCount.size();String word = "";for (Term term : ansjList) {word = term.getName(); // 分词字符串// 2、将每一个分词hash为一组固定长度的数列.比如 64bit 的一个整数.BigInteger t = this.hash(word);for (int i = 0; i < this.hashbits; i++) {BigInteger bitmask = new BigInteger("1").shiftLeft(i);// 3、建立一个长度为64的整数数组(假设要生成64位的数字指纹,也可以是其它数字),// 对每一个分词hash后的数列进行判断,如果是1000...1,那么数组的第一位和末尾一位加1,// 中间的62位减一,也就是说,逢1加1,逢0减1.一直到把所有的分词hash数列全部判断完毕.double tf = (double) wordCount.get(word) / len;if(IDF.get(word)==null) continue;Double weight = 100 * tf * IDF.get(word); // 添加权重,权重应改为出现次数,而不是根据词性来指定。
//              if(weight==null) continue;// if (wordCount.containsKey(word)) {// weight = wordCount.get(word);// }if (t.and(bitmask).signum() != 0) {// 这里是计算整个文档的所有特征的向量和v[i] += weight;} else {v[i] -= weight;}}}BigInteger fingerprint = new BigInteger("0");for (int i = 0; i < this.hashbits; i++) {if (v[i] >= 0) {fingerprint = fingerprint.add(new BigInteger("1").shiftLeft(i));}}return fingerprint;}/*** 对单个的分词进行hash计算;* * @param source* @return*/private BigInteger hash(String source) {if (source == null || source.length() == 0) {return new BigInteger("0");} else {/*** 当sourece 的长度过短,会导致hash算法失效,因此需要对过短的词补偿*/while (source.length() < 3) {source = source + source.charAt(0);}char[] sourceArray = source.toCharArray();BigInteger x = BigInteger.valueOf(((long) sourceArray[0]) << 7);BigInteger m = new BigInteger("1000003");BigInteger mask = new BigInteger("2").pow(this.hashbits).subtract(new BigInteger("1"));for (char item : sourceArray) {BigInteger temp = BigInteger.valueOf((long) item);x = x.multiply(m).xor(temp).and(mask);}x = x.xor(new BigInteger(String.valueOf(source.length())));if (x.equals(new BigInteger("-1"))) {x = new BigInteger("-2");}return x;}}/*** 计算海明距离,海明距离越小说明越相似;* * @param one* @param two* @return*/public int hammingDistance(String s1, String s2) {BigInteger one = this.simHash(s1);BigInteger two = this.simHash(s2);BigInteger m = new BigInteger("1").shiftLeft(this.hashbits).subtract(new BigInteger("1"));BigInteger x = one.xor(two).and(m);int tot = 0;while (x.signum() != 0) {tot += 1;x = x.and(x.subtract(new BigInteger("1")));}return tot;}/*** 计算文本相似度* * @param s1* @param s2* @return*/public double getSemblance(String s1, String s2) {double i = (double) this.hammingDistance(s1, s2);return 1 - i / this.hashbits;}/*** 对单行文本进行分词,分词后每个词以空格相隔开* * @param line* @return*/public static Result wordAnalyzer(String line) {StopRecognition filter = new StopRecognition();filter.insertStopNatures("w"); // 过滤标点filter.insertStopNatures("null"); // 过滤空格filter.insertStopNatures("m"); // 过滤数词,会将 半数 该词当做是数词进行过滤String[] stopWords = { "如图所示", "的", "中", "下列", "说法", "正确", "是", "若", "为", "则", "在" };filter.insertStopWords(stopWords); // 过滤单个词Result fliterContent = ToAnalysis.parse(line).recognition(filter);return fliterContent;}public static void main(String[] args) {String s1 = "如图所示,电源电压保持不变,闭合开关 S 0,滑动变阻器R的滑片向右移动的过程中,下列说法正确的是 A.闭合开关S,若甲、乙均为电压表,则两表示数均变小 B.断开开关S,若甲、乙均为电流表,则两表示数均变大 C.闭合开关S,若甲、乙均为电压表,则甲示数不变,乙示数变大 D.断开开关S,若甲、乙均为电流表,则乙示数不变,甲示数变小";String s2 = "小明今天去吃肯德基";SimHash ob = new SimHash();System.out.println(ob.getSemblance(s1, s2));// System.out.println(new BigInteger("1000003"));}}

Java---SimHash原理与实现相关推荐

  1. [转] 文本相似性算法Simhash原理及实践

    simhash(局部敏感哈希)的原理 simhash的背景 simhash广泛的用于搜索领域中,也许在面试时你会经常遇到这样的问题,如果对抓取的网页进行排重,如何对搜索结果进行排重等等.随着信息膨胀时 ...

  2. 《Java虚拟机原理图解》5. JVM类加载器机制与类加载过程

    参考网址:http://blog.csdn.net/luanlouis/article/details/50529868 0.前言 读完本文,你将了解到: 一.为什么说Jabalpur语言是跨平台的 ...

  3. 【Java 虚拟机原理】Class 字节码二进制文件分析 七 ( 局部变量表分析 )

    文章目录 前言 一.编译生成带局部变量表的字节码文件 二.局部变量表 前言 上一篇博客 [Java 虚拟机原理]Class 字节码二进制文件分析 二 ( 常量池位置 | 常量池结构 | tag | i ...

  4. 【Java 虚拟机原理】Class 字节码二进制文件分析 六 ( 属性类型 | Code 属性 | 属性名称索引 | 属性长度 | 操作数栈最大深度 | 局部变量存储空间 | 字节码长度 )

    文章目录 前言 一.属性类型 二.Code 属性表数据结构 三.属性名称索引 四.属性长度 五.操作数栈最大深度 六.局部变量存储空间 七.字节码长度 八.存储字节码指令的一系列字节流 前言 上一篇博 ...

  5. 【Java 虚拟机原理】Class 字节码二进制文件分析 五 ( 方法计数器 | 方法表 | 访问标志 | 方法名称索引 | 方法返回值类型 | 方法属性数量 | 方法属性表 )

    文章目录 前言 一.方法表结构 二.方法计数器 三.方法表数据解析 ( init 构造方法 ) 1.方法访问标志 2.方法名称索引 3.方法返回类型 4.方法属性数量 前言 上一篇博客 [Java 虚 ...

  6. 【Java 虚拟机原理】Class 字节码二进制文件分析 四 ( 字段表数据结构 | 字段表详细分析 | 访问标志 | 字段名称 | 字段描述符 | 属性项目 )

    文章目录 前言 一.字段表总数据结构 二.访问标志 三.字段名称 四.字段描述符 五.属性项目数 前言 上一篇博客 [Java 虚拟机原理]Class 字节码二进制文件分析 三 ( 访问和修饰标志 | ...

  7. 【Java 虚拟机原理】Class 字节码二进制文件分析 三 ( 访问和修饰标志 | 类索引 | 父类索引 | 接口计数器 | 接口表 | 字段计数器 | 字段表 )

    文章目录 前言 一.访问和修饰标志 二.类索引 三.父类索引 四.接口计数器 五.接口表 六.字段计数器 七.字段表 前言 上一篇博客 [Java 虚拟机原理]Class 字节码二进制文件分析 二 ( ...

  8. 【Java 虚拟机原理】Class 字节码二进制文件分析 二 ( 常量池位置 | 常量池结构 | tag | info[] | 完整分析字节码文件中的常量池二进制数据 )

    文章目录 前言 一.常量池结构分析 1.常量池位置 2.常量池结构 3.常量池单个常量 4.常量池单个常量 tag 标签 二.常量池字节码文件分析 0.常量池附加信息 1.常量池 #1 常量分析 2. ...

  9. Java NIO原理 图文分析及代码实现

    最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.可以参考: ...

  10. java native方法_并发系列-native函数回调Java方法原理实践

    写在前面 上一篇分享了Java调用native函数过程原理实践,文章最后留了一个问题,本章主要对C程序回调我们的Java程序原理进行实践. 调用C程序之后他是怎么知道来调用我们我们的哪个方法?又是如何 ...

最新文章

  1. python语言使用什么语句实现上下文管理协议_Python with语句上下文管理器两种实现方法分析...
  2. 《系统集成项目管理工程师》必背100个知识点-32工作分解结构步骤
  3. Object.defineProperty 详解
  4. Python 3.6学习笔记(一)
  5. java知识体系 servlet_03-Servlet 体系结构知识梳理
  6. linux下安装jmeter
  7. 带线程池的socket客户端与服务端
  8. 2018年股票操作策略记录(1)
  9. 发现两个有趣的CSS3效果
  10. 投入工作与生活幸福,并非简单对立
  11. Docker教程小白实操入门(4)--如何停止一个容器
  12. 20201022-成信大-C语言程序设计-20201学期《C语言程序设计B》C-trainingExercises05
  13. 如何找python 包的位置
  14. [苹果开发者账号]06 转让开发者账号后,开发者年费自动续费问题
  15. sublime Mac版怎么安装? Mac下载安装sublime的教程
  16. 《埃尔维斯与安娜贝尔》配色分享
  17. 获取IPhone相册中图片的方法(ASSets)
  18. JSP水电费管理系统myeclipse开发mysql数据库web结构java编程
  19. 产生粗体字的html标签,html字体加粗用css设置文字粗体样式
  20. python 下载图片

热门文章

  1. [转载]J2ME无线开发之MIDlet的部署
  2. CCF CSP 碰撞的小球 C++
  3. emule-Xtreme(电驴)
  4. OpenSSL心脏出血漏洞全回顾
  5. 物流英语与计算机操作,物流英语与计算机模拟题及正确答案[精选].doc
  6. 如何使用Python官网的用户手册
  7. 全国计算机二级考c语言还是access还是mysql_全国计算机二级考试哪个最简单
  8. JMETER time 获取上个月时间
  9. xampp 配置laravel5.4环境
  10. GoEasy在微信小程序中的应用