几种找出字符串相似度的计算方法。
有以下几种常用的工具包:
- Apache Commons Text:这是一个提供各种文本操作的工具包,其中有一个类叫做SimilarityScore,可以用来计算两个字符串之间的相似度,支持多种算法,如Levenshtein Distance、Jaro Winkler Distance、Cosine Distance等。你可以在这里查看它的文档和示例代码。
- SimMetrics:这是一个专门用于计算字符串相似度的工具包,也支持多种算法,如Levenshtein Distance、Jaccard Similarity、Euclidean Distance等。你可以在这里查看它的源码和使用方法。
- FuzzyWuzzy:这是一个基于Python的字符串相似度计算工具包,但是也有一个Java的实现版本,叫做java-fuzzywuzzy。它主要使用了Levenshtein Distance算法,但是也提供了一些其他的功能,如字符串匹配、分词等。可以在这里查看它的源码和使用方法。1
一、Apache Commons Text中的方法
- JaccardSimilarity:这种算法是基于两个字符串的交集和并集来计算相似度的,公式是
J(A,B) = |A∩B| / |A∪B|
,其中|A∩B|
表示两个字符串共有的字符数,|A∪B|
表示两个字符串总共的字符数。这种算法的优点是简单直观,缺点是对于不同位置的相同字符不敏感,比如"abc"和"cba"的相似度就是0。 - CosineSimilarity:这种算法是基于两个字符串的向量空间模型来计算相似度的,公式是
cos(A,B) = (A·B) / (||A||·||B||)
,其中(A·B)
表示两个字符串向量的点积,(||A||·||B||)
表示两个字符串向量的模长乘积。这种算法的优点是对于不同位置的相同字符敏感,缺点是需要将字符串转换为向量表示,比较复杂。 - LevenshteinDistance:这种算法是基于两个字符串之间的最少编辑操作次数来计算相似度的,操作包括替换、插入、删除等。编辑距离越小,说明两个字符串越相似。这种算法的优点是对于不同位置的相同字符敏感,缺点是计算复杂度较高,需要用到动态规划的思想。2
1. JaccardSimilarity 杰卡德
package com.wsj.utils;
import java.text.DecimalFormat;
import java.util.HashSet;
import java.util.Set;
public class JaccardSimilarity {
public static double calculateJaccardSimilarity(String strA, String strB) {
// 将字符串转换为字符集合
// System.out.println("原字符串:"+strA);
Set<Character> setA = new HashSet<>();
for (char c : strA.toCharArray()) {
setA.add(c);
// System.out.println("拆分的字符串:" + c);
}
Set<Character> setB = new HashSet<>();
for (char c : strB.toCharArray()) {
setB.add(c);
}
// 计算交集和并集的大小
Set<Character> intersection = new HashSet<>(setA);
intersection.retainAll(setB);
Set<Character> union = new HashSet<>(setA);
union.addAll(setB);
// 计算Jaccard相似度
double result = (double) intersection.size() / union.size();
// 格式化返回值为8位小数点
DecimalFormat decimalFormat = new DecimalFormat("#.########");
String formattedResult = decimalFormat.format(result);
return Double.parseDouble(formattedResult);
}
}
2. LevenshteinDistance 编辑距离算法 1 2
如果你想要使用Apache Commons Text来实现编辑距离算法,你可以使用它提供的一些相似度类,例如LevenshteinDistance,JaroWinklerDistance,HammingDistance等。这些类都实现了EditDistance接口,可以用来计算两个字符串之间的编辑距离。例如,你可以这样写:
import org.apache.commons.text.similarity.EditDistance;
import org.apache.commons.text.similarity.LevenshteinDistance;
public class Example {
public static void main(String[] args) {
// 创建一个LevenshteinDistance对象
EditDistance<Integer> editDistance = new LevenshteinDistance();
// 定义两个字符串
String s1 = "apple";
String s2 = "banana";
// 计算两个字符串之间的编辑距离
int distance = editDistance.apply(s1, s2);
// 打印结果
System.out.println(distance); // 5
}
}
如果你想要计算两个list的相似度,你可以对两个list中的每个字符串调用apply方法,得到一个编辑距离分数。然后,你可以对所有的分数求平均值,得到一个0到1之间的浮点数,表示两个list的整体相似度。例如,你可以这样写: 1
2
3
import org.apache.commons.text.similarity.EditDistance;
import org.apache.commons.text.similarity.LevenshteinDistance;
public class Example {
public static void main(String[] args) {
// 创建一个LevenshteinDistance对象
EditDistance<Integer> editDistance = new LevenshteinDistance();
// 创建两个list
List<String> list1 = Arrays.asList("apple", "banana", "orange");
List<String> list2 = Arrays.asList("pear", "mango", "lemon");
// 定义一个变量,用于存放总的编辑距离分数
double totalDistance = 0.0;
// 遍历两个list中的每个字符串
for (String s1 : list1) {
for (String s2 : list2) {
// 计算两个字符串之间的编辑距离
int distance = editDistance.apply(s1, s2);
// 累加到总的编辑距离分数
totalDistance += distance;
}
}
// 计算两个list的平均编辑距离分数
double averageDistance = totalDistance / (list1.size() * list2.size());
// 计算两个list的相似度分数,这里使用1 - distance / max_length的公式
double similarity = 1 - averageDistance / Math.max(list1.get(0).length(), list2.get(0).length());
// 打印结果
System.out.println(similarity); // 0.1667
}
}
二、Simmetrics余弦相似度
如果你想要通过余弦相似度比较两个字符串list的相似度,你可以使用SimMetrics提供的余弦相似度对象,然后对两个list中的每个字符串调用compare方法,得到一个相似度分数。然后,你可以对所有的分数求平均值,得到一个0到1之间的浮点数,表示两个list的整体相似度。例如,你可以这样写: 1
2
import org.simmetrics.metrics.StringMetrics;
import org.simmetrics.StringMetric;
public class Example {
public static void main(String[] args) {
// 创建两个list
List<String> list1 = Arrays.asList("apple", "banana", "orange");
List<String> list2 = Arrays.asList("pear", "mango", "lemon");
// 创建一个余弦相似度对象
StringMetric metric = StringMetrics.cosineSimilarity();
// 定义一个变量,用于存放总的相似度分数
double totalSimilarity = 0.0;
// 遍历两个list中的每个字符串
for (String s1 : list1) {
for (String s2 : list2) {
// 计算两个字符串的相似度
float similarity = metric.compare(s1, s2);
// 累加到总的相似度分数
totalSimilarity += similarity;
}
}
// 计算两个list的平均相似度分数
double averageSimilarity = totalSimilarity / (list1.size() * list2.size());
// 打印结果
System.out.println(averageSimilarity); // 0.0833
}
}
余弦相似度与编辑距离算法的区别
余弦相似度和编辑距离是两种不同的相似度度量方法,它们有各自的优缺点,适用于不同的场景。它们的主要区别是: 1
2
- 余弦相似度是基于向量空间模型的,它衡量的是两个向量之间的夹角,反映的是两个向量的方向上的相似度,而不考虑它们的长度或者大小。余弦相似度的取值范围是-1到1,其中1表示完全相同,0表示正交,-1表示完全相反。
- 编辑距离是基于字符串模型的,它衡量的是将一个字符串转换为另一个字符串所需的最少编辑操作次数,反映的是两个字符串的形式上的相似度,而不考虑它们的语义或者含义。编辑距离的取值范围是0到无穷大,其中0表示完全相同,越大表示越不相似。
一般来说,如果你想要比较两个文本或者语言数据的语义或者主题上的相似度,你可以使用余弦相似度,因为它可以捕捉到词语之间的关联性和共现性。但是你需要先将文本或者语言数据转换为向量表示,例如使用词袋模型,TF-IDF,词嵌入等方法。 3
如果你想要比较两个文本或者语言数据的拼写或者结构上的相似度,你可以使用编辑距离,因为它可以捕捉到词语之间的差异和变化。但是你需要注意编辑距离对于文本或者语言数据的长度和顺序很敏感,而且它不能反映出词语之间的语义或者含义。
总之,余弦相似度和编辑距离都有各自的优缺点,没有一个绝对好或者坏的方法。你需要根据你的数据特征和目标来选择合适的方法。