NLP—WordNet——词与词之间的最小距离
WordNet,是由Princeton 大学的心理学家,语言学家和计算机工程师联合设计的一种基于认知语言学的英语词典。它不是光把单词以字母顺序排列,而且按照单词的意义组成一个“单词的网络”。我们这次的任务就是求得词与词之间的最短路径,是对“图”这个数据结构再次灵活运用。
以下为SentiWordNet_3.0.0_20130122.txt文件截图:
应考虑如何存储“单词的网络”,此程序是以词作为基本单元,词与词之间的联系是通过语义。
我们简单地构造类(ListofSeg存储词的语义id):
class C_word { public string word; public List<int> ListofSeg = new List<int>(); public C_word(string a) { ListofSeg = new List<int>(); word = a; } }
程序大约分成两部分:
第一部分,将“语义-词-词-...-词”格式转换成“词-语义-语义-...-语义”格式;
第二部分,构造单词网络,通过广度遍历寻找最小距离。
(运行程序的过程中发现第一部分耗时较长,担心调试第二部分时花费额外无用时间,故将第一部分结果生成文本;第二部分通过读文本来获得信息,而不是再执行一遍第一部分:),第一次将中期结果生成文本存储,觉得要这方法不错,节省大量时间!)
PART1:
using System.Collections.Generic; using System.Text; using System.IO; namespace WordNet { class Program { static void Main(string[] args) { List<C_word> ListofCword = new List<C_word>(); //读取文件 string dicpath = @"C:\Users\Lian\Desktop\SentiWordNet_3.0.0_20130122.txt"; StreamReader sr = new StreamReader(dicpath, Encoding.Default); //我们用行号作为词的语义id标识,而没有使用wordnet文本中的语义id int sen_count = 0; string line; //将所有的词以及它的语义编号存在ListofCword中 while ((line = sr.ReadLine()) != null) { //选择需要的信息 if(line[0]!='#') { //通过制表符'\t'分割line string[] a = line.Split('\t'); //将我们需要的词拿出来 string[] b = a[4].Split(' '); if(b.Length>1) { //c来存储这一行中的词 string[] c = new string[b.Length]; //去掉'#' for (int i = 0; i < b.Length; i++) { int tip = b[i].IndexOf('#'); c[i] = b[i].Substring(0, tip); } //将c[]的词存入ListofCword中 for (int j = 0; j < c.Length; j++) { //向ListofCword中存储第一个WORD if (ListofCword.Count == 0) { C_word tempword = new C_word(c[j]); ListofCword.Add(tempword); ListofCword[0].ListofSeg.Add(sen_count); } else { //用来判断这个词是否出现在ListofCword中 bool e = true; //遍历整个ListofCword,查找词是否出现 for (int i = 0; i < ListofCword.Count; i++) //若出现,则存储行号(即语义id)至这个词的ListofSeg中,并且跳出循环。 if (ListofCword[i].word == c[j]) { ListofCword[i].ListofSeg.Add(sen_count); e = false; break; } //若这个词在整个ListofCword中没有出现,则将此词加入ListofCword中 if (e) { C_word tempword = new C_word(c[j]); ListofCword.Add(tempword); ListofCword[ListofCword.Count - 1].ListofSeg.Add(sen_count); } } } } } //行号(语义id)++ sen_count++; } //将ListofCword存储至文件中,方便以后使用。 string path = @"C:\Users\Lian\Desktop\wordnet.txt"; FileStream fs = new FileStream(path, FileMode.Create); StreamWriter sw = new StreamWriter(fs); for (int i = 0; i < ListofCword.Count; i++) { sw.Write(ListofCword[i].word); for (int j = 0; j < ListofCword[i].ListofSeg.Count; j++) sw.Write("\t" + ListofCword[i].ListofSeg[j]); sw.Write("\r\n"); } //清空缓冲区 sw.Flush(); //关闭流 sw.Close(); fs.Close(); } } }
----------------------------------------------------------------------------------
PART2:
using System; using System.Collections.Generic; using System.Text; using System.IO; namespace WordNet_Next { class Program { static void Main(string[] args) { List<C_word> ListofCword = new List<C_word>(); string path = @"C:\Users\Lian\Desktop\wordnet.txt"; StreamReader sr = new StreamReader(path, Encoding.Default); String line; while ((line = sr.ReadLine()) != null) { string[] temp = line.Split('\t'); C_word tempword = new C_word(temp[0]); for (int i = 1; i < temp.Length; i++) tempword.ListofSeg.Add(int.Parse(temp[i])); ListofCword.Add(tempword); } string worda="dog", wordb="robot"; //需要查询的 List<int> ListofSearch = new List<int>(); //已查询过的 List<int> ListofHaved = new List<int>(); //临时存放的 List<int> ListofTemp = new List<int>(); //初始化工作 int worda_index, wordb_index; for (worda_index = 0; worda != ListofCword[worda_index].word; worda_index++) ; for (wordb_index = 0; wordb != ListofCword[wordb_index].word; wordb_index++) ; //把worda的语义id存入ListofSearch,ListofHaved,ListofTemp中 for (int i = 0; i < ListofCword[worda_index].ListofSeg.Count; i++) { ListofSearch.Add(ListofCword[worda_index].ListofSeg[i]); ListofHaved.Add(ListofCword[worda_index].ListofSeg[i]); ListofTemp.Add(ListofCword[worda_index].ListofSeg[i]); } int distance = -1; Boolean Searched = false; while(true) { distance++; //判断wordb中是否拥有Temp列表中的语义 for(int apple=0;apple<ListofCword[wordb_index].ListofSeg.Count;apple++) for(int pear=0;pear<ListofTemp.Count;pear++) { if(ListofCword[wordb_index].ListofSeg[apple]==ListofTemp[pear]) { Searched = true; break; } } //若有,则退出循环,准备输出Distance if (Searched) break; //清空搜索列表 ListofSearch.Clear(); //将需要搜索的临时列表添加入搜索列表 for (int apple = 0; apple < ListofTemp.Count; apple++) ListofSearch.Add(ListofTemp[apple]); //清空临时列表 ListofTemp.Clear(); //用来判断judge在整个ListofCword中,哪个词拥有在search的语义 for (int apple = 0; apple < ListofCword.Count; apple++) { //用来判断一个词是否拥有任何一个在search的语义 Boolean judge = false; //循环一个词的全部语义 for (int pear = 0; pear < ListofCword[apple].ListofSeg.Count; pear++) { //循环要search的语义 for (int orange = 0; orange < ListofSearch.Count; orange++) //如果匹配上了,那么就直接跳出两层循环 if (ListofCword[apple].ListofSeg[pear] == ListofSearch[orange]) { judge = true; break; } if (judge) break; } //如果这个词拥有search的语义,那么将这个词的语义存入temp队列中(若在Haved队列中,则不存储) //否则,直接判断下一个词 if (judge) { //循环一个词的全部语义 for (int pear = 0; pear < ListofCword[apple].ListofSeg.Count; pear++) { Boolean judge_temp = true; //循环Haved中已有的语义 for (int orange = 0; orange < ListofHaved.Count; orange++) { //判断这个词的某一个语义是否存在于Haved队列中,若存在,则跳出一层循环,继续判断这个词的另一语义 if (ListofHaved[orange] == ListofCword[apple].ListofSeg[pear]) { judge_temp = false; break; } } //若这个词的这个语义不存在于Haved队列中,则存入Temp队列以及Haved队列中 if (judge_temp) { ListofTemp.Add(ListofCword[apple].ListofSeg[pear]); ListofHaved.Add(ListofCword[apple].ListofSeg[pear]); } } } } if (ListofTemp.Count == 0) { Console.WriteLine("UNREACHABLE!"); break; } } Console.WriteLine(distance); } } }
程序本身,有很多细节可以优化,有些偷懒,没有耐心去思考如何可以更快地解决词与词之间最小距离问题。
如有建议,请尽情指点!
PS:第二部分一气呵成,没有调试一次。。。一次成功,感到惊奇,故留念一下:)
小LiAn
2017/4/15夜
作者: 小LiAn
出处: http://www.cnblogs.com/cnlian/>
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接 如有问题, 可邮件(671266128@qq.com)咨询.