1. 程式人生 > >中文分詞的逆向最大匹配演算法(2016年)

中文分詞的逆向最大匹配演算法(2016年)

逆向最大匹配演算法,中文分詞機械化分詞中最基本的演算法,也是入門級別的演算法。但是,在機械化分詞方面的效果,表現卻很好。尤其是在大文字的時候,一次取較多詞語進行匹配,因為大文字匹配成詞的概率遠遠高於小文字,所以會有很好的表現。下面的程式碼,來自IK分詞的一部分原始碼包,2016年本人進行了逆向最大匹配演算法的改造,閒著沒事幹,算是入門級別的分詞。

package org.wltea.analyzer.core;

import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

import org.wltea.analyzer.cfg.Configuration;
import org.wltea.analyzer.dic.Dictionary;
/**
* 中分分詞上下文環境
* @author TongXueQiang
* @date 2016/01/22
* @since 1.7
*/
class AnalyzeContext {
private char[] segmentBuff;
private int[] charTypes;
private int buffOffset;
private int cursor;
private int available;
private Set<String> buffLocker;
private QuickSortSet orgLexemes;
private Map<Integer, LexemePath> pathMap;
private LinkedList<Lexeme> results;
private Configuration cfg;
private Integer moveIndex;

public AnalyzeContext(Configuration cfg) {
this.cfg = cfg;
this.segmentBuff = new char[4096];
this.charTypes = new int[4096];
this.buffLocker = new HashSet<String>();
this.orgLexemes = new QuickSortSet();
this.pathMap = new HashMap<Integer, LexemePath>();
this.results = new LinkedList<Lexeme>();
}

int getCursor() {
return this.cursor;
}

char[] getSegmentBuff() {
return this.segmentBuff;
}

char getCurrentChar() {
return this.segmentBuff[this.cursor];
}

int getCurrentCharType() {
return this.charTypes[this.cursor];
}

int getBufferOffset() {
return this.buffOffset;
}
/**
* 向緩衝區填充字元
* @param reader
* @return
* @throws IOException
*/
int fillBuffer(Reader reader) throws IOException {
int readCount = 0;
if (this.buffOffset == 0) {
readCount = reader.read(this.segmentBuff);
} else {
int offset = this.available - this.cursor;
if (offset > 0) {
System.arraycopy(this.segmentBuff, this.cursor,
this.segmentBuff, 0, offset);
readCount = offset;
}

readCount += reader.read(this.segmentBuff, offset, -offset);
}

this.available = readCount;
this.cursor = 0;
return readCount;
}

void initCursor() {
this.cursor = this.available-1;
//規範會字元
this.segmentBuff[this.cursor] = CharacterUtil
.regularize(this.segmentBuff[this.cursor]);
//為字元指定型別,比如阿拉伯數字型別,英文字母型別等等
this.charTypes[this.cursor] = CharacterUtil
.identifyCharType(this.segmentBuff[this.cursor]);
}

boolean moveCursor() {
if ((this.cursor-moveIndex) > 0) {
this.cursor -= (moveIndex+1);
//System.out.println("移動指標後的cursor位置:"+cursor);
//移動指標後還要進行規範化當前字元
this.segmentBuff[this.cursor] = CharacterUtil
.regularize(this.segmentBuff[this.cursor]);
//指定當前字元的型別
this.charTypes[this.cursor] = CharacterUtil
.identifyCharType(this.segmentBuff[this.cursor]);
return true;
}
return false;
}

void lockBuffer(String segmenterName) {
this.buffLocker.add(segmenterName);
}

void unlockBuffer(String segmenterName) {
this.buffLocker.remove(segmenterName);
}

boolean isBufferLocked() {
return (this.buffLocker.size() > 0);
}

boolean isBufferConsumed() {
return (this.cursor == this.available - 1);
}

boolean needRefillBuffer() {
return ((this.available == 4096) && (this.cursor < this.available - 1)
&& (this.cursor > this.available - 100) && (!(isBufferLocked())));
}

void markBufferOffset() {
this.buffOffset += this.cursor;
}

void addLexeme(Lexeme lexeme) {
this.orgLexemes.addLexeme(lexeme);
}

void addLexemePath(LexemePath path) {
if (path != null)
this.pathMap.put(Integer.valueOf(path.getPathBegin()), path);
}

QuickSortSet getOrgLexemes() {
return this.orgLexemes;
}
/**
* 輸出結果集
*/
void outputToResult() {
int index = 0;
while (index <= this.cursor) {
LexemePath path = (LexemePath) this.pathMap.get(Integer
.valueOf(index));

if (path != null) {
Lexeme l = path.pollFirst();

if (l != null) {
this.results.add(l);
index = l.getBegin() + l.getLength();
this.cursor = index;
}
} else {
outputSingleCJK(index);
++index;
}

}
this.pathMap.clear();
}

private void outputSingleCJK(int index) {
Lexeme singleCharLexeme;
if (4 == this.charTypes[index]) {
singleCharLexeme = new Lexeme(this.buffOffset, index, 1, 64);
this.results.add(singleCharLexeme);
} else if (8 == this.charTypes[index]) {
singleCharLexeme = new Lexeme(this.buffOffset, index, 1, 8);
this.results.add(singleCharLexeme);
}
}
/**
* 取出詞元,為詞元賦值
* @return
*/
Lexeme getNextLexeme() {
Lexeme result = (Lexeme) this.results.pollFirst();
while (result != null) {
compound(result);//數量詞合併
//過濾掉停用詞
if (Dictionary.getSingleton().isStopWord(this.segmentBuff,
result.getBegin(), result.getLength())) {
//System.out.println(Dictionary.getSingleton().isStopWord(this.segmentBuff,
//result.getBegin(), result.getLength()));
result = (Lexeme) this.results.pollFirst();
} else {
//為Lexeme賦值
result.setLexemeText(String.valueOf(this.segmentBuff,
result.getBegin(), result.getLength()));
break;
}
}
return result;
}

void reset() {
this.buffLocker.clear();
this.orgLexemes = new QuickSortSet();
this.available = 0;
this.buffOffset = 0;
this.charTypes = new int[4096];
this.cursor = 0;
this.results.clear();
this.segmentBuff = new char[4096];
this.pathMap.clear();
}
/**
* 數量詞合併
* @param result
*/
private void compound(Lexeme result) {
if (!(this.cfg.useSmart())) {
return;
}

if (this.results.isEmpty())
return;
Lexeme nextLexeme;
boolean appendOk;
if (2 == result.getLexemeType()) {
nextLexeme = (Lexeme) this.results.peekFirst();
appendOk = false;
if (16 == nextLexeme.getLexemeType()) {
appendOk = result.append(nextLexeme, 16);
} else if (32 == nextLexeme.getLexemeType()) {
appendOk = result.append(nextLexeme, 48);
}
if (appendOk) {
this.results.pollFirst();
}

}

if ((16 == result.getLexemeType()) && (!(this.results.isEmpty()))) {
nextLexeme = (Lexeme) this.results.peekFirst();
appendOk = false;
if (32 == nextLexeme.getLexemeType()) {
appendOk = result.append(nextLexeme, 48);
}
if (!(appendOk))
return;
this.results.pollFirst();
}
}

public void setMoveIndex(Integer moveIndex) {
this.moveIndex = moveIndex;

}

}

