首页 > 编程语言 >Java调用Apache commons-text求解字符串相似性

Java调用Apache commons-text求解字符串相似性

时间:2024-09-18 12:22:14浏览次数:12  
标签:Java String int text s1 commons gram s2 字符串

前言

       在之前的一篇漂亮国的全球的基地博客中,我们曾经对漂亮国的全球基地进行了一些梳理。博文中使用的数据来源,重点是参考以为博主分享的KML的数据,同时针对其国内的基地部署信息,我们从互联网百科的数据中搜寻到一些。其实拿到这两份数据的时候,是存在一些问题的,比如,KML的数据它的基地名称都是英文的,而在互联网上公开的数据中,良莠不齐,有的是中文的,有的是英文,有的中英文都有,而且可能还存在同样的英文,它的描述不一样的情况,比如下面的场景:




XXX Air Base 空军基地


XXX Air Station 航空站


       可能不同的信息来源,其主要的名称对应的真实位置表示的是同一个。再比如漂亮国驻小日子的基地列表。




Idesuna Jima Air Range


FAC 6078 出砂岛炸射练习场 Idesuna Jima Range 训练 冲绳渡名喜村


       上面一行是从KML中解析出来的基地名称,而下面是从某百科中得到的数据。 当我们将句子的影响词(当出现在军事基地的领域中的某些特定词,如Air、Base、Station、Range等这些词去掉后)其主要的地点就是同一个,通过对比主要关键词的相似性,为我们实现将两个地名进行对齐,甚至可以设定一个阈值,当两个字符串的相似性超过多少(比如超过90%)就认为这是同一个地点,从而可以实现将基地的中文名和所驻地的国家、城市等信息进行一一匹配。从而减少人工对比的工作。


       随着现代技术的快速发展,文本相似度计算有很多种方法,甚至可以用大数据、自然语言处理还可以结合人工智能的方法来实现精确的文本相似性判断。但是本文不想实现那么复杂的架构,只想简单快速的解决两个地址的快速匹配问题。博客以Java编程为例,讲解了在Java中求解两个字符串的几种方法。通过求解编辑距离、Q-gram Matching、还有余弦相似性计算,通过对比不同的方法,调用Apache 的Common-text中基于余弦的字符相似性得到了比较比错的结果。算法比较简单,主要想说明两个字符串的不同距离的计算,对于更复杂的模型,大家感兴趣的可以搜索相关的科研论文来学习。


一、字符串距离的几种计算方法

       为了比较字符的相似性,我们通过通过对比距离(把两个字符串的距离计算出来,当然这个距离的计算是有讲究的,具体的算法就是我们说的距离算法,不同的距离算法,得到的值是不一样的)。因此这里我们打算讲将在Java当中,有哪些距离的计算方法以及如何编写相应的代码。


1、Levenshtein 距离

       计算将一个字符串转换为另一个字符串所需的最少单字符编辑操作(插入、删除或替换)的数量。在下面这个示例中,levenshteinDistance方法使用动态规划来计算两个字符串之间的Levenshtein Distance。calculateNormalizedLevenshteinDistance方法则使用这个距离和两个字符串长度的最大值来计算标准化的Levenshtein Distance。 main方法中给出了两个示例字符串s1和s2,并调用calculateNormalizedLevenshteinDistance方法来计算并打印它们的Normalized Levenshtein Distance。根据这个得分,你可以设定一个阈值来判断两个字符串是否相似。例如,如果Normalized Levenshtein Distance小于0.2(即80%的字符是匹配的),你可以认为这两个字符串是相似的。这个阈值可以根据你的具体需求进行调整。


package com.yelang.project;

public class NormalizedLevenshteinDistance {

// 计算Levenshtein Distance

   public static int levenshteinDistance(String s1, String s2) {

       int[][] dp = new int[s1.length() + 1][s2.length() + 1];

       for (int i = 0; i <= s1.length(); i++) {

           dp[i][0] = i;

       }

       for (int j = 0; j <= s2.length(); j++) {

           dp[0][j] = j;

       }

       for (int i = 1; i <= s1.length(); i++) {

           for (int j = 1; j <= s2.length(); j++) {

               int cost = (s1.charAt(i - 1) == s2.charAt(j - 1)) ? 0 : 1;

               dp[i][j] = Math.min(Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + cost);

           }

       }

       return dp[s1.length()][s2.length()];

   }

   // 计算Normalized Levenshtein Distance

   public static double calculateNormalizedLevenshteinDistance(String s1, String s2) {

       int distance = levenshteinDistance(s1, s2);

       int maxLength = Math.max(s1.length(), s2.length());

       return (double) distance / maxLength;

   }

