java计算文本相似度与关键词

java计算文本相似度与关键词

物料准备:

1.ansj_seg和hanlp的依赖

2.定义工具类,用来计算两段文本的相似度,以及从文本中提取关键词(摘要)

3.配置ansj_seg框架需要的dic词典

pom.xml引入ansj_seg和hanlp的依赖

 <dependency>
            <groupId>org.ansjgroupId>
            <artifactId>ansj_segartifactId>
            <version>5.1.6version>
        dependency>
        <dependency>
            <groupId>com.hankcsgroupId>
            <artifactId>hanlpartifactId>
            <version>portable-1.6.8version>
        dependency>

定义TextStrUtil.java类

import cn.hutool.core.date.StopWatch;
import com.hankcs.hanlp.seg.common.Term;
import com.hankcs.hanlp.tokenizer.StandardTokenizer;
import org.ansj.app.keyword.Keyword;
import org.ansj.app.summary.SummaryComputer;
import org.ansj.app.summary.pojo.Summary;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TextStrUtil {

    /**
     * 单个分词 求hash
     *
     * @param termStr String 单个分词
     * @return BigInteger  hash值
     */
    private static BigInteger computeTermStrHash(String termStr) {
        if (termStr == null || termStr.length() == 0) {
            return new BigInteger("0");
        } else {
            //当 termStr 的长度过短,会导致hash算法失效,因此需要对过短的词补偿
            while (termStr.length() < 3) {
                termStr = termStr + termStr.charAt(0);
            }
            char[] sourceArray = termStr.toCharArray();
            BigInteger x = BigInteger.valueOf(((long) sourceArray[0]) << 7);
            BigInteger m = new BigInteger("1000003");
            BigInteger mask = new BigInteger("2").pow(64).subtract(
                    new BigInteger("1"));
            for (char item : sourceArray) {
                BigInteger temp = BigInteger.valueOf((long) item);
                x = x.multiply(m).xor(temp).and(mask);
            }
            x = x.xor(new BigInteger(String.valueOf(termStr.length())));
            if (x.equals(new BigInteger("-1"))) {
                x = new BigInteger("-2");
            }
            return x;
        }
    }

    /**
     * 长字符串求hash
     *
     * @param str String  长字符串
     * @return BigInteger  hash值
     */
    private static BigInteger computeStrHash(String str) {
        int[] v = new int[64]; //哈希位数,设置64
        // 利用hanlp 对字符串进行分词
        List<Term> termList = StandardTokenizer.segment(str);

        // 对分词的一些特殊处理 : 比如: 根据词性添加权重 , 过滤掉标点符号 , 过滤超频词汇等;
        Map<String, Integer> weightOfNature = new HashMap<String, Integer>(); // 词性的权重
        weightOfNature.put("n", 2); // 给名词的权重是2;
        weightOfNature.put("nr", 5); // 给人名的权重是5;
        weightOfNature.put("ns", 5); // 给地名的权重是5;

        // 停用的词性
        Map<String, String> stopNatures = new HashMap<String, String>();
        // 如一些标点符号之类的;
        stopNatures.put("w", "");

        // 设定超频词汇的界限 ;
        int overCount = 5;
        Map<String, Integer> wordCount = new HashMap<>();

        for (Term term : termList) {
            // 分词字符串
            String word = term.word;
            // 分词属性;
            String nature = term.nature.toString();
            // 过滤超频词
            if (wordCount.containsKey(word)) {
                int count = wordCount.get(word);
                if (count > overCount) {
                    continue;
                }
                wordCount.put(word, count + 1);
            } else {
                wordCount.put(word, 1);
            }

            // 过滤停用词性
            if (stopNatures.containsKey(nature)) {
                continue;
            }

            // 将每一个分词hash为一组固定长度的数列.比如 64bit 的一个整数.
            BigInteger t = computeTermStrHash(word);
            for (int i = 0; i < 64; i++) {
                BigInteger bitmask = new BigInteger("1").shiftLeft(i);
                //    建立一个长度为64的整数数组(假设要生成64位的数字指纹,也可以是其它数字),
                // 对每一个分词hash后的数列进行判断,如果是1000...1,那么数组的第一位和末尾一位加1,
                // 中间的62位减一,也就是说,逢1加1,逢0减1.一直到把所有的分词hash数列全部判断完毕.
                int weight = 1; // 添加权重
                if (weightOfNature.containsKey(nature)) {
                    weight = weightOfNature.get(nature);
                }
                if (t.and(bitmask).signum() != 0) {
                    // 这里是计算整个文档的所有特征的向量和
                    v[i] += weight;
                } else {
                    v[i] -= weight;
                }
            }
        }

        BigInteger fingerprint = new BigInteger("0");
        for (int i = 0; i < 64; i++) {
            if (v[i] >= 0) {
                fingerprint = fingerprint.add(new BigInteger("1").shiftLeft(i));
            }
        }
        return fingerprint;

    }


    /**
     * 计算海明距离,海明距离越小说明字符串内容越相似
     *
     * @param str1Hash 字符串1的hash值
     * @param str2Hash 字符串2的hash值
     * @return 海明距离
     */
    private static int computeHammingDistance(BigInteger str1Hash, BigInteger str2Hash) {
        BigInteger m = new BigInteger("1")
                .shiftLeft(64) //hash位数
                .subtract(new BigInteger("1"));
        //xor运算
        BigInteger x = str1Hash.xor(str2Hash).and(m);
        int tot = 0;
        while (x.signum() != 0) {
            tot += 1;
            x = x.and(x.subtract(new BigInteger("1")));
        }
        return tot;
    }

    /**
     * 求2个长文本字符串的内容相似度百分比
     *
     * @param s1 String
     * @param s2 String
     * @return double 文本相似度百分比
     */
    public static double getSemblance(String s1, String s2) {
        StopWatch sw1 = StopWatch.create("c1");
        sw1.start();

        BigInteger str1Hash = computeStrHash(s1);
        System.out.println("字符串1的hash值" + str1Hash);

        BigInteger str2Hash = computeStrHash(s2);
        System.out.println("字符串2的hash值" + str2Hash);

        sw1.stop();
        System.out.println("字符串hash计算耗时" + sw1.getTotalTimeMillis());

        double i = (double) computeHammingDistance(str1Hash, str2Hash);
        return 1 - i / 64;
    }

    /**
     * 利用ansj框架 提取文本里的关键词 或 摘要
     *
     * @param strContent String
     * @return String
     */
    public static String getKeyWords(String strContent) {
        String strReturn = "";
        //限制关键词长度300词
        SummaryComputer summaryComputer = new SummaryComputer(300, "",
                strContent);
        Summary summary = summaryComputer.toSummary();
        //提取出的关键词列表
        List<Keyword> strKeylist = summary.getKeyWords();
        for (int i = 0; i < strKeylist.size(); ++i) {
            if (i == 0) {
                strReturn = String.format("%s%s", strReturn, strKeylist.get(i).getName());
            } else {
                strReturn = String.format("%s、%s", strReturn, strKeylist.get(i).getName());
            }
        }
        return strReturn;
    }


    public static void main(String[] args) {
        StopWatch stopWatch = StopWatch.create("t1");
        stopWatch.start();
        String s1 = "腾讯首款回合MMO手游大作《梦幻诛仙(微博)》超多内容强势爆料,在近期成为了游戏圈的一大热点。现又有消息称《梦幻诛仙》手游即将公开自己的游戏主题曲,谁将代表这款经典之作,唱响诛仙情缘,也成了玩家间热议的话题。从小说到电视剧,从端游再到如今的手游,诛仙热还将继续,谁将成为众望所归的演唱者呢?手Q兴趣部落更多组图谁将献唱梦幻诛仙 玩家猜想不二人选《梦幻诛仙》手游作为一款基于诛仙小说以及同名端游改编的移动游戏,2D回合制MMORPG玩法也很符合许多玩家的胃口。而近期《梦幻诛仙》宣布即将公开自己的游戏主题曲,谁将受邀演唱手游主题曲也吊住了玩家们的胃口。诛仙仙气十足的故事内核,搭配上古风古韵的音乐主题,女歌手演唱主题曲似乎是一个很好的选择,而演唱近期诛仙IP电视剧青云志片尾曲《时光笔墨》的张碧晨,就成了许多玩家心中的不二人选。张碧晨的气质与仙侠风契合,并多次受邀演唱电视剧古风曲目,且凭借好声音夺冠在内地乐坛人气颇高,如果张碧晨能携手《梦幻诛仙》发声,相信也会是场不错的视听体验。音乐才子是否助力 男歌手选择有点说完了女歌手,再说说男歌手。如果《梦幻诛仙》想邀请一个男歌手进行这次的主题曲创作,玩家们心中的答案又会是谁呢?回顾近些年热门IP音乐产品的创作史,大陆创作奇才汪苏泷可谓是炙手可热。汪苏泷可算是大热影视剧OST的承包户,《花千骨》、《微微一笑很倾城》等热门电视剧的音乐创作均有他的身影,而他创作的《年轮》更是登顶QQ音乐7周,由他创作演唱的《微微一笑很倾城》插曲《下一秒》近期还登陆了各大榜单前三,也让他再度进入了大家的视野。结合之前预测的女歌手张碧晨,两者的契合度也非常高,合作推出的两首歌曲均有不俗的成绩,如果能再度合作为《梦幻诛仙》发声,相信也会带来不小的惊喜。更多候选引发热议 手游发声悬念重重除了上述说到的张碧晨和汪苏泷,曾为《梦幻诛仙》端游版本演唱过歌曲的胡歌,亦或近期诛仙电视剧中的男主角新晋小生李易峰,还有曾为诛仙游戏演唱过单曲的任贤齐,也纷纷在玩家们的猜想名单之列。诛仙这个有多年情怀底蕴的热门IP,凭借电视剧在近期掀起的诛仙热潮,也让《梦幻诛仙》手游的一举一动牵动着玩家们的心。无论最终会是谁为《梦幻诛仙》手游演唱主题曲,相信都会让诛仙情缘再度绽放,让玩家们的青春跨越时空,在全新的游戏世界中随着音乐苏醒。“下一回合,遇见不凡”。《梦幻诛仙》手游移植移动平台,在传承经典玩法的同时,游戏主题曲的公开倒计时,也为玩家们留足了悬念,究竟最终谁将唱响诛仙情缘,敬请玩家们拭目以待。获取梦幻诛仙最新资讯动态,请登录梦诛官网:http://mhzx.qq.com ,或扫描下方二维码,即刻关注《梦幻诛仙》手游微信公众号和手Q兴趣部落。";
        String s2 = "【重磅新闻】腾讯首款回合MMO手游大作《梦幻诛仙(微博)》超多内容强势爆料,在近期成为了游戏圈的一大热点。现又有消息称《梦幻诛仙》手游即将公开自己的游戏主题曲,谁将代表这款经典之作,唱响诛仙情缘,也成了玩家间热议的话题。从小说到电视剧,从端游再到如今的手游,诛仙热还将继续,谁将成为众望所归的演唱者呢?手Q兴趣部落更多组图谁将献唱梦幻诛仙 玩家猜想不二人选《梦幻诛仙》手游作为一款基于诛仙小说以及同名端游改编的移动游戏,2D回合制MMORPG玩法也很符合许多玩家的胃口。而近期《梦幻诛仙》宣布即将公开自己的游戏主题曲,谁将受邀演唱手游主题曲也吊住了玩家们的胃口。诛仙仙气十足的故事内核,搭配上古风古韵的音乐主题,女歌手演唱主题曲似乎是一个很好的选择,而演唱近期诛仙IP电视剧青云志片尾曲《时光笔墨》的张碧晨,就成了许多玩家心中的不二人选。张碧晨的气质与仙侠风契合,并多次受邀演唱电视剧古风曲目,且凭借好声音夺冠在内地乐坛人气颇高,如果张碧晨能携手《梦幻诛仙》发声,相信也会是场不错的视听体验。音乐才子是否助力 男歌手选择有点说完了女歌手,再说说男歌手。如果《梦幻诛仙》想邀请一个男歌手进行这次的主题曲创作,玩家们心中的答案又会是谁呢?回顾近些年热门IP音乐产品的创作史,大陆创作奇才汪苏泷可谓是炙手可热。汪苏泷可算是大热影视剧OST的承包户,《花千骨》、《微微一笑很倾城》等热门电视剧的音乐创作均有他的身影,而他创作的《年轮》更是登顶QQ音乐7周,由他创作演唱的《微微一笑很倾城》插曲《下一秒》近期还登陆了各大榜单前三,也让他再度进入了大家的视野。结合之前预测的女歌手张碧晨,两者的契合度也非常高,合作推出的两首歌曲均有不俗的成绩,如果能再度合作为《梦幻诛仙》发声,相信也会带来不小的惊喜。更多候选引发热议 手游发声悬念重重除了上述说到的张碧晨和汪苏泷,曾为《梦幻诛仙》端游版本演唱过歌曲的胡歌,亦或近期诛仙电视剧中的男主角新晋小生李易峰,还有曾为诛仙游戏演唱过单曲的任贤齐,也纷纷在玩家们的猜想名单之列。诛仙这个有多年情怀底蕴的热门IP,凭借电视剧在近期掀起的诛仙热潮,也让《梦幻诛仙》手游的一举一动牵动着玩家们的心。无论最终会是谁为《梦幻诛仙》手游演唱主题曲,相信都会让诛仙情缘再度绽放,让玩家们的青春跨越时空,在全新的游戏世界中随着音乐苏醒。“下一回合,遇见不凡”。《梦幻诛仙》手游移植移动平台,在传承经典玩法的同时,游戏主题曲的公开倒计时,也为玩家们留足了悬念,究竟最终谁将唱响诛仙情缘,敬请玩家们拭目以待。获取梦幻诛仙最新资讯动态,请登录梦诛官网:http://mhzx.qq.com ,或扫描下方二维码,即刻关注《梦幻诛仙》手游微信公众号和手Q兴趣部落。";

        System.out.println("两段文本的相似度:" + getSemblance(s1, s2));
        stopWatch.stop();
        System.out.println("相似度计算程序总耗时:" + stopWatch.getTotalTimeMillis());


        StopWatch sw3 = StopWatch.create("t2");
        sw3.start();
        System.out.println("提取文本s1里的关键词:" + getKeyWords(s1));

        System.out.println("提取文本s2里的关键词:" + getKeyWords(s2));
        sw3.stop();
        System.out.println("关键词提取程序耗时:" + sw3.getTotalTimeMillis());
    }
}