以下是CJK逆向最大匹配演算法:

package org.wltea.analyzer.core;

import org.wltea.analyzer.dic.Dictionary;
import org.wltea.analyzer.dic.Hit;

/**
* 中日韓分詞器,逆向最大匹配演算法
*
* @author TongXueQiang
* @date 2016/01/20
* @since 1.7
*/
class CJKSegmenter implements ISegmenter {
static final String SEGMENTER_NAME = "CJK_SEGMENTER";
static Integer MATCH_LEN = 7;
static Integer moveIndex = MATCH_LEN - 1;

CJKSegmenter() {

}

/*
* 逆向最大匹配演算法
*
* @see org.wltea.analyzer.core.ISegmenter#analyze(org.wltea.analyzer.core.
* AnalyzeContext)
*/
public void analyze(AnalyzeContext context) {
if (context.getCursor() < moveIndex) {
moveIndex = context.getCursor();
MATCH_LEN = context.getCursor() + 1;
}
Hit singleCharHit = Dictionary.getSingleton().matchInMainDict(
context.getSegmentBuff(), context.getCursor() - moveIndex,
MATCH_LEN);
if (singleCharHit.isMatch() || MATCH_LEN == 1) {
Lexeme newLexeme = new Lexeme(context.getBufferOffset(),
context.getCursor() - moveIndex, MATCH_LEN, 4);
context.addLexeme(newLexeme);
context.setMoveIndex(moveIndex);
init();
} else {
if (!singleCharHit.isUnmatch() || singleCharHit.isUnmatch()) {
--moveIndex;
--MATCH_LEN;
analyze(context);
}
}

}

private void init() {
moveIndex = 6;
MATCH_LEN = 7;
}

@Override
public void reset() {

}
}

