深圳大学研究生课程论文题目大作业:变位词实验成绩专业计算机与软件学院软件工程课程名称、代码年级2015 姓名文成学号2150230509 时间2015 年12 月任课教师杨烜一、大作业要求与内容大作业内容:在下列问题中挑选一个问题,选用适当的算法进行实现,在课堂上,针对该问题完成一个10分钟的论文演讲与演示,并提交演讲PPT。
(30分)在一个类似英语词典的大文件中找出变位词的所有集合,例如,tea和eat是变位词,同属一个集合,找出所有这种集合。
大作业要求:(70分)(1)要求演示算法解决问题的完整过程,如果我对解这个问题一无所知,看了你的解决过程,就要能理解算法是如何解决问题的;(2)要求交互界面活泼生动,演示速度可控;(3)尽可能提供丰富的功能让我理解你是如何解决这个问题的;(4)提交源程序、大作业报告(介绍详细的算法设计说明和使用说明);(5)以论文、报告等形式考核专用答题纸写大作业,大作业报告中要分析算法效率,并给出实测效率和理论效率图表;(6)大作业用5号字体,总页数不得少于8页,否则视为无效。
二、大作业步骤Introduction:给定一本英语单词词典,找出所有的变位词集。
所谓的变位词是指,组成各个单词的字母完全相同,只是字母排列的顺序不同。
例如,tea和eat是变位词,同属一个集合,找出所有这种集合。
Motivating idea:1.如何判断两个单词是否为变位词。
思路一:如果两个单词是变位词,那么它们具有相同的长度,且每个英语字母的个数是一样的。
我们只需要挨个对各个单词进行比较即可。
这个思路容易想,但时间效率太低,还可以继续改进一下,且看下面的思路二。
思路二:将两个字符串按照字母表顺序排序,看排序后的字符串是否相等,如果相等则是兄弟字符串(变位词)。
这种方法的时间效率根据你使用的排序算法不同而不同。
这里我采取思路二,我使用的是快速排序。
但是依旧有个问题,单词与单词一个一个比较的话效率还是太低了,我们可以再做改进。
2.如何从字典中找出所有变位词的集合。
思路一:对于这个问题,最快想到的最直接的方法就是针对每一个单词跟字典中的其他单词进行比较。
然而,假设一次比较至少花费1微秒的时间,则拥有二十万单词的字典将花费:200000单词 x 200000比较/单词 x 1微秒/比较 = 40000x10^6秒 = 40000秒≈ 11.1小时。
比较的次数太多了,导致效率低下,我们需要找出效率更高的方法。
思路二:标识字典中的每一个单词,使得在相同变位词类中的单词具有相同的的标识,然后集中具有相同标识的单词。
所以我们要做的就是将每个单词按照字母表排序,排序后得到的字符串作为该单词的标识。
再按照排序后的字符串再对单词进行一次快速排序,使得同位词都集中在一起,接下来只需要对这些同位词进行整合就可以了。
下面给出我解决这个问题的方法。
the way to solve the problem:在《编程珠玑》书中就有部分提到了变位词程序的设计思路,我的解决步骤与它大同小异。
但考虑到词典现实情况的特殊性,我在实现的过程中还进行了很多改进和处理,经过改进后,我的程序不仅可以读入词典的索引,也可以读入完整的词典,还可以读入英语文章。
如果考虑单词中字母的所有可能排列,那算法会出奇的低效和复杂。
实际上,每个变位词相当于一个等价类。
算法的关键是要选择一个标识来标识这个等价类,使得在相同变位词类中的单词具有相同的标识。
然后,将所有相同标识的单词集中在一起。
我们可以使用基于排序的标识(可见要使用排序技术),将单词中的字母按照字母表排序。
我的变位词程序按照三个步骤来执行,其中前一个步骤程序的输出作为下一个步骤程序的输入。
(预处理:程序读入字典文件,去除无用符号和阿拉伯数字,找出一个不重复的只含单词的列表)第一步:读入字典文件,对单词进行排序得到标识第二步:将所有的单词按照其标识的顺序排序第三步:程序将这些单词整理为每个变位词类一行的形式由于问题的规模不大,采取面向过程的方法解决问题更简单。
我依次使用以下三个方法来解决问题。
Sign方法是对每个单词进行标识,reorder方法是对标识后的单词重新排序,arrange方法就是一个整合的过程,具有相同标识的单词就是互为变位词。
●sign();对每个输入的单词进行标识,每个单词的标识就是依照字母进行排序后的串,在这里我使用的是快速排序。
●reorder();对标识后的单词,按照字典序进行排序,使得具有相同标识的单词都排在一起。
要区分的是,这里是对单词间的排序,而上一步是对单词中的字母的排序。
●arrange();对之前的结果进行整合,具有相同标识符的单词都挨着。
后面只需要将同一个变位词类中的各个单词放到同一行中。
Example:下面是简单的例子,仅有6个单词的字典的处理过程对这六个单词tea apple ate said dais eatAlgorithm:这里只提供算法思路的代码,实际在实现源程序中还做了很多特殊情况的处理。
void sign()//程序标识单词{ifstream dataFile(inPath); //字典文件ofstream ofs(outPath+"step1.txt",ios::out);//标识单词后的输出路径vector<string> str; //用于逐行扫描单词vector<string> vstr; //用于存放不重复单词if (!dataFile.is_open()){cout<<"Error opening file!"<<endl; //读文件失败则输出错误信息exit(-1);}while(!dataFile.eof()){string line,word;getline(dataFile,line); //逐行读入数据istringstream is(line);str.push_back(line);if(!ofs) exit(-1);while (is >> word) //只要是个单词就把它提取出来,然后接着扫描{{;}//把读入的单词全部转换为小写vstr.push_back(word);{;}//删除容器中的重复字符串}}for (int i = 0; i < vstr.size(); i++)//逐个标识单词并写入文件{string original=vstr[i];//定义一个original字符串,用来存储原单词sort(vstr[i].begin(), vstr[i].end());//对单词按字母顺序快速排序ofs<<vstr[i]<<" ";//输出标识ofs<<original<<endl;//输出原单词}ofs.close();dataFile.close();}而对于Void reorder();就是对标识后的单词,按照字典序进行排序,使得具有相同标识的单词都排在一起。
我是用快速排序对这些标识进行排序,下面给出一个快速排序的算法:int Compare(string s1, string s2)//按字典序比较两个单词的顺序{string::size_type s1_len = s1.size();string::size_type s2_len = s2.size();int i = 0;while (i < s1_len && i < s2_len){if (s1[i] < s2[i]){return -1;}else if (s1[i]>s2[i]){return 1;}i++;}if (i < s1_len)return 1;if (i < s2_len)return -1;return 0;}void QuickSort(vector<string> &s, int p, int r)//快速排序,对一系列的单词按字典序排序{if (p<r){int i = p ;int j = r;string x = s[p];while (i<j){while (i < j && Compare(s[j], x) == 1)j--;if (i < j)s[i++] = s[j];while (i < j && Compare(s[i], x) == -1)i++;if (i < j)s[j--] = s[i];}s[i] = x;QuickSort(s, p, i - 1);QuickSort(s, i + 1, r);}}最后是一个整理的过程。
对之前的结果进行整合,具有相同标识符的单词都挨着。
后面只需要将同一个变位词类中的各个单词放到同一行中。
void arrange()//程序将这些单词整理为每个变位词类一行的形式{ifstream dataFile(outPath+"step2.txt"); //将sign方法的输出作为这个函数的输入ofstream ofs(outPath+"变位词集合.txt",ios::out);//把变位词的集合输出到这个文件中vector<string> str; //用于逐行扫描单词int flag=1;string wordset; //每次读入单词的标识,都把这个标识放进wordset if (!dataFile.is_open()){cout<<"Error opening file!"<<endl; //读文件失败则输出错误信息exit(-1);}while(!dataFile.eof()){string line,word;getline(dataFile,line);istringstream is(line); //逐行读入数据str.push_back(line);if(!ofs) exit(-1);while(is >> word) //只要是个单词,就把它取出来比较{ //后面需要判断是原单词还是单词的标识if(flag==1){wordset=word; //第一次读入的时候把第一个单词的标识存给wordsetofs<<wordset<<": "; //wordset就作为没一行的开头,也就是一个等价类的标识,然后再输出一个:flag++;continue;}else{if(flag%2==0){ofs<<word<<" "; //把相同标识的单词放到同一行flag++;continue;}else{if (wordset==word) //如果读入得标识没有改变,那么wordset不做变化{flag++;continue;}else//如果读入的标识改变了,那么wordset=word{ofs<<" "<<endl;wordset=word;ofs<<wordset<<": ";flag++;continue;}}}}}ofs.close();dataFile.close();Implementation:实现使用面向过程的方法,用C++实现,详细的代码见附件中的cpp文件。