首先去《知網》的官方網站上下載WordSimilarity.rar,解壓后有兩個文件是我們需要的:whole.dat和glossary.dat,關於那個《基於知網的詞匯語義相似度計算.doc》建議不要看,那是個老版本的,寫的不清楚,可以去這里看Final版(劉群等著),本博客就是按照這個版本來計算詞語相似度的,只是個細節略有改動。
現在很多人提出的“改進”算法實際上只是“拓展”,因為他們忘了劉群等人的計算方法是以基於實例的機器翻譯為背景的。
一切工作都是在Linux下進行的,所先要把whole.dat和glossary.dat從cp936轉換為utf8編碼,使用iconv工具。
glossary.dat中有一些像“跟 ... 過不去”、“既...又”這樣的詞匯,我們使用分詞工具不會得到這樣的結果,將含有“...”的行刪除。
glossary.dat一行有3項,我們希望可以利用空格把這3項分隔開,可不幸的是在第3項內部有時候也會有空格,比如“蘭特 N money|貨幣,(South Africa|南非)”,“South Africa”中間就有空格。所以進行下面的處理:awk 'BEGIN{OFS="\t"}{print $1,$2,$3 $4 $5;}' glossary.dat>gloss.dat
下面是《知網》中的一些概念,你必須清楚:
一個詞語有n個“概念”,一個“概念”有n個“義原”。比如
拉平 ADJ aValue|屬性值,content|內容,neat|齊,desired|良
拉平 V equal|相等
這里“拉平”就有2個概念,並且詞性還不相同,一個是ADJ,一個是V。上面那個概念就有4個義原,義原之間用逗號隔開。
whole.dat中記錄了所有的義原,形如
0 event|事件 0
1 static|靜態 0
2 relation|關系 1
3 isa|是非關系 2
4 be|是 3
5 become|成為 4
6 mean|指代 4
7 BeNot|非 3
8 possession|領屬關系 2
9 own|有 8
每行的第3個數字指示了該義原的父節點是誰。所有的義原按照這種父子關系形成了一個森林,這個森林中有10棵樹,它們的根節點分別是:
1) Event|事件
2) entity|實體
3) attribute|屬性值
4) aValue|屬性值
5) quantity|數量
6) qValue|數量值
7) SecondaryFeature|次要特征
8) syntax|語法
9) EventRole|動態角色
10) EventFeatures|動態屬性
所有的義原還進行了分類,在1到7號樹上的為“基本義原”,8號樹上的是“語法義原”,9號10號樹上的是“關系義原”。
同一棵樹上的義原存在上下位關系,從根節點“event|事件”我們找到“become|成為”和“own|有”:
event|事件--static|靜態--relation|關系--isa|是非關系--be|是--become|成為
event|事件--static|靜態--relation|關系--possession|領屬關系--own|有
則義原“become|成為”和“own|有”在樹上的距離是5。
義原是用來解釋概念的,在glossary.dat中你還會看到一些“符號”,形如
# 表示“與其相關”
^ 表示不存在,或沒有,或不能
在glossary.dat的第3列中用逗號隔開的是一些“語義描述式”。
實詞的“語義描述式”又可分為3類:
a) 獨立義原描述式:用“基本義原”,或者“(具體詞)”進行描述;
b) 關系義原描述式:用“關系義原=基本義原”或者“關系義原=(具體詞)”或者“(關系義原=具體詞)”來描述;
c) 符號義原描述式:用“關系符號 基本義原”或者“關系符號(具體詞)”加以描述;
在glossary.dat中用小括號括起來的就是“具體詞”,具體詞不是義原,它們不包含在whole.dat中。
在一行中上述3種語義描述式有誰沒誰、誰先出現都是不確定的,但是當有“(具體詞)”出現時,就肯定有“基本義原”出現。
虛詞的描述式整體被一個大括號括起來,並且大括號里不會出現關系義原描述式和具體詞,比如
你 PRON {SecondPerson|你}
你們 PRON {SecondPerson|你,mass|眾}
毋庸置疑V{modality|語氣,neg|否,#doubt|懷疑}
注意括在大括號里的不一定是虛詞概念,但虛詞概念的描述式都被括在大括號里。那如何區分哪些是虛詞概念呢?由於虛詞概念描述式中不沒有關系義原描述式,那兩個虛詞概念之間的相似度又該如何計算呢?我沒有在相關的文獻上找到答案。
按照基本的語言知識,虛詞包括:副詞、介詞、連詞、助詞、嘆詞、擬聲詞。可是《知網》中的連詞是用什么來標注呢?
以及COOR{and|和}
和COOR{and|和}
而 CONJ {but|但}
而 CONJ {EventResult|事件結局}
而 COOR {and|和}
縱然CONJ{concession|讓步}
要不然CONJ{transition|轉折}
你能分清COOR和CONJ的區別嗎?
起來 STRU {Vdirection|動趨,upper|上}
進來 STRU {Vdirection|動趨,internal|內}
上來 STRU {Vdirection|動趨,upper|上}
看完上面的好像STRU表示“方向、方位”,可是再看下面的你就迷惑了:
不了 STRU {^Vable|能力}
了 STRU {MaChinese|語助}
以來 STRU {TimeIni}
及 STRU {Vachieve|達成}
總之,我感覺《知網》有一些工作粗糙的地方,劉群的論文里面也有一些沒說清楚(比如哪些是虛詞概念,虛詞概念相似度的計算)和說錯的地方(比如“{}”內從來就沒有出現過“=”,“在實詞的描述中,第一個描述式總是一個基本義原”這句話也是不對的,比如“不務正業 V ^endeavour|賣力,content=duty|責任”,根本就沒有出現基本義原)。
一個虛詞概念只有一個義原;而一個實詞概念有多個義原,這些義原又分為4部分:
1) 第一獨立義原描述式
2) 其他獨立義原描述式:語義表達式中除第一獨立義原以外的所有其他獨立義原(或具體詞)
3) 關系義原描述式:語義表達式中所有的用關系義原描述式
4) 符號義原描述式:語義表達式中所有的用符號義原描述式
其他獨立義原描述式存在時,第一獨立義原描述式就一定存在,但兩都可能都不存在。同時考慮到當有“(具體詞)”出現時,就肯定有“基本義原”出現,所以第一獨立義原描述式肯定不是具體詞。
舉個例子,假如有個概念的語義表達式是:ContentProduct=text|語文,aValue|屬性值,attachment|歸屬,#country|國家,ProperName|專,(Nicaragua|尼加拉瓜)
則我們在把它存入sqlite數據庫按照4部分的順序存儲為:
aValue|屬性值,
attachment|歸屬,ProperName|專,(Nicaragua|尼加拉瓜),
ContentProduct=text|語文,
#country|國家,
概念講完了,下面講詞語相似度的計算。
假如一個詞語有m個概念,另一個詞語有n個概念,那么就有m*n種組合,計算每對概念的相似度,取最大者作為詞語間的相似度。
那么概念間的相似度又如何計算呢?實詞概念和虛詞概念之間的相似度設為0。我們知道一個實詞概念的語義表達式分為4部分:
1) 第一獨立義原描述式: 這一部分的相似度記為sim1
2) 其他獨立義原描述式: 這一部分的相似度記為sim2
3) 關系義原描述式: 這一部分的相似度記為sim3
4) 符號義原描述式: 這一部分的相似度記為sim4
總的相似度是這4部分的加權和
\begin{equation} sim=\sum_{i=1}^{4}{{\beta}_{i}*{sim}_{i}} \end{equation}
劉群等人實際應用的是這個公式
\begin{equation} sim=\sum_{i=1}^{4}{{\beta}_{i}\prod_{j=1}^{i}{{sim}_{j}}} \end{equation}
因為劉群等認為由於第一獨立義原描述式反映了一個概念最主要的特征,主要部分的相似度值對於次要部分的相似度值應起到制約作用,也就是說,如果主要部分相似度比較低,那么次要部分的相似度對於整體相似度所起到的作用也要降低。
下面分別說明4部分的相似度如何計算。
1) 第一獨立義原描述式:由於第一獨立義原描述式只包含一個基本義原,因此可以轉換為一對基本義原相似度的計算。
2) 其他獨立義原描述式:這部分包含多個基本義原(或具體詞),按照如下步驟對這些獨立義原描述式分組:
a) 先把兩個表達式的所有獨立義原(第一個除外)任意配對,計算出所有可能的配對的義原相似度;
b) 取相似度最大的一對,並將它們歸為一組;
c) 在剩下的獨立義原的配對相似度中,取最大的一對,並歸為一組,如此反復,直到所有獨立義原都完成分組。
最后不對配對的丟棄。計算每對義原的相似度,算術平均后得到sim2
3) 關系義原描述式:把關系義原相同的描述式分為一組,不能配對的舍棄。計算每對義原的相似度,算術平均后得到sim3
4) 符號義原描述式: 與3)類似,把符號義原相同的描述式分為一組。
剩下的問題就是如何計算一對義原的相似度。
義原和具體詞的相似度記為0;具體詞和具體詞相同時相似度記為1,不同時記為0;義原和義原的相似度用公式:
\begin{equation}sim({p}_{1},{p}_{2})=\frac{\alpha}{\alpha+d}\end{equation}
d是兩個義原在樹上的距離,如果兩個義原不在同一棵樹上,則相似度記為0。
劉群等人用的參數是:
α= 1.6;
β1 = 0.5, β2 = 0.2,β3 = 0.17,β4 = 0.13
下面是我的代碼
注意我粗略地認為括在大括號里的就是虛詞概念,並且一對虛詞概念它們的描述式中只含有一個語法義原,這顯然是非常錯誤的。
glossary2db.cpp
#include<iostream> #include<sqlite3.h> #include<cstdlib> #include<string> #include<cstring> #include<fstream> #include<sstream> #include<sys/stat.h> #include<cassert> using namespace std; const int KeyWordLen=60; //“概念”的最大長度 const int POSLen=8; //“詞性”的最大長度 const int SememeSetLen=200; //一個“概念”對應的“義原”集合的最大長度 int main(int argc,char *argv[]){ sqlite3 *db; char *zErrMsg=0; int rc; rc=sqlite3_open("glossary.db",&db); //打開數據庫 assert(rc==SQLITE_OK); char sql[500]={0}; sprintf(sql,"create table t_gloss(id integerprimary key,concept varchar(%d),pos char(%d),semset varchar(%d))",KeyWordLen,POSLen,SememeSetLen); rc=sqlite3_exec(db,sql,0,0,&zErrMsg); //創建表 assert(rc==SQLITE_OK); ifstream ifs("glossary.dat"); //打開詞典文件 assert(ifs); string line; int recordid=0; while(getline(ifs,line)){ //逐行讀取詞典文件 istringstream stream(line); string word,pos,sememe; stream>>word>>pos>>sememe; //由空白把一行分割成:詞、詞性、義原集合 string set; if(sememe[0]=='{'){ //該行是虛詞,因為虛詞的描述只有“{句法義原}”或“{關系義原}” set=sememe+","; } else{ //該行是實詞,要按“基本義原描述式\n其他義原描述式\n關系義原描述式\n關系符號義原描述式”存儲 string str1,str2,str3,str4; string::size_type pos1,pos2; pos1=0; bool flag=true; while(flag){ pos2=sememe.find(",",pos1); string sem; if(string::npos==pos2){ //已是最后一個義原 flag=false; sem=sememe.substr(pos1); //提取最后一個義原 } else{ sem=sememe.substr(pos1,pos2-pos1); //提取下一個義原 } pos1=pos2+1; if(sem.find("=")!=string::npos){ //關系義原,加入str3 str3+=sem+","; } else{ char c=sem[0]; if((c>64&&c<91) || (c>96&&c<123) || (c==40)){ //義原以大/小寫英文字母開始,或者是具體詞--單獨在小括號里,屬於其他義原,加入str2。40是"("的ASCII值 str2+=sem+","; } else{ //關系符號義原,加入str4 str4+=sem+","; } } } //把str2中的第一條取出來,賦給str1 string::size_type pos3=str2.find(","); if(pos3!=string::npos){ str1=str2.substr(0,pos3+1); str2.erase(0,pos3+1); } set=str1+"\n"+str2+"\n"+str3+"\n"+str4; } bzero(sql,sizeof(sql)); sprintf(sql,"insert into t_gloss values(%d,\'%s\',\'%s\',\'%s\')",recordid++,word.c_str(),pos.c_str(),set.c_str()); rc=sqlite3_exec(db,sql,0,0,&zErrMsg); assert(rc==SQLITE_OK); } ifs.close(); //在“概念”上建立索引。以后要經常依據“概念”進行查詢 bzero(sql,sizeof(sql)); sprintf(sql,"create index index1 on t_gloss(concept)"); rc=sqlite3_exec(db,sql,0,0,&zErrMsg); assert(rc==SQLITE_OK); sqlite3_close(db); return 0; }
similary.cpp
#include<iostream> #include<fstream> #include<sstream> #include<string> #include<cstring> #include<cassert> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<sqlite3.h> using namespace std; const int vec_len=1618; //一共vec_len個基本義原 const double alpha=1.6; //計算基本義原相似度時的參數 const double beta1=0.5; //4種描述式相似度的權值 const double beta2=0.2; const double beta3=0.17; const double beta4=0.13; //const double delta=0.2; //const double gama=0.2; class myclass{ public: int index; string sememe; int parent; myclass(){}; myclass(int i,string sem,int p):index(i),sememe(sem),parent(p){} //重載關系運算符是為了使用STL中的find()函數 inline bool operator == (const myclass & m){ return sememe.compare(m.sememe)==0; } inline bool operator >(const myclass & m) const { return sememe.compare(m.sememe)>0; } inline bool operator <(const myclass & m) const { return sememe.compare(m.sememe)<0; } }; vector<myclass> semeVec(vec_len); //把基本義原從文件讀入vector void initSemVec(string filename){ ifstream ifs(filename.c_str()); assert(ifs); string line; while(getline(ifs,line)){ istringstream stream(line); int index,pind; string seme; stream>>index>>seme>>pind; myclass mc(index,seme,pind); semeVec[index]=mc; } ifs.close(); } //把用逗號分隔的string片段放到vector<string>中 static void splitString(string line,vector<string> &vec){ string::size_type pos1,pos2; pos1=0; while((pos2=line.find(",",pos1))!=string::npos){ string sem=line.substr(pos1,pos2-pos1); //把可能包含的一對小括號去掉 /*string::size_type pp=sem.find("("); if(pp!=string::npos){ sem.erase(pp,1); pp=sem.find(")"); sem.erase(pp,1); }*/ vec.push_back(sem); pos1=pos2+1; if(pos1>line.size()) break; } } //計算兩個基本義原的相似度 double calSimBase(string sem1,string sem2){ assert(sem1.size()>0 && sem2.size()>0); if(sem1[0]==40 ^ sem2[0]==40) //有一個是具體詞,而另一個不是 return 0; if(sem1[0]==40 && sem2[0]==40){ //如果兩個都是具體詞 if(sem1!=sem2) return 0.0; } if(sem1==sem2) return 1.0; cout<<"將要計算基本義原["<<sem1<<"]和["<<sem2<<"]的相似度"<<endl; stack<string> stk1,stk2; myclass mc1(0,sem1,0); myclass mc2(0,sem2,0); vector<myclass>::iterator itr=find(semeVec.begin(),semeVec.end(),mc1); if(itr==semeVec.end()){ cout<<"["<<sem1<<"]不在詞典中"<<endl; return 0; } //把sem1的路徑壓入棧中 stk1.push(sem1); int child=itr->index; int parent=itr->parent; while(child!=parent){ stk1.push(semeVec[parent].sememe); child=parent; parent=semeVec[parent].parent; } itr=find(semeVec.begin(),semeVec.end(),mc2); if(itr==semeVec.end()){ cout<<"["<<sem2<<"]不在詞典中"<<endl; return 0; } //把sem2的路徑壓入棧中 stk2.push(sem2); child=itr->index; parent=itr->parent; while(child!=parent){ stk2.push(semeVec[parent].sememe); child=parent; parent=semeVec[parent].parent; } if(stk1.top()!=stk2.top()){ cout<<"["<<stk1.top()<<"]和["<<stk2.top()<<"]是兩棵不同子樹的根"<<endl; return 0; } while(!stk1.empty() && !stk2.empty() && stk1.top()==stk2.top()){ stk1.pop(); stk2.pop(); } int dist=stk1.size()+stk2.size(); double result=alpha/(alpha+dist); cout<<result<<endl; return result; } //計算兩個基本關系義原的相似度 double calSimReal(string sem1,string sem2){ cout<<"將要計算關系義原["<<sem1<<"]和["<<sem2<<"]的相似度"<<endl; //如果整體是括在小括號里的,先把小括號去掉 if(sem1[0]==40){ sem1.erase(0,1); sem1.erase(sem1.size()-1,1); } if(sem2[0]==40){ sem2.erase(0,1); sem2.erase(sem2.size()-1,1); } string::size_type p1=sem1.find("="); string rela1=sem1.substr(0,p1); string::size_type p2=sem2.find("="); string rela2=sem2.substr(0,p2); if(rela1==rela2){ string base1=sem1.substr(p1+1); string base2=sem1.substr(p2+1); return calSimBase(base1,base2); } else return 0; } //計算第一獨立義原描述式的相似度 double calSim1(string line1,string line2){ if(line1=="" || line2=="") return 0; cout<<"將要計算第一獨立義原描述式["<<line1<<"]和["<<line2<<"]的相似度"<<endl; vector<string> vec1,vec2; splitString(line1,vec1); splitString(line2,vec2); assert(vec1.size()==1 && vec2.size()==1); return calSimBase(vec1[0],vec2[0]); } //計算其他獨立義原描述式的相似度 double calSim2(string line1,string line2){ if(line1=="" || line2=="") return 0; cout<<"將要計算其他獨立義原描述式["<<line1<<"]和["<<line2<<"]的相似度"<<endl; vector<double> maxsim_vec; vector<string> vec1,vec2; splitString(line1,vec1); splitString(line2,vec2); int len1=vec1.size(); int len2=vec2.size(); while(len1 && len2){ int m,n; double max_sim=0.0; for(int i=0;i<len1;++i){ for(int j=0;j<len2;++j){ double simil=calSimBase(vec1[i],vec2[j]); if(simil>max_sim){ m=i; n=j; max_sim=simil; } } } if(max_sim==0.0) break; maxsim_vec.push_back(max_sim); vec1.erase(vec1.begin()+m); vec2.erase(vec2.begin()+m); len1=vec1.size(); len2=vec2.size(); } //把整體相似度還原為部分相似度的加權平均,這里權值取一樣,即計算算術平均 if(maxsim_vec.size()==0) return 0.0; double sum=0.0; vector<double>::const_iterator itr=maxsim_vec.begin(); while(itr!=maxsim_vec.end()) sum+=*itr++; return sum/maxsim_vec.size(); } //計算關系義原描述式的相似度 double calSim3(string line1,string line2){ if(line1=="" || line2=="") return 0; cout<<"將要計算關系義原描述式["<<line1<<"]和["<<line2<<"]的相似度"<<endl; vector<double> sim_vec; vector<string> vec1,vec2; splitString(line1,vec1); splitString(line2,vec2); int len1=vec1.size(); int len2=vec2.size(); while(len1 && len2){ for(int j=0;j<len2;++j){ double ss=calSimReal(vec1[len1-1],vec2[j]); if(ss!=0){ sim_vec.push_back(ss); vec2.erase(vec2.begin()+j); break; } } vec1.pop_back(); len1=vec1.size(); len2=vec2.size(); } if(sim_vec.size()==0) return 0.0; double sum=0.0; vector<double>::const_iterator itr=sim_vec.begin(); while(itr!=sim_vec.end()) sum+=*itr++; return sum/sim_vec.size(); } //計算符號義原描述式的相似度 double calSim4(string line1,string line2){ if(line1=="" || line2=="") return 0; cout<<"將要計算符號義原描述式["<<line1<<"]和["<<line2<<"]的相似度"<<endl; vector<double> sim_vec; vector<string> vec1,vec2; splitString(line1,vec1); splitString(line2,vec2); int len1=vec1.size(); int len2=vec2.size(); while(len1 && len2){ char sym1=vec1[len1-1][0]; for(int j=0;j<len2;++j){ char sym2=vec2[j][0]; if(sym1==sym2){ string base1=vec1[len1-1].substr(1); string base2=vec2[j].substr(1); sim_vec.push_back(calSimBase(base1,base2)); vec2.erase(vec2.begin()+j); break; } } vec1.pop_back(); len1=vec1.size(); len2=vec2.size(); } if(sim_vec.size()==0) return 0.0; double sum=0.0; vector<double>::const_iterator itr=sim_vec.begin(); while(itr!=sim_vec.end()) sum+=*itr++; return sum/sim_vec.size(); } //計算兩個“概念”的相似度 double calConceptSim(string concept1,string concept2){ cout<<"將要計算概念["<<concept1<<"]和["<<concept2<<"]的相似度"<<endl; if(concept1[0]=='{'){ //概念1是虛詞 if(concept2[0]!='{'){ //概念2是實詞 return 0; } else{ //概念2是虛詞 string sem1=concept1.substr(1,concept1.size()-2); //去掉"{"和"}" string sem2=concept2.substr(1,concept2.size()-2); string::size_type p1=sem1.find("="); string::size_type p2=sem2.find("="); if(p1==string::npos ^ p2==string::npos){ //一個句法義原,一個是關系義原 return 0; } else if(p1==string::npos && p2==string::npos){ //都是句法義原 return calSimBase(sem1,sem2); } else{ //都是關系義原 return calSimReal(sem1,sem2); } } } else{ //概念1是實詞 if(concept2[0]=='{'){ //概念2是虛詞 return 0; } else{ //概念2是實詞 double sim1=0.0; //分別計算4種描述式的相似度 double sim2=0.0; double sim3=0.0; double sim4=0.0; string::size_type pos11,pos12,pos21,pos22; pos11=pos21=0; for(int i=0;i<4;++i){ pos12=concept1.find("\n",pos11); pos22=concept2.find("\n",pos21); string sem1=concept1.substr(pos11,pos12-pos11); string sem2=concept2.substr(pos21,pos22-pos21); switch(i){ case 0: sim1=calSim1(sem1,sem2); break; case 1: sim2=calSim2(sem1,sem2); break; case 2: sim3=calSim3(sem1,sem2); break; case 3: sim4=calSim4(sem1,sem2); break; default: break; } pos11=pos12+1; pos21=pos22+1; } //4部分的加權和作不整體的相似度 return beta1*sim1+ beta2*sim1*sim2+ beta3*sim1*sim2*sim3+ beta4*sim1*sim2*sim3*sim4; } } } //select回調函數 static int select_callback(void *output_arg,int argc,char *argv[],char *azColName[]){ vector<string> *vec=(vector<string> *)output_arg; string rect(argv[0]); vec->push_back(rect); return 0; } //計算兩個詞語的相似度 double calWordSim(string word1,string word2,sqlite3 *db){ cout<<"將要計算詞語["<<word1<<"]和["<<word2<<"]的相似度"<<endl; char *zErrMsg=0; int rc; vector<string> vec1,vec2; //兩個詞語的概念分別存放在vec1和vec2中 char sql[100]={0}; sprintf(sql,"select semset from t_gloss where concept=\'%s\'",word1.c_str()); rc=sqlite3_exec(db,sql,select_callback,&vec1,&zErrMsg); assert(rc==SQLITE_OK); sprintf(sql,"select semset from t_gloss where concept=\'%s\'",word2.c_str()); rc=sqlite3_exec(db,sql,select_callback,&vec2,&zErrMsg); assert(rc==SQLITE_OK); int len1=vec1.size(); int len2=vec2.size(); if(len1==0) cout<<word1<<"不在詞典中"<<endl; if(len2==0) cout<<word2<<"不在詞典中"<<endl; double maxsim=0.0; for(int i=0;i<len1;++i){ for(int j=0;j<len2;++j){ double sim=calConceptSim(vec1[i],vec2[j]); if(sim>maxsim) maxsim=sim; } } return maxsim; } int main(int argc,char *argv[]){ if(argc<3){ cerr<<"Usage:command word1 word2."<<endl; return 0; } string fn("whole.dat"); initSemVec(fn); sqlite3 *db; char *zErrMsg=0; int rc; rc=sqlite3_open("glossary.db",&db); //打開數據庫 assert(rc==SQLITE_OK); string word1(argv[1]); string word2(argv[2]); double sim=calWordSim(word1,word2,db); cout<<"["<<word1<<"]和["<<word2<<"]的相似度是"<<sim<<endl; sqlite3_close(db); return 0; } /* int main(){ ifstream ifs("glossary.dat"); vector<string> vecstr; string line; while(getline(ifs,line)){ istringstream stream(line); string word; stream>>word; vecstr.push_back(word); } ifs.close(); string fn("whole.dat"); initSemVec(fn); sqlite3 *db; char *zErrMsg=0; int rc; rc=sqlite3_open("glossary.db",&db); //打開數據庫 assert(rc==SQLITE_OK); int len=vecstr.size(); double sim=0; for(int i=0;i<len;i++){ for(int j=0;j<i;++j){ sim=calWordSim(vecstr[i],vecstr[j],db); cout<<"["<<vecstr[i]<<"]和["<<vecstr[j]<<"]的相似度是"<<sim<<endl; } } sqlite3_close(db); return 0; }*/