java版本结巴分词算法bug[通俗易懂]

java版本结巴分词算法bug[通俗易懂]Nevertolate。所以明天再做也不会晚结巴分词的过程是:1、根据dict.txt中的词库构建一棵trie树,这棵树的实例只有一个,采取单例模式。2、每来一次分词构造,就顺着trie树进行分词,这将产生很多种结果,于是就生成了一个DGA,分词的有向无环图,终点是句子的左边或者右边(实际上应该分别以左边和右边为终点来做处理)。3、利用动态规划,从句子的终点开始,到这算回去(这个在动态…

大家好,又见面了,我是你们的朋友全栈君。

Never to late。所以明天再做也不会晚

结巴分词的过程是:
1、根据dict.txt中的词库构建一棵trie树,这棵树的实例只有一个,采取单例模式。
2、每来一次分词构造,就顺着trie树进行分词,这将产生很多种结果,于是就生成了一个DGA,分词的有向无环图,终点是句子的左边或者右边(实际上应该分别以左边和右边为终点来做处理)。
3、利用动态规划,从句子的终点开始,到这算回去(这个在动态规划中很常见,概率dp):对DGA中查找最大的概率的分词路径,路径上的词语就是分词结果。
4、返回分词结果。

bug1:在实现单例模式的时候,作者用的如下方法

public class WordDictionary{ 
   
	private static WordDictionary singleton;
	public static WordDictionary getInstance() { 
   
        if (singleton == null) { 
   
            synchronized (WordDictionary.class) { 
   
                if (singleton == null) { 
   
                    singleton = new WordDictionary();
                    return singleton;
                }
            }
        }
        return singleton;
    }
}

这种双重锁的方式,在并发场景下,是不安全的,为了避免java编译器对代码进行重排序,应该改为如下形式

private static volatile WordDictionary singleton;
public static WordDictionary getInstance() { 
   
   if (singleton == null) { 
   
        synchronized (WordDictionary.class) { 
   
            if (singleton == null) { 
   
                singleton = new WordDictionary();
                return singleton;
            }
        }
    }
    return singleton;
}

bug2:使用trie树对待分词句子建立DGA的时候采取递归建树,使得大量DictSegment和DictSegment[]堆积,对内存消耗特别严重。使用visual vm进行测试可以发现,将该分词加入到项目中一段时间后,在内存中可以看见DictSegment和DictSegment[]的占比非常高,如果老年代不够大,很有可能会引起OutOfMemory的异常

 Hit match(char[] charArray, int begin, int length, Hit searchHit) { 
   

        if (searchHit == null) { 
   
            // 如果hit为空,新建
            searchHit = new Hit();
            // 设置hit的起始文本位置
            searchHit.setBegin(begin);
        } else { 
   
            // 否则要将HIT状态重置
            searchHit.setUnmatch();
        }
        // 设置hit的当前处理位置
        searchHit.setEnd(begin);
        //设置起始字符为当前字典树的根节点
        Character   keyChar = new Character(charArray[begin]);
        //该keyChar对应的DictSegment
        DictSegment ds      = null;

        // 引用实例变量为本地变量,避免查询时遇到更新的同步问题
        DictSegment[]               segmentArray = this.childrenArray;
        Map<Character, DictSegment> segmentMap   = this.childrenMap;

        // STEP1 在节点中查找keyChar对应的DictSegment
        if (segmentArray != null) { 
   
            // 在数组中查找
            DictSegment keySegment = new DictSegment(keyChar);
            int         position   = Arrays.binarySearch(segmentArray, 0, this.storeSize, keySegment);
            if (position >= 0) { 
   
                ds = segmentArray[position];
            }

        } else if (segmentMap != null) { 
   
            // 在map中查找
            ds = (DictSegment) segmentMap.get(keyChar);
        }

        // STEP2 找到DictSegment,判断词的匹配状态,是否继续递归,还是返回结果
        if (ds != null) { 
   
            if (length > 1) { 
   
                // 词未匹配完,继续往下搜索
                return ds.match(charArray, begin + 1, length - 1, searchHit);
            } else if (length == 1) { 
   

                // 搜索最后一个char
                if (ds.nodeState == 1) { 
   
                    // 添加HIT状态为完全匹配
                    searchHit.setMatch();
                }
                if (ds.hasNextNode()) { 
   
                    // 添加HIT状态为前缀匹配
                    searchHit.setPrefix();
                    // 记录当前位置的DictSegment
                    searchHit.setMatchedDictSegment(ds);
                }
                return searchHit;
            }

        }
        // STEP3 没有找到DictSegment, 将HIT设置为不匹配
        return searchHit;
    }
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/149054.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 八数码问题-A*(AStar)算法实现[通俗易懂]

    八数码问题-A*(AStar)算法实现[通俗易懂]八数码问题可以说得上是搜索问题中比较经典的,可以有很多种搜索策略,比如说有最常见的BFS,DFS,此外,A*也是一个比较普遍的搜索算法。在八数码问题A*往往可以得到最优的求解路径。

    2022年7月13日
    13
  • 跳频介绍_跳频功能

    跳频介绍_跳频功能跳频是最常用的扩频方式之一,其工作原理是指收发双方传输信号的载波频率按照预定规律进行离散变化的通信方式,也就是说,通信中使用的载波频率受伪随机变化码的控制而随机跳变。从通信技术的实现方式来说,“跳频”是一种用码序列进行多频频移键控的通信方式,也是一种码控载频跳变的通信系统。从时域上来看,跳频信号是一个多频率的频移键控信号;从频域上来看,跳频信号的频谱是一个在很宽频带上以不等间隔随机跳变的。其中:跳

    2025年8月12日
    1
  • OAuth2.0 原理流程及其单点登录和权限控制

    点击上方“全栈程序员社区”,星标公众号 重磅干货,第一时间送达 作者:王克锋 kefeng.wang/2018/04/06/oauth2-sso 单点登录是多域名企业站点流行的登录…

    2021年6月28日
    92
  • MIUI刷Android原生,小米手机怎么刷安卓原生系统

    MIUI刷Android原生,小米手机怎么刷安卓原生系统小米手机自带的系统很好,但是很多人还是希望用安卓的原生系统,小米手机是支持刷机成为安卓原生系统的。那么小米手机怎么刷安卓原生系统呢?今天学习啦小编与大家分享下小米手机刷安卓原生系统的具体操作步骤,有需要的朋友不妨了解下。小米手机刷安卓原生系统方法下载工具包和线刷完整包,下载完成后解压,刷机工具点击下载MIUI最新版线刷Fastboot开发版完整包点击下载。(请检查文件后缀名是否为”.tgz”,如…

    2022年6月19日
    120
  • 关于WinExec和System的比较

    关于WinExec和System的比较http://nt04.blog.163.com/blog/static/3297804920116246622829/WinExec是一个WIN32API,它的第一个参数必须包含一个可执行文件名,SYSTEM是C库函数,它接受一个DOS命令。你可以这样:WinExec(“command/CdirC:\>dir.txt”,SW_HIDE);system

    2022年7月27日
    7
  • c语言findwindow函数_findwindow函数

    c语言findwindow函数_findwindow函数转自:http://blog.csdn.net/coolszy/article/details/5523486[DllImport(“User32.dll”,EntryPoint=”FindWindow”)]句柄,是整个Windows编程的基础。一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象和同类中的不同的指针,程序不能利用句柄来…

    2022年8月13日
    15

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号