import com.jeeplus.solr.MySolr;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.collections4.SetUtils;
import java.util.*;
import java.util.stream.Collectors;
public class TextSimilarity {
/**分词的maven 感觉没啥用
* <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
* <dependency>
* <groupId>org.apache.commons</groupId>
* <artifactId>commons-collections4</artifactId>
* <version>4.4</version>
* </dependency>
* @param text1
* @param text2
* @return
*/
public static double jaccardSimilarity(String text1, String text2) {
List<String> words1 = tokenize(text1);
List<String> words2 = tokenize(text2);
List<String> union = new ArrayList<>(words1);
CollectionUtils.addAll(union, words2);
List<String> intersection = ListUtils.intersection(words1, words2);
return (double) intersection.size() / union.size();
}
private static List<String> tokenize(String text) {
// 您可以根据需要替换为您喜欢的分词器,例如:结巴分词等
// String[] tokens = text.toLowerCase().split("\\s+");
// return new ArrayList<>(List.of(tokens));
//利用solr进行分词
MySolr mySolr=new MySolr();
mySolr.init("jiushipin");
List<String> results = mySolr.getAnalysis(text);
return results;
}
//Jaccard 相似度 集合的交集与集合的并集的比例.
public static float jaccard(String a, String b) {
if (a == null && b == null) {
return 1f;
}
// 都为空相似度为 1
if (a == null || b == null) {
return 0f;
}
Set<Integer> aChar = a.chars().boxed().collect(Collectors.toSet());
Set<Integer> bChar = b.chars().boxed().collect(Collectors.toSet());
// 交集数量
int intersection = SetUtils.intersection(aChar, bChar).size();
if (intersection == 0) return 0;
// 并集数量
int union = SetUtils.union(aChar, bChar).size();
return ((float) intersection) / (float)union;
}
//Sorensen Dice 相似度系数 集合交集的 2 倍除以两个集合相加。并不是并集.
public static float SorensenDice(String a, String b) {
if (a == null && b == null) {
return 1f;
}
if (a == null || b == null) {
return 0F;
}
Set<Integer> aChars = a.chars().boxed().collect(Collectors.toSet());
Set<Integer> bChars = b.chars().boxed().collect(Collectors.toSet());
// 求交集数量
int intersect = SetUtils.intersection(aChars, bChars).size();
if (intersect == 0) {
return 0F;
}
// 全集,两个集合直接加起来
int aSize = aChars.size();
int bSize = bChars.size();
return (2 * (float) intersect) / ((float) (aSize + bSize));
}
//编辑距离表示字符串相似度, 编辑距离越小,字符串越相似
public static float Levenshtein(String a, String b) {
if (a == null && b == null) {
return 1f;
}
if (a == null || b == null) {
return 0F;
}
int editDistance = editDis(a, b);
return 1 - ((float) editDistance / Math.max(a.length(), b.length()));
}
private static int editDis(String a, String b) {
int aLen = a.length();
int bLen = b.length();
if (aLen == 0) return aLen;
if (bLen == 0) return bLen;
int[][] v = new int[aLen + 1][bLen + 1];
for (int i = 0; i <= aLen; ++i) {
for (int j = 0; j <= bLen; ++j) {
if (i == 0) {
v[i][j] = j;
} else if (j == 0) {
v[i][j] = i;
} else if (a.charAt(i - 1) == b.charAt(j - 1)) {
v[i][j] = v[i - 1][j - 1];
} else {
v[i][j] = 1 + Math.min(v[i - 1][j - 1], Math.min(v[i][j - 1], v[i - 1][j]));
}
}
}
return v[aLen][bLen];
}
//汉明距离
//汉明距离是编辑距离中的一个特殊情况,仅用来计算两个等长字符串中不一致的字符个数。
//因此汉明距离不用考虑添加及删除,只需要对比不同即可,所以实现比较简单。
//我们可以用similarity=汉明距离/长度来表示两个字符串的相似度。
public static float hamming(String a, String b) {
if (a == null || b == null) {
return 0f;
}
if (a.length() != b.length()) {
return 0f;
}
int disCount = 0;
for (int i = 0; i < a.length(); i++) {
if (a.charAt(i) != b.charAt(i)) {
disCount++;
}
}
return (float) disCount / (float) a.length();
}
//将字符串向量化,之后就可以在一个平面空间中,求出他们向量之间夹角的余弦值
public static float cos(String a, String b) {
if (a == null || b == null) {
return 0F;
}
Set<Integer> aChar = a.chars().boxed().collect(Collectors.toSet());
Set<Integer> bChar = b.chars().boxed().collect(Collectors.toSet());
// 统计字频
Map<Integer, Integer> aMap = new HashMap<>();
Map<Integer, Integer> bMap = new HashMap<>();
for (Integer a1 : aChar) {
aMap.put(a1, aMap.getOrDefault(a1, 0) + 1);
}
for (Integer b1 : bChar) {
bMap.put(b1, bMap.getOrDefault(b1, 0) + 1);
}
// 向量化
Set<Integer> union = SetUtils.union(aChar, bChar);
int[] aVec = new int[union.size()];
int[] bVec = new int[union.size()];
List<Integer> collect = new ArrayList<>(union);
for (int i = 0; i < collect.size(); i++) {
aVec[i] = aMap.getOrDefault(collect.get(i), 0);
bVec[i] = bMap.getOrDefault(collect.get(i), 0);
}
// 分别计算三个参数
int p1 = 0;
for (int i = 0; i < aVec.length; i++) {
p1 += (aVec[i] * bVec[i]);
}
float p2 = 0f;
for (int i : aVec) {
p2 += (i * i);
}
p2 = (float) Math.sqrt(p2);
float p3 = 0f;
for (int i : bVec) {
p3 += (i * i);
}
p3 = (float) Math.sqrt(p3);
return ((float) p1) / (p2 * p3);
}
public static void main(String[] args) {
String text1 = "五粮液好不好?";
String text2 = "今天喝五粮液";
//0.40
float jaccard1 = cos(text1, text2);//0.77151674
// float jaccard2 = hamming(text1, text2);//0.0
// float jaccard3 = Levenshtein(text1, text2);//0.375
//0.4
float jaccard4 = SorensenDice(text1, text2);//0.7692308
// float jaccard5 = jaccard(text1, text2);//0.625
// double similarity = jaccardSimilarity(text1, text2); //0.2
System.out.println("文本相似度1: " + jaccard1);
// System.out.println("文本相似度2: " + jaccard2);
// System.out.println("文本相似度3: " + jaccard3);
System.out.println("文本相似度4: " + jaccard4);
// System.out.println("文本相似度5: " + jaccard5);
// System.out.println("文本相似度similarity: " + similarity);
}
}
java计算文本相似度
最新推荐文章于 2025-05-21 16:55:00 发布