配置ansj_seg框架需要的dic词典

在maven项目根目录下的library目录里
配置
ambiguity.dic
default.dic
regex.dic
stop.dic
synonyms.dic

java计算文本相似度与关键词_第1张图片

测试执行结果

字符串1的hash值14366090094003174383
字符串2的hash值14366090094003172335
字符串hash计算耗时1295
两段文本的相似度:0.984375
相似度计算程序总耗时:1297
19:02:38.955 [main] WARN org.ansj.util.MyStaticValue - not find library.properties in classpath use it by default !
19:02:38.963 [main] INFO org.ansj.dic.impl.File2Stream - path to stream library/ambiguity.dic
19:02:38.964 [main] DEBUG org.ansj.library.AmbiguityLibrary - begin init ambiguity
19:02:38.969 [main] INFO org.ansj.library.AmbiguityLibrary - load dic use time:5 path is : library/ambiguity.dic
19:02:38.970 [main] DEBUG org.ansj.library.DicLibrary - begin init dic !
19:02:38.970 [main] INFO org.ansj.dic.impl.File2Stream - path to stream library/default.dic
19:02:39.808 [main] INFO org.ansj.library.DicLibrary - load dic use time:838 path is : library/default.dic
19:02:39.811 [main] DEBUG org.ansj.library.CrfLibrary - begin init crf model!
19:02:41.883 [main] INFO org.ansj.app.crf.Model - load crf model ok ! use time :2065
19:02:41.884 [main] INFO org.ansj.library.CrfLibrary - load crf use time:2073 path is : jar://crf.model
19:02:42.766 [main] INFO org.ansj.library.DATDictionary - init core library ok use time : 790
19:02:43.166 [main] INFO org.ansj.library.NgramLibrary - init ngram ok use time :394
提取文本s1里的关键词:诛仙、手游、梦幻、主题曲、张碧晨、端游、游戏、演唱、男歌手、热议
提取文本s2里的关键词:诛仙、手游、梦幻、主题曲、张碧晨、端游、游戏、演唱、男歌手、热议
关键词提取程序耗时:8290

你可能感兴趣的:(JavaSE笔记,java,hanlp,ansj_seg,文本相似度,文本摘要提取,文本分词)