相關推薦

中文逆向匹配演算法(2016)

逆向最大匹配演算法,中文分詞機械化分詞中最基本的演算法,也是入門級別的演算法。但是,在機械化分詞方面的效果,表現卻很好。尤其是在大文字的時候,一次取較多詞語進行匹配,因為大文字匹配成詞的概率遠遠高於小文字,所以會有很好的表現。下面的程式碼,來自IK分詞的一部分原始碼包,201

中文--逆向匹配

res 最長 java 搜索字符串 name ++ san imp 匹配 上一篇文章中介紹了正向最大匹配。能夠看到有時候效果不是非常好。這裏在介紹一種逆向最大匹配的算法。詞典和匹配的字符串都和上一篇文章同樣 僅僅是本算法是從後到前搜索字符串。然後找到最長的

中文——正向匹配

中文分詞應用很廣泛,網上也有很多開源專案。我在這裡主要講一下中文分詞裡面演算法的簡單實現,廢話不多說了,現在先上程式碼 package com; import java.util.ArrayList; import java.util.List; public cl

用正向和逆向匹配演算法進行中文(續)

一、結果分析:         1.程式執行結果,如下圖所示:         2.總體分析。         (1)正向和逆向匹配都正確的句子數目為 1731,佔句子總數的39.0%         (2)正向最大匹配完全正確的句子數目為 1917,佔句子總數的43

逆向匹配演算法之python實現

1.執行環境 python 3.6.4 2.思路 大致思路與正向相同,可參考我的上一篇部落格。 3.程式碼實現 import codecs #獲得分詞字典,儲存為字典形式 f1 = codecs.open('./corpus/WordList.txt', 'r', encodi

中文熵馬爾可夫模型MEMM

Xue & Shen '2003 [2]用兩種序列標註模型——MEMM (Maximum Entropy Markov Model)與CRF (Conditional Random Field)——用於中文分詞;看原論文感覺作者更像用的是MaxEnt (Maximum Entropy) 模型而非MEM

詞法分析-中文技術-正向匹配法與逆向匹配

Long Time No See... 最近深受痛苦的折磨,這一年來所有的事跌宕起伏,如同一瞬,一個個打擊接踵而至,從年初的各種擦邊掛,到各種失敗,各種放棄,似乎沒有發生一個順心的事,不知道從什麼時候起戾氣變得越來越重,更無與人說。不管如何,“盡吾志也而不能至者,可以無悔矣,其孰能譏之乎?”……

中文之正向匹配演算法

中文分詞目前可以分為“規則分詞”,“統計分詞”,“混合分詞(規則+統計)”這三個主要流派。這次介紹下基於規則的分詞,其是一種機械的分詞方法,主要通過維護詞典,在切分語句時,將語句的每個字串與詞表中的詞逐一進行匹配,找到則切分,否則不予切分。 正向最大匹配演算法

雙向匹配演算法——基於詞典規則的中文(Java實現)