   public static void main(String[] args) {

       String s1 = "Camp Humphreys";

       String s2 = "United States Army Garrison-Humphreys";

       double normalizedDistance = calculateNormalizedLevenshteinDistance(s1, s2);

       System.out.println("Normalized Levenshtein Distance: " + normalizedDistance);

   }

}


       运行上述程序后,得到的结果如下,表示两个字符串的相似度是0.67:


Normalized Levenshtein Distance: 0.6756756756756757

2、Overlap Coefficient计算

       Overlap Coefficient是通过计算两个字符串的最长公共子序列的长度来求两者的相似度的。这种算法有弊有利,在主要的关键词不同而基地的信息完一致的情况下,比如xxx air base 和xx1 air base,通过计算得到的结果一定及其相似,因为这两个字符串的最长公共序列很长啊,计算机看起来就像是完全一致的。


package com.yelang.project;

public class OverlapCoefficient {

// 计算最长公共子序列的长度

   public static int longestCommonSubsequence(String s1, String s2) {

       int len1 = s1.length();

       int len2 = s2.length();

       int[][] dp = new int[len1 + 1][len2 + 1];

       for (int i = 1; i <= len1; i++) {

           for (int j = 1; j <= len2; j++) {

               if (s1.charAt(i - 1) == s2.charAt(j - 1)) {

                   dp[i][j] = dp[i - 1][j - 1] + 1;

               } else {

                   dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);

               }

           }

       }

       return dp[len1][len2];

   }

   // 计算Overlap Coefficient

   public static double calculateOverlapCoefficient(String s1, String s2) {

       int lcsLength = longestCommonSubsequence(s1, s2);

       int minLength = Math.min(s1.length(), s2.length());

       return (double) lcsLength / minLength;

   }

   public static void main(String[] args) {

       String s1 = "Camp Humphreys";

       String s2 = "United States Army Garrison-Humphreys";

       double overlapCoefficient = calculateOverlapCoefficient(s1, s2);

       System.out.println("Overlap Coefficient: " + overlapCoefficient);

   }

}


       运行以上程序后,得到的结果是:


Overlap Coefficient: 0.8571428571428571

       它判定是85%的相似度,貌似看起来比第一种距离很接近真相,你换个词去测试就发现,这种算法其实很蠢。


3、Q-gram Matching

       Q-gram Matching是一种基于子串的字符串相似度计算方法,其中Q表示子串的长度。对于两个字符串,计算它们共有的Q-gram(长度为Q的连续子串)的数量,然后将这个数量除以两个字符串中Q-gram数量较少的那个, 得到相似度的比例。下面给出它的Java实现:


package com.yelang.project;

import java.util.HashSet;

import java.util.Set;

public class QGramMatching {

/**

 * 在这个示例中,calculateQGrams方法接受一个字符串和一个整数Q,然后返回该字符串所有可能的Q-gram的集合。calculateQGramSimilarity方法接受两个字符串和一个整数Q,计算它们的Q-gram集合,并找出共有的Q-gram数量,然后计算相似度。main方法中给出了两个示例字符串s1和s2,以及Q-gram的长度Q,并调用calculateQGramSimilarity方法来计算并打印它们的Q-gram相似度。

请注意,Q-gram的长度Q是一个参数,可以根据具体应用场景进行调整。较短的Q值可以捕捉到更细粒度的相似性,而较长的Q值则可以捕捉到更宽泛的相似性。此外,相似度的阈值可以根据实际需求设定,以判断两个字符串是否相似。

 */

// 计算字符串的Q-gram集合

   public static Set<String> calculateQGrams(String s, int q) {

       Set<String> qGrams = new HashSet<>();

       for (int i = 0; i <= s.length() - q; i++) {

           qGrams.add(s.substring(i, i + q));

       }

       return qGrams;

   }

   // 计算两个字符串的Q-gram相似度

   public static double calculateQGramSimilarity(String s1, String s2, int q) {

       Set<String> qGrams1 = calculateQGrams(s1, q);

       Set<String> qGrams2 = calculateQGrams(s2, q);

       // 计算两个集合的交集

       Set<String> intersection = new HashSet<>(qGrams1);

       intersection.retainAll(qGrams2);

       // 计算相似度

       double similarity = intersection.size() / (double) Math.min(qGrams1.size(), qGrams2.size());

       return similarity;

   }

   public static void main(String[] args) {

       String s1 = "Camp Humphreys";

       String s2 = "United States Army Garrison-Humphreys";

       // Misawa Air Range<==>Misawa Air Base:similar = 0.9058216273156766

       // Gun Training Area<==>Tsuken Jima Training Area:similar = 0.9202830114233174

       int q = 3; // Q-gram的长度

       double qGramSimilarity = calculateQGramSimilarity(s1, s2, q);

       System.out.println("Q-gram Similarity (Q=" + q + "): " + qGramSimilarity);

   }

}


       运行以上的程序,得到以下的执行结果:


Q-gram Similarity (Q=3): 0.5833333333333334

4、余弦相似性计算

       使用词嵌入(如word2vec或BERT)将单词转换为向量,并计算向量之间的余弦相似度。这里基于Apache 的common-text来进行余弦计算,看结果如何,首先在Pom.xml中引入以下资源。


<!-- 求解字符相似性 -->

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-text -->

<dependency>

<groupId>org.apache.commons</groupId>

<artifactId>commons-text</artifactId>

<version>1.10.0</version>

</dependency>

       然后在Java中实现以下方法,请注意,这里是一种比较简单的方法,更精准的实现还要结合实际场景进行优化。


标签:Java,String,int,text,s1,commons,gram,s2,字符串
From: https://blog.51cto.com/u_16271212/12045006

相关文章

  • 【编程底层原理】Java执行CAS后底层由谁执行cmpxchg指令?CPU?是否会导致从用户态切换
    Java中的CAS操作是由Java虚拟机(JVM)提供的原子类实现的,这些原子类利用了底层硬件的CAS指令,比如x86架构中的cmpxchg指令。以下是这个过程的一些关键点:原子类封装:Java的java.util.concurrent.atomic包提供了一系列的原子类,如AtomicInteger、AtomicLong等,它们封装了CAS操作,使得......
  • Java客户端SpringDataRedis(RedisTemplate使用)
    文章目录⛄概述⛄快速入门❄️❄️导入依赖❄️❄️配置文件❄️❄️测试代码⛄数据化序列器⛄StringRedisTemplate⛄RedisTemplate的两种序列化实践方案总结⛄概述SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,......
  • java获取object中的value
    在Java中,获取对象(Object)中的值通常取决于对象的类型以及它的结构。Java是一种面向对象的编程语言,对象可以包含不同类型的数据,包括基本数据类型(如int,double等)的包装类、其他对象以及数组等。下面列出了一些常见的方法来获取对象中的值:1.直接访问(针对基本数据类型和包装类)如果你的......
  • 大学生网页制作期末作业——html+css+javascript+jquery旅游官网6页 html大学生网站开
    ......
  • 基于Java的面向社区的智能化健康管理系统设计与实现
    文章目录1.前言2.详细视频演示3.论文参考4.项目运行截图5.技术框架5.1后端采用SpringBoot框架5.2前端框架Vue6.可行性分析7.系统测试7.1系统测试的目的7.2系统功能测试8.数据库表设计9.代码参考10.数据库脚本11.作者推荐项目12.为什么选择我?13.获取源......
  • 【Java】若依(ruoyi)——7.代码生成(二)细节操作
    之前我们已经学习了代码生成的基础使用:https://www.cnblogs.com/luyj00436/p/18398248。即创建数据库并根据三种数据结构生成代码。1.基本信息和生成信息 前缀可以在配置表设置默认配置。单应用在resources目录下的application.yml,多模块ruoyi-generator中的resources目录下......
  • 【Java】若依框架(RuoYi-master)——8.文件上传
     若依框架的自带上传和下载功能,但需要我们进行恰当的操作(具体也可以参考示例和源码)。 新建表格新建一张学生信息表(这里的字段、文件路径、文件名称与改说明相关):DROPTABLEIFEXISTS`sys_student`;CREATETABLE`sys_student`(`student_id`intNOTNULLAUTO_INCRE......
  • 基于JAVA客户关系管理系统的计算机毕设源码+论文
    摘要【摘要】随着市场体系的日益完善,我们已经进入了一个买方市场时代。选择空间扩大、购买渠道拓宽、消费心态和购买行为越发成熟,使客户再不会对某一企业或者产品盲目地保持绝对忠诚,如何不断改善客户关系,掌据更多的客户信息,提高客户满意度;如何在日益激烈的市场竞争中把握机会,占有更......
  • 在CentOS系统中如何配置Java开发环境环境变量的手把手操作流程
    以下是一份关于“在CentOS系统中如何配置Java开发环境环境变量”的手把手操作流程,由浅入深,先整体后分部:一、引言Java是一种广泛使用的编程语言,其跨平台特性使得它在不同操作系统上都能运行。为了在CentOS系统中进行Java开发,我们需要配置Java开发环境。本文将详细介绍如何......
  • 在Windows系统中如何配置Java开发环境环境变量&零基础手把手教程
    以下是一份关于“在Windows系统中如何配置Java开发环境环境变量”的手把手操作流程,由浅入深,先整体后分部:一、引言Java是一种广泛使用的编程语言,其跨平台特性使得它在不同操作系统上都能运行。为了在Windows系统中进行Java开发,我们需要配置Java开发环境。本文将详细介绍如......