分词学习(1)--正向最大匹配分词
来源:互联网 发布:anaconda linux的安装 编辑:程序博客网 时间:2024/05/01 17:49
汉字分词最简单的就是正向最大匹配分词了,其基本原理很简单,而且经常作为笔试题
该算法主要分两个步骤:
1 一般从一个字符串的开始位置,选择一个最大长度的词长的片段,如果序列不足最大词长,则选择全部序列。
2 首先看该片段是否在词典中,如果是,则算为一个分出来的词,如果不是,则从右边开始,减少一个字符,然后看短一点的这个片段是否在词典中,依次循环,逐到只剩下一个字。
3 序列变为第2步骤截取分词后,剩下的部分序列。
然后就开始写代码了,这里使用英文进行分词,如“itisatest”,需要分为“it is a test”,词典文件为count_1w.txt,数据来源于beautiful data(数据之美)这本书,附件中有。
之所以经常会面试写这个,主要是这个是个典型的应用分治思路的题目,只要分清楚了,代码其实非常简单易懂,如果几个步骤混在一起考虑,就非常容易糊涂。
对于这样稍微有点逻辑算法的题目,一般是思路就是case方法,就是用一个例子,看下算法怎么跑,然后再分析终止条件和一些特殊情况。当然,标准的设计初始条件,找出循环不变式的方法也是可以的,但是太麻烦了。。。。。。。。。。,一般都是先把代码写出来了,再来证明代码的正确性@#¥#@¥
画个简单的例子图,这样写就容易的多,人的思维很难同时思考很多情况很多步骤,case方法是最直接的:
这里就是注意,黑点的位置就是字母的索引下标,就是唯一字母的前一个空位,这样理解更清楚一些。特别是做分词,黑点就是所有可能的分割位置。
有了这个case,就是分别写两个步骤的处理函数,然后主函数设置初始条件,并循环,调用两个函数,并判断终止条件即可。
具体代码如下(python):
#!/usr/bin/env python#coding=utf-8##############################################################function: 最大正向分词##############################################################import sys import math#global parameterDELIMITER = " " #分词之后的分隔符class MLSegment: def __init__(self): self.word_dict = {} #记录单词 self.gmax_word_length = 0 #单词的最大长度 #功能:从sequence中选取一个片段, # 如果sequence长度大于最大词长,则从头取最大词长的片段 # 否则,则取整个sequence作为片段 #输入:sequence 需要处理的序列 #返回:(选择的片段,剩下的片段) def get_segment(self, sequence): if len(sequence) > self.gmax_word_length: return (sequence[0:self.gmax_word_length], \ sequence[self.gmax_word_length:]) else: return (sequence,"") #功能:从sequence选择一个最长的词 # 基本方法,先取整个sequence,看是否是词,如果不是,依次从右边 # 减少字母,看缩短的片段是否是词,如果减少到只剩下一个字母,则直接 # 作为一个词 #返回:(词,剩下的片段) def select_max_length_word(self, sequence): for length in range(len(sequence),1,-1): word = sequence[0:length] if self.word_dict.has_key(word): return (word, sequence[length:] ) #至少分出一个字母 return (sequence[0:1],sequence[1:]) #最大长度分词 def ml_seg(self, sequence): sequence = sequence.strip() #初始化 segment_start_pos = 0 leave_segment = sequence word_list = [] #开始循环 while True: #step 1,获取一段片段,用来找词 (select_segment, leave_segment) = self.get_segment(leave_segment) #step 2, 从选择的片段中获得可能的最长的词,以及剩余的部分片段 (word,word_right) = self.select_max_length_word(select_segment) word_list.append(word) #step 3,将剩余的词片段与剩下的片段组合,重新进入下一轮循环 leave_segment = word_right + leave_segment #如果剩下的部分为空,退出循环 if leave_segment == "": break return DELIMITER.join(word_list) #加载词典,为词\t词频的格式 def initial_dict(self,filename): dict_file = open(filename, "r") for line in dict_file: sequence = line.strip() key = sequence.split('\t')[0] self.word_dict[key] = 1 #获取最大词长 self.gmax_word_length = max(len(key) for key in self.word_dict.iterkeys())#测试if __name__=='__main__': myseg = MLSegment() myseg.initial_dict("count_1w.txt") sequence = "itisatest" seg_sequence = myseg.ml_seg(sequence) print "original sequence: " + sequence print "segment result: " + seg_sequence
这个是完全按照上面的思路写的,当然,因为截取片段的时候,其实只要记录截取的起始位置即可,然后逐步更新这个变量就行,不用记录所有的片段字符串,因此可以写一个改进方法,代码比上面的更清楚,但是理解起来会稍微麻烦一些。写代码一般都是从基础思路写起,然后慢慢改进的,不能一下就找出最优的写法:
#!/usr/bin/env python#coding=utf-8##############################################################function: 正向最大分词##############################################################import sysimport math#global parameterDELIMITER = " " #分词之后的分隔符class MLSegment: def __init__(self): self.word_dict = {} #记录单词 self.gmax_word_length = 0 #单词的最大长度 #最大长度分词 def ml_seg(self, sequence): sequence = sequence.strip() #初始化 segment_start_pos = 0 #开始 word_list = [] #循环体 while True: #step 1,确定候选片段的最大长度 if len(sequence) - segment_start_pos > self.gmax_word_length : max_segment_length = self.gmax_word_length else: max_segment_length = len(sequence) - segment_start_pos #step 2,从候选片段中选择候选词 for word_length in range(max_segment_length, 0, -1): word = sequence[segment_start_pos:segment_start_pos+word_length] if self.word_dict.has_key(word): word_list.append(word) break if 1 == word_length and not self.word_dict.has_key(word): word_list.append(word) #候选片段位置前移 segment_start_pos += word_length #如果移动到了末尾位置,则退出 if segment_start_pos == len(sequence): break return DELIMITER.join(word_list) #加载词典,为词\t词频的格式 def initial_dict(self,filename): dict_file = open(filename, "r") for line in dict_file: sequence = line.strip() key = sequence.split('\t')[0] self.word_dict[key] = 1 #获取最大词长 self.gmax_word_length = max(len(key) for key in self.word_dict.iterkeys())#测试if __name__=='__main__': myseg = MLSegment() myseg.initial_dict("count_1w.txt") sequence = "itisatest" seg_sequence = myseg.ml_seg(sequence) print "original sequence: " + sequence print "segment result: " + seg_sequence
当然,还可以进一步的写递归的形式,会更简洁,但递归基本都是奇淫技巧,实际项目很少用到。
对"itisatest", 利用正向最大匹配,分割出来的是“itis atest”, 现在不符合需求,但是一个最基本的分词方法,在汉语分词上也有不错的效果,一般都作为分词算法的baseline。
后面一节就介绍最大概率分词,就可以解决这个问题。
字典文件和代码baidupan下载:http://pan.baidu.com/s/1dDikxg9
- 分词学习(1)--正向最大匹配分词
- 【分词】正向最大匹配中文分词算法
- 分词】正向最大匹配中文分词算法
- 【分词】正向最大匹配中文分词算法
- 中文分词-- 正向最大匹配法分词
- 【分词】正向最大匹配中文分词算法
- 最大正向匹配分词MM
- python正向最大匹配分词和逆向最大匹配分词
- python 实现机械分词(1)-正向最大匹配算法
- 正向最大匹配中文分词算法
- NLP中文信息处理---正向最大匹配法分词
- 正向最大匹配中文分词算法
- 正向最大匹配中文分词算法
- 正向最大匹配中文分词算法
- 分词算法:正向最大匹配算法
- 正向最大匹配中文分词算法
- java实现正向最大匹配分词
- python 中文分词:正向最大匹配
- 数据库-Oracle-plsql(二)
- Redis的存储
- 也许没你那么铁的朋友
- oracle数据导入导出(本地/远程)
- svn图标不显示的解决办法
- 分词学习(1)--正向最大匹配分词
- 等比例缩放图片
- 关于用ajax 乱码的问题的解决;
- Objective-C语法之KVO的使用
- Objective-C语法之KVC的使用
- 也谈C++中char*与wchar_t*之间的转换
- nginx配置文件学习
- java使用代理服务器获取网页脚本
- 十分钟让你明白Objective-C的语法(和Java、C++的对比)