需求
**文本推荐:**有多个文本字符串,如何设计一个简单的统计方法(从词频的角度设计),来计算出多个文本字符串两两之间的相似度,并输出大于指定相似度阈值的文本
分析理解
使用Java实现文本相似度计算的一种方法是通过构建词频向量并计算余弦相似度,具体介绍如下,简单易懂
代码实现
复杂粘贴可以直接运行
<!-- 使用HanLP进行分词 -->
<dependency>
<groupId>com.hankcs</groupId>
<artifactId>hanlp</artifactId>
<version>portable-1.8.4</version>
</dependency>
import com.hankcs.hanlp.tokenizer.StandardTokenizer;
import java.util.*;
import java.util.stream.Collectors;
public class ChineseTextRecommender {
// 使用HanLP进行中文分词
// 构建词频向量
// 假设我们有两个文本文档,我们想衡量它们的主题相似性。每个文档可以被表示为一个向量,其中包含词频(TF)或TF-IDF值。
// 文档A: "the cat sat on the mat on the mat"
// 文档B: "the cat and the dog played"
// 我们选择几个关键词:"the", "cat", "sat", "on", "mat", "and", "dog", "played"。每个词在文档中出现的次数(词频)可以构成一个向量。
// 向量A: [2, 1, 1, 1, 2, 0, 0, 0]("the", "cat", "sat", "on", "mat", "and", "dog", "played")
// 向量B: [1, 1, 0, 0, 0, 1, 1, 1]
public static Map<String, Integer> buildTermVector(String text) {
List<String> words = StandardTokenizer.segment(text).stream()
.map(term -> term.word)
.collect(Collectors.toList());
Map<String, Integer> termVector = new HashMap<>();
for (String word : words) {
termVector.put(word, termVector.getOrDefault(word, 0) + 1);
}
return termVector;
}
// 计算余弦相似度
public static double cosineSimilarity(Map<String, Integer> vectorA, Map<String, Integer> vectorB) {
double dotProduct = 0.0;
double normA = 0.0;
double normB = 0.0;
for (String key : vectorA.keySet()) {
dotProduct += vectorA.get(key) * (vectorB.getOrDefault(key, 0));
normA += Math.pow(vectorA.get(key), 2);
}
for (String key : vectorB.keySet()) {
normB += Math.pow(vectorB.get(key), 2);
}
if (normA == 0 || normB == 0) {
return 0.0;
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
// 推荐与指定文本相似度高的文本 texts为待判断文本列表
public static List<String> recommendTexts(List<String> texts, String targetText, double threshold) {
Map<String, Double> similarityScores = new HashMap<>();
Map<String, Integer> targetVector = buildTermVector(targetText);
for (String text : texts) {
Map<String, Integer> textVector = buildTermVector(text);
double similarity = cosineSimilarity(targetVector, textVector);
similarityScores.put(text, similarity);
System.out.println(text + " ----Similarity: " + similarity);
}
return similarityScores.entrySet().stream()
.filter(entry -> entry.getValue() >= threshold)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
public static void main(String[] args) {
// 相似度分别为0.91 0.59 0.54 0.799 0.791
List<String> texts = Arrays.asList("这是一个测试文档吗", "这是第二个文档", "这是第三个文档","这是一个文档吗","这是第一个测试文档吧哈哈");
String targetText = "这是一个测试文档";
double threshold = 0.8; // 理论上,阈值在0.5左右都可以接受
List<String> recommendedTexts = recommendTexts(texts, targetText, threshold);
System.out.println("推荐文本:");
recommendedTexts.forEach(System.out::println);
}
}