输入法引擎的模型设计

最近在写《自表达代码》一书,该书第22章将会介绍一个Android平台上的日文输入法的开发过程。经过该开发过程展现如何在程序开发过程当中保持代码的可读性、可扩展性和可变动性。html

在写该部分以前,首先须要进行输入法引擎的设计。android

下面是一个相对来讲傻大笨粗的输入法引擎设计思路。虽然傻大笨粗,可是因为数据量并不大,数据算法次数并很少,因此该设计仍然是“能够接受的”。即处理时间上比较快、存储空间上占用不大。可是距离十分优秀的输入法还有很长的距离要走。算法

 

输入法的基本工做原理就是,输入一堆英文字符,而后利用英文字符到一个字典中去查找英文字符应该翻译成什么对应的天然语言文字。express

这里提到的输入法引擎就按照这个思路来设计的。数据结构

 

首先,输入法的字典应该是一个二进制文件。ide

二进制文件中包含了这样三部份内容。svn

1. 文件头ui

      表述了一些基本信息,好比:索引的尺寸,字典的尺寸等。 假定为100字节。    lua

2. 索引url

     用来加快计算速度的。可是因为双字节语言(CJK)都是采用了长拼写的方式,而且还有词组的概念,因此这个索引会比较复杂。

     还会涉及到索引的索引。具体后续会讨论。

3. 字典数据

     用来存储具体的字典数据。

 

很显然,这样的数据结构是很容易被注入的,DOS下的一些病毒就是利用了文件分区表 的特色进行注入的。这个设计思路和文件分区表并没有二致。

可是这里只是代表算法的基本思路,对于数据文件的保护能够经过其余的方式来进行,这里不作讨论。

 

为了可以更好的描述算法,从输入的角度开始,而不是从技术的角度开始。

以中文为例,输入zhuru,应该显示注入,诸如,侏儒三个候选汉字词组。

那么这个过程是怎么完成的呢?

 

首先,在索引中找zhu;而后,在zhu下面找ru;再而后在zhuru下找到词组。

这听起来很简单,不是吗?

那么在具体的技术中怎么实现的呢?

