ictclas和別的分司系統不一樣的地方就是於--n最短路徑分詞演算法。所謂n最短路徑其實就是最短路徑和最大路徑的折中,保留前n個最優路徑。這樣做的目的就是對這兩種方法取長補短,既能達到乙個比較理解的分詞不達意效果,又能保證分詞不達意速度。在此處,我們中國人的中庸思想被完美體現:)。
在n-最短路徑求解之前,ictclas首先通過二叉分詞圖表(鄰接表,如下圖一所示)表示出了每個片語之間的耦合關係,每乙個節點都表示分詞圖表中的一條邊,它的行值代表邊的起點(前驅),它的列值代表邊的終點(後驅),這一點務必弄清楚。可以通過圖
一、圖二相結合對照來理解。通過計算片語之間的耦合關係,來最終確定初次的分詞路徑。我們都知道dijkstra演算法是求源點到某一點的最短路徑,也就是最優的那一條,在此處的n-最短路徑指的是找出前n條最優的路徑(實際上在freeictclas的源**當中n是等於1的,即nvaluekind==1)。按照dijkstra的表示方法把二叉分詞圖表轉化成圖二的表示形式,就能比較清楚地看出來,求解的過程實際就是求源點0到終於12的最短路徑,和純粹的dijkstra演算法不同的地方是在此處需要記錄每個節點的n個前驅,dijkstra當中記錄乙個即可。
圖一圖二
在求解過程中,源程式通過二維陣列m_pparent[i][j]、m_pweight[m][n]來記錄每個節點的n個前驅和每個前驅和權重,而求解最短路徑權重時借用了乙個佇列來實現排序,資料結構如下圖三所示:
圖四在源程式中,n最短路徑是在cnshortpath類裡裡面實現的。
bool
csegment::bisegment(
char
*ssentence,
double
d**oothingpara, cdictionary
&dictcore, cdictionary
&dictbinary, unsigned
intnresultcount)
...
對源**進行解析,以「他說的確實在理」為例項:
//進行n-最短路徑的求解,找出每乙個節點的前驅計算前驅的權值(從源點到該前驅節點)
intcnshortpath::shortpath()
...else
//該條邊的起點是0,即該起點沒有父結點,是分詞的源點
...}
//end for
pedgelist
=pedgelist
->
next;}//
end while
//now get the result queue which sort as weight.
//set the current node information
for(i=0
;i<
m_nvaluekind;i++)
...//
memset((void *),(int),sizeof(element_type)*);
//init the weighti=
0;
//設定當前節點的n個前驅節點的最短路徑的權值
//以"他說的確實在理"為例
//m_pweight[0][0]=3.846
//m_pweight[1][0]=6.025
//m_pweight[2][0]=10.208
//m_pweight[3][0]=15.063
//m_pweight[4][0]=16.190
//m_pweight[5][0]=16.184
//m_pweight[6][0]=28.331
//m_pweight[7][0]=28.331
//m_pweight[8][0]=28.923
//m_pweight[9][0]=28.923
//m_pweight[10][0]=36.416
//m_pweight[11][0]= 39.889
while
(i<
m_nvaluekind
&&quework.pop(
&nprenode,
&nindex,
&eweight)
!=-1
)...
//m_pparent[0][0]=(0,0,0)
//m_pparent[1][0]=(1,0,0)
//m_pparent[2][0]=(2,0,0)
//m_pparent[3][0]=(2,0,0)
//m_pparent[4][0]=(3,0,0)
//m_pparent[5][0]=(3,0,0)
//m_pparent[6][0]=(4,0,0)
//m_pparent[7][0]=(4,0,0)
//m_pparent[8][0]=(6,0,0)
//m_pparent[9][0]=(6,0,0)
//m_pparent[10][0]=(9,0,0)
//m_pparent[11][0]=(11,0,0)
m_pparent[ncurnode-1
][i].push(nprenode,nindex);}}
//end for
return1;
}
經過對每個節點的前驅求解後,得到前驅的最短路徑權值和它的父節點,記錄如下圖四所示:
圖四然後通過佇列(其實更象乙個棧)來求出二叉分詞路徑:
//bbest=true: only get one best result and ignore others
//added in 2002-1-24
void
cnshortpath::getpaths(unsigned
intnnode,unsigned
intnindex,
int**
nresult,
bool
bbest)
...if
(ncurnode
>0)
queresult.push(ncurnode,ncurindex);}//
當到0節點時,也就意為著形成了一條最短路徑
if(ncurnode==0
)...
nresult[m_nresultcount][nresultindex]=-1
;//
set the end
m_nresultcount+=1
;//
the number of result add by 1
if(m_nresultcount
>=
max_segment_num)
//only need 10 result
return
;nresultindex=0
;nresult[m_nresultcount][nresultindex]=-1
;//
init the result
if(bbest)
//return the best result, ignore others
return;}
//首先判斷棧頂元素是否有下乙個前驅,如果沒有則刪除棧頂元素直到有下乙個前驅的元素出現
queresult.pop(
&ncurnode,
&ncurindex,0,
false
,true
);//
read the top node
while
(queresult.isempty()
==false
&&(m_pparent[ncurnode-1
][ncurindex].issingle()
||m_pparent[ncurnode-1
][ncurindex].isempty(
true
)))...
//如果找到了有下乙個前驅的節點,則它的前驅壓入棧中,重新迴圈直到把源點也壓入
if(queresult.isempty()
==false
&&m_pparent[ncurnode-1
][ncurindex].isempty(
true)==
false
)...}}
最終得到最短路麼(0,1,2,3,6,9,11,12),裡面的數值分別對應研究(四)中圖四的下標,到此分詞的第一大步就結束了,並形成最終結果:始##始/他/說/的/確實/在/理/末##末
ICTCLAS分詞系統研究(三) 原子切分
ictclas分詞的第一步就是原子分詞。但在進行原子切分之前,首先要進行斷句處理。所謂斷句,就是根據分隔符 回車換行符等語句的分隔標誌,把源字串分隔成多個稍微簡單一點的短句,再進行分詞處理,最後再把各個分詞結果合起來,形成最終的分詞結果。分成短句之後,即可進行原子分詞,所謂原子,是指該短句中不可分割...
DOS系統研究
這裡說的特指ms dos,一款由微軟從seattle computer products購買來的針對16位8086 8088系列處理器的作業系統。它隨著由16位的80x86處理器的桌面電腦的普及而成為曾經的主流作業系統,但是當更先進的處理器在桌面電腦中流行後,它的主流地位也讓位於其他更成熟的作業系統...
ACARS系統研究
1 概述 acars aircraftcommunication addressing reporting systems 飛機通訊定址報告系統,美國arinc公司開發,採用迴圈冗餘校驗碼 crc 進行校驗。航空器與地面站之間通過無線電或衛星傳輸短訊息 報文 的數字資料鏈系統。具有傳輸速度快 抗干擾...