最近在寫《自表達代碼》一書,該書第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兩種組合的,則不在詞典檢索範疇來作,而是在輸入端進行控制。
這個時候詞頻表的做用就體現了。