java计算文本相似度

本文介绍了如何使用Java实现几种常见的文本相似度算法,如Jaccard系数、SorensenDice系数、Levenshtein距离、汉明距离和Cosine相似度,以及利用ApacheCommonsCollections库进行分词和Solr进行全文本分析。示例代码展示了如何计算两个字符串的相似度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一名落魄的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值