一种快速分词方法

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

一种基于自动机的分词方法

迟呈英1,战学刚2, 姚天顺2

(1鞍山科技大学计算机学院辽宁鞍山114002,

2东北大学信息学院辽宁沈阳110004)

摘要本文介绍一种简洁有效的快速分词方法,并通过理论分析和实验对比说明几种分词方法的效率差异,以说明我们所提出的方法的有效性。

关键词:中文信息处理,分词,顺序查找,二分查找,自动机,二叉树

分类号:TP 文献标识码

1 引言

西方语言在语句(或从句)内词汇之间存在分割符(空格),而汉语的词汇在语句中是连续排列的。因此,汉语词汇的切分(分词)在中文信息处理的许多应用领域,如机器翻译、文献检索、文献分类、文献过滤、以及词频统计等,是非常重要的第一步。

自动分词是基于字符串匹配的原理进行的。迄今为止,已经有许多文献对各种分词方法进行探讨,其着重点或为分词的速度方面,或为分词的精度方面以及分词的规范。本文主要探讨分词的速度问题,通过实验对比和理论分析,说明我们所提出的算法是有效的。

目前人们所提出的分词方法,在考虑效率问题时,通常在词典的组织方面进行某种调整,以适应相应的算法,如最大匹配法、最小匹配法、逐词遍历法、以及最佳匹配法等。这些方法中,或将词典按词条长度排序或按词频排序,其目的在于协调算法与数据结构,使之效率最高。客观地说,它们都在一定程度上提高了分词的效率。

本文所介绍的是基于词典的最大向前匹配方法。而在数据结构方面,我们则是将词典组织成自动机形式。

2 数据结构与算法

文献[1,2,3]给出了三种基于词典的最大向前匹配方法的分词算法(相应于文献编号,我们以后分别称其对应的算法为算法1、算法2、和算法3)。我们可以把算法1看作是原始算法,把算法2看作是算法1的改进,而算法3则是算法2的进一步优化。在词典的组织方面,算法2和算法3是按照正常的词典排序(即按汉字的机器内码表示排序),并辅以词条的首字索引,以标明以该字起始词条在词典中的首记录。例如,在一般的词典中,词条的形式如下图所示:

图1:一般分词词典的形式

啊啊哈

啊呀

啊哟

阿阿爸

阿斗

阿尔巴尼亚

阿飞

阿富汗

在实际存储时,可以在词尾部分删除首字。这样做不仅节省了存储空间,更重要的是缩短了字符串比较的长度。

算法2和算法3对首字的检索都是基于哈希算法;算法2对于词尾部分采用线性搜索,而算法3则采用二分搜索。采用何种搜索算法应根据所用词典中每个首字下的词条数目确定,一般词条数较小时,二者无明显差异。这是由这两种算法本身的特性决定的。实际词典中许多首字下的词条数目很大,因此,采用二分搜索法较优。我们的实验结果也证实了这一点。

算法2和算法3在词典的组织方面是一致的,即如同普通词典一样,按照汉字的内码递增排序,并以词条的首字建立哈希索引。我们可以将同一首字下的所有词条组织成一个子表结构,如下图所示。

图2:词典的逻辑结构

假设:源文本source_text=“中华人民共和国成立于1949年。”

分词结果=“中华人民共和国/成立/于/1949/年/。”

分词过程为:

1.从源文本source_text中取首字head_word = “中”,并设置已切分词汇

segmented_word = head_word;

2.从索引中查找该首字。若未找到,则暂将该字作为单字词输出;否则,将其后续字

符加入临时变量tail_word =“华”;

3.在以“中”为首字的子表中查找包含tail_word的词条;若查到,则从source_text

中取字,继续加入tail_word中,并继续在子表中查找。在此过程中,如果满足条

件的词条等于当前的tail_word,则置segmented_word = head_word + tail_word;

4.步骤3中的查找失败时,则以当前segmented_word中的字符串作为输出结果。

算法2和算法3的处理思想是一致的,只是在上述第三步的查找中,算法2采用的是顺序查找,而算法3采用的是二分查找。

