NLP 开源形近字算法之相似字列表(番外篇)
创作目的
国内对于文本的相似度计算,开源的工具是比较丰富的。
但是对于两个汉字之间的相似度计算,国内基本一片空白。国内的参考的资料少的可怜,国外相关文档也是如此。
本项目旨在抛砖引玉,实现一个基本的相似度计算工具,为汉字 NLP 贡献一点绵薄之力。
推荐阅读:
开源项目在线化 中文繁简体转换/敏感词/拼音/分词/汉字相似度/markdown 目录

需求
有时候我们并不是需要返回两个字的相似,而是需要返回一个汉字的相似列表。
实现思路
我们可以分别计算所有的汉字之间的相似度,然后保留最大的前100个,放在字典中。
然后实时查询这个字典即可。
实现方式
bihuashu_2w.txt 中我们主要需要的是对应的 2W 常见汉字。
hanzi_similar_list.txt 用来存放汉字和相似字的映射关系。
数据初始化
public static void main(String[] args) {
final String path = "D:\\code\\coin\\nlp-hanzi-similar\\src\\main\\resources\\hanzi_similar_list.txt";
// 读取列表
List<String> lines = FileUtil.readAllLines("D:\\code\\coin\\nlp-hanzi-similar\\src\\main\\resources\\nlp\\bihuashu_2w.txt");
// 所有的单词
Set<String> allWordSet = new HashSet<>();
for(String line : lines) {
String word = line.split(" ")[0];
allWordSet.add(word);
}
// 循环对比
for(String word : allWordSet) {
List<String> list = getSimilarListData(word, allWordSet);
String line = word +" " + StringUtil.join(list, "");
FileUtil.append(path, line);
}
}
- 优先级队列取前 100 个
我们通过优先级队列存储:
private static List<String> getSimilarListData(String word, Set<String> wordSet) {
PriorityQueue<SimilarListDataItem> items = new PriorityQueue<>(new Comparator<SimilarListDataItem>() {
@Override
public int compare(SimilarListDataItem o1, SimilarListDataItem o2) {
// 相似度大的放在前面
return -o1.getRate().compareTo(o2.getRate());
}
});
for(String other : wordSet) {
if(word.equals(other)) {
continue;
}
// 对比
double rate = HanziSimilarHelper.similar(word.charAt(0), other.charAt(0));
SimilarListDataItem item = new SimilarListDataItem(other, rate);
items.add(item);
}
final int limit = 100;
List<String> wordList = new ArrayList<>();
for(SimilarListDataItem item : items) {
wordList.add(item.getWord());
if(wordList.size() >= limit) {
break;
}
}
return wordList;
}
相似字的获取
初始化好数据之后,一切就变得非常简单:
- 接口定义
/**
* 数据接口-相似列表
* @author binbin.hou
* @since 1.3.0
*/
public interface IHanziSimilarListData {
/**
* 返回数据信息
* @param word 单词
* @return 结果
* @since 1.3.0
*/
List<String> similarList(String word);
}
- 数据获取
public class HanziSimilarListData implements IHanziSimilarListData {
private static volatile Map<String, List<String>> map = Guavas.newHashMap();
@Override
public List<String> similarList(String word) {
if(MapUtil.isEmpty(map)) {
initDataMap();
}
return map.get(word);
}
private void initDataMap() {
if(MapUtil.isNotEmpty(map)) {
return;
}
//DLC
synchronized (map) {
if(MapUtil.isEmpty(map)) {
List<String> lines = StreamUtil.readAllLines("/hanzi_similar_list.txt");
for(String line : lines) {
String[] words = line.split(" ");
// 后面的100个相近词
List<String> list = StringUtil.toCharStringList(words[1]);
map.put(words[0], list);
}
}
}
}
}
便利性
为了用户使用方便,我们在 HanziSimilarHelper 中添加 2 个工具类方法:
/**
* 相似的列表
* @param hanziOne 汉字一
* @param limit 大小
* @return 结果
* @since 1.3.0
*/
public static List<String> similarList(char hanziOne, int limit) {
return HanziSimilarBs.newInstance().similarList(hanziOne, limit);
}
/**
* 相似的列表
* @param hanziOne 汉字一
* @return 结果
* @since 1.3.0
*/
public static List<String> similarList(char hanziOne) {
return similarList(hanziOne, 10);
}
测试效果
我们使用看一下效果:
我们来看一下【爱】的形近字。
List<String> list = HanziSimilarHelper.similarList('爱');
Assert.assertEquals("[爰, 爯, 受, 爭, 妥, 憂, 李, 爳, 叐, 雙]", list.toString());
开源地址
为了便于大家使用学习,项目已开源。
小结
一个字的形近字可以做很多有趣的事情,这个要看大家的想象力。
实现方式也不难,最核心的还是相似度的计算。
我是老马,期待与你的下次重逢。
NLP 开源形近字算法之相似字列表(番外篇)的更多相关文章
- NLP 开源形近字算法补完计划(完结篇)
前言 所有的故事都有开始,也终将结束. 本文将作为 NLP 汉字相似度的完结篇,为该系列画上一个句号. 起-NLP 中文形近字相似度计算思路 承-中文形近字相似度算法实现,为汉字 NLP 尽一点绵薄之 ...
- KMP算法番外篇--求解next数组
KMP算法实现字符串的模式匹配的时间复杂度比朴素的模式匹配好很多,但是它时间效率的提高是有前提的,那就是:模式串的重复率很高,不然它的效率也不会凸显出来.在实际的应用中,KMP算法不算是使用率很高的一 ...
- 数据结构和算法 – 番外篇.时间测试类Timing
public class Timing { //startingTime--用来存储正在测试的代码的开始时间. TimeSpan startingTime; //duration--用来存储正在测试的 ...
- CWMP开源代码研究番外篇——博通方案
声明:本篇文章来自于某公司Cable Modem产品的文档资料,源码来自于博通公司,只提供参考(为保护产权,本人没有源码). 前文曾提到会写一篇关于博通的tr069,那么福利来了.福利,福利,福利,重 ...
- 痞子衡嵌入式:超级下载算法(RT-UFL)开发笔记番外(1) - JLinkScript妙用
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是超级下载算法开发笔记番外篇之JLinkScript妙用. JLinkScript 文件是配套 J-Link 调试器使用的脚本,这个脚本适 ...
- Java实现 蓝桥杯 算法提高 周期字串
算法提高 周期字串 时间限制:1.0s 内存限制:256.0MB 问题描述 右右喜欢听故事,但是右右的妈妈总是讲一些"从前有座山,山里有座庙,庙里有个老和尚给小和尚讲故事,讲的什么呢?从前有 ...
- zz【清华NLP】图神经网络GNN论文分门别类,16大应用200+篇论文最新推荐
[清华NLP]图神经网络GNN论文分门别类,16大应用200+篇论文最新推荐 图神经网络研究成为当前深度学习领域的热点.最近,清华大学NLP课题组Jie Zhou, Ganqu Cui, Zhengy ...
- Duang的成长——使用造字程序输入生僻字
使用造字程序输入生僻字 最近,一个字突然间火了起来,那就是——duang! (图片来自网络) 那么,问题来了!造字程序哪家强?(此处有掌声) 其实,微软早就考虑到各国文字的博大精深,在系统中集成了一个 ...
- 【开源.NET】轻量级内容管理框架Grissom.CMS(第三篇解析配置文件和数据以转换成 sql)
该篇是 Grissom.CMS 框架系列文章的第三篇, 主要介绍框架用到的核心库 EasyJsonToSql, 把标准的配置文件和数据结构解析成可执行的 sql. 该框架能实现自动化增删改查得益于 E ...
- 顶级开源项目 Sentry 20.x JS-SDK 设计艺术(Unified API篇)
SDK 开发 顶级开源项目 Sentry 20.x JS-SDK 设计艺术(理念与设计原则篇) 顶级开源项目 Sentry 20.x JS-SDK 设计艺术(开发基础篇) 顶级开源项目 Sentry ...
随机推荐
- 【LeetCode - 1055】形成字符串的最短路径
1.题目描述 代码: #include <iostream> #include <string> using namespace std; const int MAX_LETT ...
- vue父子件,子件页面table数据列按条件显示不同的内容
需求:在父件中点击按钮.子件弹框中,table列根据条件显示不同的数据 实现思路:点击按钮,执行不同的方法,方法中参数值不同,从而展示不同的columns. 父件按钮如下图: 父件中导入子件需要注意的 ...
- Docker基本命令之 容器运行命令参数
容器命令行参数介绍: docker run:顾名思义就是运行一个Docker 以下是具体参数介绍: -a: stdin:指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项 - ...
- 项目实训 DAY 10
今天,我写了一些前端代码规范,并按规范修改了一下代码.规范写到了README.md上
- Office 2016 未授权
用于管理 Office 批量激活的工具 https://docs.microsoft.com/zh-cn/DeployOffice/vlactivation/tools-to-manage-volum ...
- uc 小游戏接入经验
使用引擎,egret 相关资料: https://blog.csdn.net/weixin_42276579/article/details/107379544 https://minigame.uc ...
- mybatis的xml中#{}和${}区别
#{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入.初步编译后的sql语句是" ...
- CentOS7 进入修复模式的办法
有时候配置Centos文件修改错误会导致系统无法登录,可以通过修复模式进行单用户运行模式,进行修复. 具体操作如下: 1.重启服务器,在选择内核界面使用上下箭头移动 2.选择内核并按"e&q ...
- MATLAB默认路径修改
笔者曾尝试在软件界面的"设置路径"或者Parallel中修改默认路径,但多次尝试均失败.后来经人提点,MATLAB默认文件夹路径可以在桌面图标属性中"起始位置" ...
- ubuntu16.04+win10双系统安装
先下载Ubuntu到电脑:http://mirrors.ustc.edu.cn/ubuntu-releases/16.04.6/ubuntu-16.04.6-desktop-amd64.iso 准备个 ...