1. 索引中找zhu

    把中文的声母 和韵母列成两张表格

   声母:b p m f d t n l g k h j q x zh ch sh r z c s w y

   韵母:a o e i u v ai ei ui ao uo ou ang eng ing ong an en in un vn iong...(还有很长)

   分别依次对这些符号进行编号,都采用2位数字

   好比:b = 01 p = 02..

            a= 01 o =02..

   那么zhu是多少呢? zh=15 u=05,那么zhu就是1505

   ok 那么zhu的偏移量就是 (15(zh的id) x (韵母的个数) + 05(u的id)) x 4(4字节为单位)。

   这个数字是干什么用的呢?100(文件头的尺寸) + 这个数字(zhu的偏移量)能够定位到文件中的一个位置。

   假设韵母的个数是50吧(好算)

    (15 x 50 + 05 ) x 4 = 755 x 4 = 3,020

    再 + 100,就是3,120。

    找到3120这个位置,读取4个字节,这4个字节是一个数字,这个数字是一个地址,这个数字表示了zhu开头的全部拼音组合的索引的开头地址。

    好比它就是600000吧,那么咱们定位到600,000这个位置,这里记录了zhua,zhuo,zhue,zhui,zhuu,zhuv等等全部的词组的索引。

 

    而后,找到这个zhu开头的全部的索引以后,直接定位到其中的ru的位置。

    怎么计算呢? 和刚才同样,r=18, u = 05,ru就是1,805。

    而后ru的偏移量就是 (18 x 50 + 05) x 4 = 3,620。

    那么定位到603,620这个位置,从这个位置也读取4个字节,它仍是一个地址。好比就是5,000,000吧。

    这个意思是说5,000,000这个位置开始是zhuru的词组,可是到底有几个呢?因此,这个位置上来不是词组,而是词组的一些简要信息,

    1个字节,表示数据的个数。那么,这里应该是3。而后向后依次读取3 x (2 个汉字 + 3字节的词频) 。每2个字组成一个词组,返回回来。

 

    这样算下来,一共须要的计算是

     1. zhu的位置计算

     2. ru的位置计算

     3. zhuru的位置定位

     4. zhuru的信息读取

     5. 词组的读取

     6. 词组的分离

  这样几个步骤。能够达到秒杀了。

 

  而后,看看文件的尺寸,

  文件头100字节

  索引的索引的尺寸 = 声母数 x 韵母数 x 4字节 = 21 x 50 x 4 = 4,200字节

  索引的尺寸(按照只收录2字词组计算) = 索引的索引个数(第一个字) x 索引的索引个数(第二个字) x 4字节 = 4,410,000字节

  而后,平均每种组合有3个词组(实际上会比这个略低一点,由于有不少拼音是不存在的,并且不少拼音是没有词组的)。

  数据的尺寸 = 索引的个数(第一个字) x 索引的个数(第二个字) x 3(个词组) x (2个字/个词组  + 3字节/词频 ) x 2字节/字 + 索引的个数 x 索引的个数 x 1字节 = 

                    (索引的个数 x 索引的个数) x ( 3 x (2 + 3)x 2 + 1) = 1050 x 1050 x 31 = 34,175,500 字节

 

  累加  100 + 4,200 + 4,410,000 + 34,175,500 = 38,591,800

  约36.8M字节。这还只是2个汉字的词组。这是没法容忍的。

 

  上面的算法用下图能够表示出来。

 

  因此,应当对词典进行瘦身,也应当对算法进行改进。

 

  1. 因为每一个拼音下面的汉字不会超过 256个,因此汉字能够用索引来代替,从而减小1个字节。

      须要另外创建一张汉字与拼音对照表。

  2. 从新认真的数一下韵母的个数

       a o e i u v ai ei ui ao ou iu ie ve er an en in un vn ang eng ing ong uan uang ian iao iang iong 

     共30个。

       另外,还有一个没有声母的状况。

  3. 从索引中排除那些确定不会存在的拼音

      和v相拼的 bv pv mv fv dv tv gv kv hv jv qv xv zhv chv shv rv zv cv sv yv wv

      和vn相拼的 bvn pvn mvn fvn dvn tvn gvn kvn hvn jvn qvn xvn zhvn chvn shvn rvn zvn cvn svn yvn wvn

      和i相拼的gi ki hi wi

      和ie相拼的fie gie kie hie zhie chie shie zie cie sie yie wie

      和iu相拼的biu piu miu fiu giu kiu hiu zhiu chiu shiu riu ziu ciu siu yiu wiu

      和in相拼的fin din tin gin kin hin zhin chin shin rin zin cin sin win

      和ian相拼的fian gian kian hian zhian chian shian zian cian sian rian yian wian

      和iao相拼的fiao giao kiao hiao zhiao chiao shiao ziao ciao siao yiao wiao

      和iang相拼的biang piang miang fiang diang tiang giang kiang hiang zhiang chiang shiang ziang ciang siang yiang wiang

      和iong相拼的biong piong miong fiong diong tiong niong liong giong kiong hiong zhiong chiong shiong ziong ciong siong yiong wiong

      和un相拼的 bun pun mun fun nun

      和uo相拼的buo puo muo fuo juo quo xuo yuo wuo

      和uan相拼的 buan puan muan fuan wuan

      和uang相拼的buang puang muang fuang duang tuang nuang luang ruang zuang cuang suang yuang wuang

      和a相拼的ja qa xa ra

      和ai相拼的fai jai qai xai rai yai

      和an相拼的 jan qan xan

      和ang相拼的 jang ang xang

      和e相拼的je qe xe we

      和ei相拼的dei zhei chei shei cei sei yei

      和er相拼的ber per mer fer der ter ner ler ger ker her jer qer xer zher cher sher rer zer cer ser yer wer

      和en相拼的den ten len jen qen xen 

      和eng相拼的jeng qeng xeng yeng weng

      和o相拼的do to no lo go ko ho jo qo xo zho cho sho ro zo co so yo

      和on相拼的don ton non lon gon kon hon jon qon xon zhon chon shon ron zon con son yon won

      和ong相拼的jong qong xong yong wong

      和ou相拼的bou jou qou xou you wou

      (懒得数这有多少了,用程序计算了一下是292个。)      

     
      这须要牺牲算法时间为代价,由于不能仅仅经过offset来定位。(至于新算法也和老算法意思差很少,只是偏移形式从新规划要按照声母 + 韵母进行偏移量计算,而不是分开计算。)

      22个声母 x 30 个韵母 - 292 = 368个组合

      

按照新思路从新计算一下空间

1. 100 字节的文件头

2.  368 x 4字节的索引的索引 = 1,472 字节

3. 368 x 368 x 4字节的索引 = 541,696字节

4. 368 x 368 x (3 x (1 + 3) x 2 + 1) =  3,385,600字节

累计为 3,928,868字节。容量已经缩减为3.7M左右了,是第一个方案的1/10左右。

之因此没有一上来就提这个3.7M的方案是为了可以先描述清楚思路。另外,对于这个方案还有瘦身的余地,可是思路已经描述清楚,就再也不继续瘦身了。

 

好了,至此,一个简单的文字翻译引擎就设计完了。

 

只输入的时候xian是如何变成xian和xi'an两种组合的,则不在词典检索范畴来作,而是在输入端进行控制。

这个时候词频表的做用就体现了。

相关文章
相关标签/搜索