个人项目
这个作业属于哪个课程 | 网工1934-软件工程 |
---|---|
这个作业要求在哪里 | 个人项目 |
这个作业的目标 | <学习如何做一个软件项目,了解项目各流程,学会如何进行编程、测试、性能分析等来完成一个初步软件,同时继续学习如何写好一个博客,熟悉掌握github的使用。> |
github
一、项目需求
题目:论文查重
描述如下:
设计一个论文查重算法,给出一个原文文件和一个在这份原文上经过了增删改的抄袭版论文的文件,在答案文件中输出其重复率。
原文示例:今天是星期天,天气晴,今天晚上我要去看电影。
抄袭版示例:今天是周天,天气晴朗,我晚上要去看电影。
要求输入输出采用文件输入输出,规范如下:
从命令行参数给出:论文原文的文件的绝对路径。
从命令行参数给出:抄袭版论文的文件的绝对路径。
从命令行参数给出:输出的答案文件的绝对路径。
我们提供一份样例,课堂上下发,上传到班级群,使用方法是:orig.txt是原文,其他orig_add.txt等均为抄袭版论文。
注意:答案文件中输出的答案为浮点型,精确到小数点后两位
二、项目开发流程分析
(一)开发环境
1、编程语言:java
2、项目集成工具:Intellij IDEA 2019.3.4
3、项目框架工具:maven
4、单元测试工具:JUnit4
5、性能分析工具:JProfiler 12.0.3.0
(二)实现算法原理
SimHash算法原理:SimHash也即相似hash,是一类特殊的信息指纹,常用来比较文章的相似度,与传统hash相比,传统hash只负责将原始内容尽量随机的映射为一个特征值,并保证相同的内容一定具有相同的特征值。而且如果两个hash值是相等的,则说明原始数据在一定概率下也是相等的。此原理实现分以下几步:
(1)分词:把需要判断文本分词形成这个文章的特征单词,例如:“用” “同情” “的” “目光” “看待” “世界”
(2)计算hash值:此项目使用MD5加密的方式让每个分词生成长度固定的0,1字符串
(3)加权:按照分词的权重进行加权,例如对100101,权重为4,则加权计算为4-4-4 4-4 4
(4)合并:把各个分词算出来的序列值累加,变成只有一个序列串
(5)降维形成SimHash值:例如 8-8-8 8-8 8,正数记为1,负数记为0,即100101
(三)整体流程图
(四)项目框架及主要类
主要maven框架
maven框架配置pom.xml配置
配置依赖包
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.hankcs</groupId>
<artifactId>hanlp</artifactId>
<version>portable-1.5.4</version>
</dependency>
</dependencies>
(五)项目模块核心算法
(1)文件读写模块fileIO类
(2)simHash类(分词、获取hash值、加权、合并、降维)
分词
List<String> kwList = HanLP.extractKeyword(s,s.length());//提取关键词
加权、合并、降维
for (int j = 0; j < val.length ; j++) {
if(kwHash.charAt(j) == '1'){
val[j] += (10-(i/(len/10)));
}else {
val[j] -= (10-(i/(len/10)));
}
}
i++;
}
//降维获取simHash值
String simhash = "";
for (int j = 0; j <val.length ; j++) {
if(val[j]<0){
simhash += "0";
}else {
simhash += "1";
}
}
(3)获取海明距离getHamming类(求出差异位数、求重合率)
return (100 - len * 100 /128)*0.01;
(4)主模块mainDupCheck类
通过命令行传参或者idea配置参数传参,分别传入原始文件、抄袭文件、结果文件,,读取路径名,将内容转化为字符串,调用上述类,分别求出simHash值,再求海明距离及重合率,将结果写入结果文件中。
三、项目性能分析
概览
实时内存
由上图可知,运行时调用com.hankcs.hanlp包的次数较多,主要是核心算法的实现,加载此依赖包是项目必须的,其余加载次数少,符合性能分析标准。
四、单元测试
部分模块单元测试代码
(一)主模块测试mainCheckTest类
将测试文件依次读取,与原始文件进行比较,分别得出最终重合率,将结果写入结果文件
部分测试代码
String[] s = new String[6];
s[0] = filesIO.readFile("D:\\Test2\\orig.txt");
s[1]= filesIO.readFile("D:\\Test2\\orig_0.8_add.txt");
s[2]= filesIO.readFile("D:\\Test2\\orig_0.8_del.txt");
s[3]= filesIO.readFile("D:\\Test2\\orig_0.8_dis_1.txt");
s[4] = filesIO.readFile("D:\\Test2\\orig_0.8_dis_10.txt");
s[5]= filesIO.readFile("D:\\Test2\\orig_0.8_dis_15.txt");
String finallyAllTest = "D:\\Test2\\finallyAll.txt";
for (int i = 0; i < s.length ; i++) {
double result = getHamming.getCoincidence(simHash.getSimHash(s[0]),simHash.getSimHash(s[i]));
filesIO.writeFile(result,finallyAllTest);
}
测试说明:主要是将5个抄袭文件与原始文件进行查重对比,最后输出重合率,写入结果文件,若每一项测试都合理通过,则测试完成。
测试点
(二)读取文件测试filesIOTest类
部分测试代码
@Test
public void readFileTest(){
String s = filesIO.readFile("D:\\Test2\\orig.txt");
String[] strings = s.split(" ");
for(String str : strings){
System.out.println(str);
}//测试路径正常,读取
}
测试说明:主要是测试文件读取功能,是否能正常读取文件路径,将内容转化为字符串,此处以原始文件路径作为测试样例。
测试点
覆盖率
(三)simHash测试simHashTest类
部分测试代码
@Test
public void getHashTest(){
String[] strings = {"用","同情","的","目光","看待","世界"};
for(String str : strings){
String strHash = simHash.getHash(str);
System.out.println(strHash.length());
System.out.println(strHash);
}
}
测试说明:这部分主要是测试分词及求得的simHash值是否正确,此处用文中一句话进行测试,这部分代码是分词后的测试,事先将句子分好,作为参数测试此函数。
测试点
覆盖率
(四)海明距离测试getHammingTest类
部分测试代码
@Test
public void getHammingLenTest(){
String s = filesIO.readFile("D:\\Test2\\orig.txt");
String s1 = filesIO.readFile("D:\\Test2\\orig_0.8_add.txt");
int len = getHamming.hammingValue(simHash.getSimHash(s),simHash.getSimHash(s1));
System.out.println("Hamming距离为:"+len);
System.out.println("论文重合率为:"+(100-len*100/128)+"%");
}//对hammingValue()方法的测试
测试说明:主要是测试求差异位数和重合率的方法,这部分代码以添加抄袭文件为例,调用上述方法,求出海明距离和重合率。
测试点
覆盖率
结果文件
五、异常处理说明
为防止读取路径的文件内容过短,设置异常处理类filesException类
主要代码:
public class filesException extends Exception {
public filesException() {
super();
}
public filesException(String message) {
super(message);
}
public filesException(String message, Throwable cause) {
super(message, cause);
}
public filesException(Throwable cause) {
super(cause);
}
}
测试代码
@Test
public void filesExceptionTest(){
System.out.println(simHash.getSimHash("用同情看待世界"));
}//测试当字符串长度小于180的情况
测试点
覆盖率
测试结果
六、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 60 |
Estimate | 估计这个任务需要多少时间 | 50 | 50 |
Development | 开发 | 1220 | 1395 |
Analysis | 需求分析 (包括学习新技术) | 360 | 420 |
Design Spec | 生成设计文档 | 60 | 65 |
Design Review | 设计复审 | 30 | 30 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
Design | 具体设计 | 80 | 90 |
Coding | 具体编码 | 300 | 360 |
Code Review | 代码复审 | 60 | 80 |
Test | 测试(自我测试,修改代码,提交修改) | 300 | 320 |
Reporting | 报告 | 130 | 135 |
Test Repor | 测试报告 | 60 | 60 |
Size Measurement | 计算工作量 | 10 | 15 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 60 | 60 |
合计 | 1460 | 1640 |