1.场景:

        在很多公司的文件管理系统中,都有类似于对比多篇文章的相似度,例如在写公众号推文时,如果标记了原创,就会对比当前文章和库里已存在文章的相似程度,如果相似度过于高,则标记为原创的文章无法实现推送,那么,该功能是如何实现的呢?可以参考如下思路。


2.算法:

        此例子借助的是海明距离的实现方式,具体原理请移步(海明距离的定义与说明),此处不做过多的阐述。


3.工具类:SimilarityTwoUtils

package com.alex.examples.utils;import com.hankcs.hanlp.seg.common.Term;
import com.hankcs.hanlp.tokenizer.StandardTokenizer;
import org.apache.commons.lang.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;import java.math.BigInteger;
import java.util.*;/*** 对比多篇文章中内容相似度算法工具类*/
public class SimilarityTwoUtils {/*** 标题名称*/private String topicName;/*** 分词向量*/private BigInteger bigSimHash;/*** 初始桶大小* 备注:对每条文本根据SimHash 算出签名后,再计算两个签名的海明距离(两个二进制异或后1的个数)即可。根* 据经验值,对64位的SimHash,海明距离在3以内的可以认为相似度比较高。* 假设对64位的SimHash,查找海明距离在3以内的所有签名。* 可以把64位的二进制签名均分成4块,每块16位。根据鸽巢原理(也称抽屉原理,见组合数学),如果两个签名的海明距离在3以内,它们必有一块完全相同。* 把上面分成的4块中的每一个块分别作为前16位来进行查找。建立倒排索引。*/private Integer hashCount = 64;/*** 分词最小长度限制*/private static final Integer WORD_MIN_LENGTH = 3;private static final BigInteger ILLEGAL_X = new BigInteger("-1");public SimilarityTwoUtils(String topicName, Integer myHashCount) {this.topicName = topicName;this.bigSimHash = this.simHash();//如果myHashCount为null,则默认64if (null != myHashCount) {this.hashCount = myHashCount;}}/*** 分词计算向量** @return BigInteger*/private BigInteger simHash() {// 清除特殊字符this.topicName = this.clearSpecialCharacters(this.topicName);int[] hashArray = new int[this.hashCount];// 对内容进行分词处理List<Term> terms = StandardTokenizer.segment(this.topicName);// 配置词性权重Map<String, Integer> weightMap = new HashMap<>(16, 0.75F);weightMap.put("n", 1);// 设置停用词Map<String, String> stopMap = new HashMap<>(16, 0.75F);stopMap.put("w", "");// 设置超频词上线Integer overCount = 5;// 设置分词统计量Map<String, Integer> wordMap = new HashMap<>(16, 0.75F);for (Term term : terms) {// 获取分词字符串String word = term.word;// 获取分词词性String nature = term.nature.toString();// 过滤超频词if (wordMap.containsKey(word)) {Integer count = wordMap.get(word);if (count > overCount) {continue;} else {wordMap.put(word, count + 1);}} else {wordMap.put(word, 1);}// 过滤停用词if (stopMap.containsKey(nature)) {continue;}// 计算单个分词的Hash值BigInteger wordHash = this.getWordHash(word);for (int i = 0; i < this.hashCount; i++) {// 向量位移BigInteger bitMask = new BigInteger("1").shiftLeft(i);// 对每个分词hash后的列进行判断,// 例如:1000...1,则数组的第一位和末尾一位加1,中间的62位减一,//       也就是,逢1加1,逢0减1,一直到把所有的分词hash列全部判断完// 设置初始权重Integer weight = 1;if (weightMap.containsKey(nature)) {weight = weightMap.get(nature);}// 计算所有分词的向量if (wordHash.and(bitMask).signum() != 0) {hashArray[i] += weight;} else {hashArray[i] -= weight;}}}// 生成指纹BigInteger fingerPrint = new BigInteger("0");for (int i = 0; i < this.hashCount; i++) {if (hashArray[i] >= 0) {fingerPrint = fingerPrint.add(new BigInteger("1").shiftLeft(i));}}return fingerPrint;}/*** 计算单个分词的hash值** @return BigInteger*/private BigInteger getWordHash(String word) {if (StringUtils.isEmpty(word)) {// 如果分词为null,则默认hash为0return new BigInteger("0");} else {// 分词补位,如果过短会导致Hash算法失败while (word.length() < SimilarityTwoUtils.WORD_MIN_LENGTH) {word = word + word.charAt(0);}// 分词位运算char[] wordArray = word.toCharArray();BigInteger x = BigInteger.valueOf(wordArray[0] << 7);BigInteger m = new BigInteger("1000003");// 初始桶pow运算BigInteger mask = new BigInteger("2").pow(this.hashCount).subtract(new BigInteger("1"));for (char item : wordArray) {BigInteger temp = BigInteger.valueOf(item);x = x.multiply(m).xor(temp).and(mask);}x = x.xor(new BigInteger(String.valueOf(word.length())));if (x.equals(ILLEGAL_X)) {x = new BigInteger("-2");}return x;}}/*** 过滤特殊字符** @return BigInteger*/private String clearSpecialCharacters(String topicName) {// 将内容转换为小写topicName = StringUtils.lowerCase(topicName);// 过来HTML标签topicName = Jsoup.clean(topicName, Whitelist.none());// 过滤特殊字符String[] strings = {" ", "\n", "\r", "\t", "\\r", "\\n", "\\t", "&nbsp;", "&amp;", "&lt;", "&gt;", "&quot;", "&qpos;"};for (String string : strings) {topicName = topicName.replaceAll(string, "");}return topicName;}/*** 获取标题内容的相似度** @return Double*/public Double getSimilar(SimilarityTwoUtils simHashUtil) {// 获取海明距离Double hammingDistance = (double) this.getHammingDistance(simHashUtil);// 求得海明距离百分比Double scale = (1 - hammingDistance / this.hashCount) * 100;Double formatScale = Double.parseDouble(String.format("%.2f", scale));return formatScale;}/*** 获取标题内容的海明距离** @return Double*/private int getHammingDistance(SimilarityTwoUtils simHashUtil) {// 求差集BigInteger subtract = new BigInteger("1").shiftLeft(this.hashCount).subtract(new BigInteger("1"));// 求异或BigInteger xor = this.bigSimHash.xor(simHashUtil.bigSimHash).and(subtract);int total = 0;while (xor.signum() != 0) {total += 1;xor = xor.and(xor.subtract(new BigInteger("1")));}return total;}}

4.测试类:ArticleSimilarityTest

package com.alex.examples;import cn.hutool.core.io.FileUtil;
import com.alex.examples.utils.SimilarityTwoUtils;public class ArticleSimilarityTest {public static void main(String[] args) {// 简单模拟,此处【库里已存在的文章】可以通过数据库查询后,再做对比String str1 = FileUtil.readString("你当前的文章", "utf-8");String str2 = FileUtil.readString("库里已存在的文章", "utf-8");// 计算相似度SimilarityTwoUtils mySimHash_1 = new SimilarityTwoUtils(str1, 64);SimilarityTwoUtils mySimHash_2 = new SimilarityTwoUtils(str2, 64);Double similar = mySimHash_1.getSimilar(mySimHash_2);System.out.println("两个文件的相似度相似度:" + similar);if (similar >= 95L) { // 这个相似度值的界限,根据公司的要求定义即可System.out.println("相似度过于高!!!");}}
}

4.运行结果:


5.鸽巢原理(对文章出现的鸽巢原理进行讲解):

鸽巢原理也称为抽屉原理,是组合数学中一个重要的原理。

抽屉原理的含义:如果每个抽屉代表一个集合,每一个皮球代表一个元素,假如有N+1个元素放到N个集合中,其实必定有一个集合里至少含有两个元素,如图:

JAVA-计算两篇文章的相似度相关推荐

  1. C++/JAVA 计算两篇文章的相似度

    C++/JAVA 计算两篇文章的相似度 这位少侠,要不要进店瞧瞧? 实验介绍及思路 问题描述: 编写程序,计算任意两篇文章的相似度. 基本思路: 利用余弦相似度来计算其相似度. 完整代码 C++ 代码 ...

  2. 【python 走进NLP】simhash 算法计算两篇文章相似度

    互联网网页存在大量的重复内容网页,无论对于搜索引擎的网页去重和过滤.新闻小说等内容网站的内容反盗版和追踪,还是社交媒体等文本去重和聚类,都需要对网页或者文本进行去重和过滤.最简单的文本相似性计算方法可 ...

  3. python余弦定理_使用余弦定理计算两篇文章的相似性

    使用余弦定理计算两篇文章的相似性:(方法论,细致易懂版) http://blog.csdn.net/dearwind153/article/details/52316151 python 实现(代码) ...

  4. [将小白进行到底] 如何比较两篇文章的相似度

    其实这个题目已经有很多人写过了,数学之美里就有,最近阮一峰的博客里也写了,本文基本上遵循的就是他的思路,只是让其看起来再小白一点点.其实说白了就是用自己的话,再把同样一件事描述一下,顺便扩扩句,把其中 ...

  5. php类似if,php 比较两篇文章的相似度的方法

    昨天说了一下php中的 similar_text() 函数,此函数可以比较两个字符串之间的相似度(以百分比计),但此函数在比较中文字符串时感觉不是那么的准确. 在网上搜索了一些php用户比较两个中文字 ...

  6. 计算两组标签/关键词 相似度算法

    原文连接 http://www.zhaochao.net/index.php/2016/02/05/14/ 写作背景 标签在互联网行业有大量的应用,给博客打标签,给商品打标签,给新闻打标签.通常每篇文 ...

  7. 比较两篇文章的相似性方法

       对于这个题目,开始毫无头绪,后来经过查阅资料现在讲方法总结如下:   1.利用余弦定理    我们知道向量a,b之间的夹角可用余弦定理求得:              如果夹角的余弦值越小,那么 ...

  8. 【NLP学习笔记】文本相似度计算——判断两篇文章是否相似

    一.算法流程 (1)使用TF-IDF算法,提取出两篇文章的关键词: (2)每篇文章各取出若干个关键词(比如20个),合并成一个集合,计算每篇文章对于这个集合中的词的词频(为了避免文章长度的差异,可以使 ...

  9. 利用TF_IDF算法计算两英文文章的文本相似度 C++实现

    利用TF_IDF算法计算两英文文章的文本相似度 C++实现,仅用于应付课程小作业. 链接:利用TF_IDF算法计算两个英文文章的文本相似度(C++实现)-C++文档类资源-CSDN下载

  10. 【读书笔记】NeurIPS2018的两篇文章:The Tradeoffs of Large Scale Learning和Neural Ordinary Differential Equations

    今天看了 NeurIPS 2018 上的两篇文章,一篇是获得 best paper 的 Neural Ordinary Differential Equations (陈天奇的文章),一篇是获经典论文 ...

最新文章

  1. hibernate开启二级缓存
  2. 限流算法之漏桶算法、令牌桶算法
  3. matlab实现矩阵的旋转变换
  4. 电脑上装蓝牙_指甲盖变触控板,隔空就能操控手机和电脑!
  5. mysql-Federated存储方式,远程表,相当于sql server的linked server
  6. leetcode529. 扫雷游戏(dfs)
  7. 中国四季帐篷行业市场供需与战略研究报告
  8. Thinking in Java 14.7 动态代理
  9. 云计算摆摊的可行性分析 | 凌云时刻
  10. Spring Boot Web简介
  11. 电源大师课笔记 2.6
  12. 移动端H5解惑-页面适配
  13. 人脸识别示例代码解析(二)——人脸识别解析
  14. laravel 下载使用
  15. javaWeb——servlet的认识及Tomcat动态部署
  16. 图像有损压缩与无损压缩_有损压缩与无损压缩之间的区别
  17. eCharts01-地图标记散点图
  18. Nimbus线上AMA内容记录-第四期
  19. Android 音乐播放器模块心得卡顿优化
  20. 【2023年Mathorcup杯数学建模竞赛C题】电商物流网络包裹应急调运与结构优化--完整作品分享

热门文章

  1. 富集分析:(一)概述
  2. android 微信浮窗实现_转载:Android悬浮窗的实现
  3. cad画直线长度与实际不符_cad测量直线长度(CAD测量长度与实际画线长度不符)...
  4. 测试9年,面试华为要薪1万,华为员工:公司没这么低工资的岗
  5. 在线教育APP的功能和优势
  6. 【bug解决】上传图片后,取消这次上传 再次执行上传,上次的图片还存在
  7. git lfs mac 安装_GIT LFS 安装及使用
  8. 腾讯微博qq说说备份导出工具_电竞和游戏火了,和它走得很近的腾讯微博却早已透心凉...
  9. 华为USG6320做双线-基于源地址的策略路由
  10. android 键盘快捷键大全,[键盘快捷键使用大全]AndroidStudio 快捷键使用总结大全.doc...