在本例中,tail_word从“华”递增到“华人民共和国”的过程中,即使不计查找过程中的比较次数,tail_word与词典中的子表项“华”字比较了1次,同“华人民共和国”比较了5次。其比较长度分别为2、4、6、8、10、12。

“华”(segmented_word = “中华”)

“华人”

“华人民”

“华人民共”

“华人民共和”

“华人民共和国”(segmented_word = “中华人民共和国”)

显然,这种比较过程存在冗余的比较操作。例如,“人”字比较了5次,其中后4次的比较是多余的。因为字符串比较所需的时间同字符串的长度成正比,对于较长的词条,这种现象尤为突出。

为了消除这种冗余操作,我们提出将词典的词尾部分以自动机的形式来组织。为此,我们将组成单词的每个字以一种链表节点的形式存储,其抽象数据结构的定义如下:Pnode = ^Tnode;

Tnode = record

Brother: Pnode;

Cchar: String[2];

Accepted: Boolean;

Child: Pnode;

End;

这样,上述的例子中,词典的部分内容形式如下(其中T代表True,F代表False;节点左侧为兄弟链,右侧为孩子链):

图3:改进的词典逻辑结构

显然,这实际上是将词典以二叉树的形式组织起来,只是各节点中增加了接收状态。

在词典中对于特定的首字,前两字相同的词条很少,前三字相同的词条更少。当我们以这种形式组织词典后,除子表的第一层外,各个节点的兄弟数目都很小,对它们的查找采用顺序查找方法较为适宜。对于子表的第一层,则采用二分查找。由于我们无法在一个纯粹的链表结构中进行二分查找,为此,我们可以将子表的首层节点以动态数组形式组织,或装入容器类(Container)的可直接存取的线性表结构中(如C++的vector,Delphi的Tlist等)。

对应于前文所述的算法,其第三步变为:

以二分查找方法在子表首层中查找含tail_word的节点;若查到,从source_text中取后续汉字,继续加入tail_word中,并继续在当前子表的孩子节点中顺序查找该汉字。在此过程中,如果满足条件的节点中Accepted域为真,则置segmented_word = head_word + tail_word;

依此算法,显然不会出现同一词条中的重复比较,且每次比较的字符串(一个汉字)长度均为2。与算法2和算法3相比,在子表首层以下的搜索过程中,每次搜索的范围因词典的组织方式变化而大大缩小,这也在一定程度上提高了分词效率。

3 对比

文献[2,3]都采用了文献[2]所提出的复杂度估算方法,其相应算法的复杂度分别为2.89和1.66。由于在处理3字以上词时搜索空间的缩小(即使对于出现频率最高的双字词,基于最大匹配原则,也需对其后继字符进一步测试比较),本文的算法复杂度显然应低于算法3的1.66。此外,文献[2]所提出的复杂度估算方法并未考虑字符串长度对比较时间的影响。

根据我们的实验测定,算法3比算法2大约快3倍。表1是针对同一组2.576MB文本的分词结果比较,其中的比较次数是指实际调用字符串比较函数的次数,比较总长度是指每次调用字符串比较函数所比较的字符串的总长度,运行环境为Windows2000(奔腾III,800M 主频,256M内存)。

从表1可以看出,算法2比较次数过多,且字符串比较的总长度过大。而算法3的比较次数和字符串比较的总长度均大于本文算法。同一首字下的所有词条组织成的子表的查找,应采用二分查找。这正是算法3优于算法2的关键之处。而本文算法的优势则体现在比较次数的缩小和被比较字符串的长度缩小,绝无多余的比较,从而在总比较长度上占绝对优势。

从实验结果可以看出,分词时间大约与字符串比较的总长度成正比。实际上,我们的多组对比实验表明,随着被处理语料规模的增大,这种线性比例关系表现得更为明显。这也是“字符串比较所需的时间同字符串的长度成正比”这一结论的实际验证。

我们的实验所对比的三种算法均至少用C++和Object Pascal两种语言实现,而且为了对比结果客观公正,所用数据结构和算法的实现细节都尽可能一致。文中的实验数据是Delphi 版本的结果,其中括号内的时间是在用小于运算代替字符串比较函数时的结果(Object Pascal

相关文档
最新文档