目錄 一、中文分詞理論描述 二、演算法描述       1、正向最大匹配演算法       2、反向最大匹配演算法       3、雙劍合璧 三、案例描述 四、JAVA實現完整程式碼 五、組

演算法:正向匹配演算法

正向最大匹配演算法 正向最大匹配演算法(FMM)是一種基於詞典的分詞方法,同樣的基於詞典的方法還有逆向最大匹配法(RMM),ngram法.FMM故名思意,左向右掃描尋找詞的最大匹配,是一種貪心的思想。

演算法記事本#NLP-1】匹配演算法

> **本文地址**: # \#NLP-1 最大匹配演算法(MM) **最大匹配演算法**(Maximum Matching)被用於對一個**文段**進行**詞語劃分**(Word Segmentation)。 > ## 注意 > 這是**詞元化(Tokenization)演算法** > 此方法*

基於詞典的正向匹配演算法優先匹配

public Set<String> matchChinese(String text, Set<String> dictionary, int maxLength) { //text:待匹配文字 dictiona:詞典

正向匹配演算法實現之python實現

1.python 版本:python 3.6.4 2.思路: s1.匯入分詞詞典,儲存為字典形式dic,匯入停用詞詞典stop_words,儲存為字典形式,需要分詞的文字檔案cutTest.txt,儲存為字串chars s2.遍歷分詞詞典,找出最長的詞,長度為max_chars s3

深度解析中文演算法正向/逆向匹配

中文分詞演算法概述:  1:非基於詞典的分詞(nlp語義領域)     相當於人工智慧領域計算。一般用於機器學習,特定領域等方法,這種在特定領域的分詞可以讓計算機在現有的規則模型中, 推理如何分詞。在某個領域(垂直領域)分詞精度較高。但是實現比較複雜。 2:基於詞典的分

中文演算法正向匹配演算法(Python版)

最大匹配演算法是自然語言處理中的中文匹配演算法中最基礎的演算法,分為正向和逆向,原理都是一樣的。 正向最大匹配演算法,故名思意,從左向右掃描尋找詞的最大匹配。 首先我們可以規定一個詞的最大長度,每次掃描的時候尋找當前開始的這個長度的詞來和字典中的詞匹配,如果沒有找到,就縮短

一個簡單正向匹配(Maximum Matching)MM中文演算法的實現

1.構建詞典記憶體樹的TrieNode節點類:       package cn.wzb.segmenter.mm.bean; import java.util.HashMap; /** * 構建記憶體詞典的Trie樹結點 * */ public cla

中文--正向匹配算法python實現

命中 col odin app () 切分 -- \n 多個 最大匹配法:最大匹配是指以詞典為依據,取詞典中最長單詞為第一個次取字數量的掃描串,在詞典中進行掃描(為提升掃描效率,還可以跟據字數多少設計多個字典,然後根據字數分別從不同字典中進行掃描)。例如:詞典中最長詞為“中

中文實現——雙向匹配

關於中文分詞的一些基本介紹,可以看這篇部落格《中文分詞方法總結》。這裡就不再進行詳細介紹了。 雙向最大匹配方法 雙向最大匹配方法是一種基於詞典的分詞方法。基於詞典的分詞方法是按照一定策略將待分析的漢字串與一個“大機器詞典”中的詞條進行匹配,若在詞典中找到某個字串,則匹配成功

HMM匹配演算法(Python)

正向最大匹配演算法是我國最早提出的解決中文分詞問題的演算法,因其簡單易操作,至今仍作為機器分詞的粗分演算法,在今天看來,這種演算法的準確率遠不夠高,無法達到令人滿意的要求。這只是一次練習。 待切分

自己動手寫引擎——逆向、正向、雙向演算法的實現

分詞引擎已經是NLP最成熟的部分了,經歷了:字典分詞,統計分詞等發展之後,除了應付學術中才有的各種變態歧義句之外,目前工業界的分詞引擎對於絕大多數的常見問題已經足以應對,頂多是需要不斷優化新詞字典就可以了。 但不管怎麼樣,對於一個NLPer還是要能夠手寫最簡單的分詞演算法的