1.作业的基本信息
作业属于课程 |
|
---|---|
作业的要求 |
|
作业的目标 |
2、使用PSP表格记录项目完成流程 3、有质量地完成论文查重项目 4、编写单元测试 5、对项目做性能测试分析与改进 |
2.作业地址
gitcode仓库地址:
https://gitcode.net/weixin_55642001/3120005470-paper/-/tree/master/
3.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 15 | 20 |
· Estimate | · 估计这个任务需要多少时间 | 1000 | 750 |
Development | 开发 | 200 | 160 |
· Analysis | · 需求分析 (包括学习新技术) | 120 | 130 |
· Design Spec | · 生成设计文档 | 30 | 25 |
· Design Review | · 设计复审 | 30 | 40 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
· Design | · 具体设计 | 90 | 100 |
· Coding | · 具体编码 | 200 | 150 |
· Code Review | · 代码复审 | 40 | 40 |
· Test | · 测试(自我测试,修改代码,提交修改) | 180 | 185 |
Reporting | 报告 | 150 | 165 |
· Test Repor | · 测试报告 | 60 | 65 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 25 |
· 合计 | 1000 | 800 |
4.算法的设计与实现过程
设计思想概述以及实现流程:实现流程基于Simhash和海明距离计算simhash相似度
4.1 分词
需要判断文本分词,形成这个文章的特征单词。最后形成去掉噪音词的单词序列并为每个词加上权重,我们假设权重分为5个级别(1~5)。
比如:“ 美国“51区”雇员称内部有9架飞碟,曾看见灰色外星人 ” ==> 分词后为 “ 美国(4) 51区(5) 雇员(3) 称(1) 内部(2) 有(1) 9架(3) 飞碟(5) 曾(1) 看见(3) 灰色(4) 外星人(5)”,括号里是代表单词在整个句子里重要程度,数字越大越重要。
4.2 hash
通过hash算法把每个词变成hash值,比如“美国”通过hash算法计算为 100101,“51区”通过hash算法计算为 101011。这样我们的字符串就变成了一串串数字,要把文章变为数字计算才能提高相似度计算性能,现在是降维过程进行时。
4.3 加权
通过 4.2步骤的hash生成结果,需要按照单词的权重形成加权数字串,比如“美国”的hash值为“100101”,通过加权计算为“4 -4 -4 4 -4 4”;“51区”的hash值为“101011”,通过加权计算为 “ 5 -5 5 -5 5 5”。
4.4 合并
把上面各个单词算出来的序列值累加,变成只有一个序列串。比如 “美国”的 “4 -4 -4 4 -4 4”,“51区”的 “ 5 -5 5 -5 5 5”, 把每一位进行累加, “4+5 -4±5 -4+5 4±5 -4+5 4+5” ==> “9 -9 1 -1 1 9”。这里作为示例只算了两个单词的,真实计算需要把所有单词的序列串累加。
4.5 降维
把4.4步骤算出来的 “9 -9 1 -1 1 9” 变成 0 1 串,形成我们最终的simhash签名。 如果每一位大于0 记为 1,小于0 记为 0。最后算出结果为:“1 0 1 0 1 1”。
4.6 通过海明距离计算simhash的相似度
现在通过这样的转换,文本都转换为simhash 代码,如何计算两个simhash的相似度呢?我们通过海明距离(Hamming distance)就可以计算出两个simhash到底相似不相似。两个simhash对应二进制(01串)取值不同的数量称为这两个simhash的海明距离。举例如下: 10101 和 00110 从第一位开始依次有第一位、第四、第五位不同,则海明距离为3。对于二进制字符串的a和b,海明距离为等于在a XOR b运算结果中1的个数(普遍算法)。
5.具体代码设计部分
5.1 设计顶层接口,选用一种算法作为实现类
由于计算论文相似度、重复度的算法较多,这里也只采用了一种方法,即simhash+海明距离。因此我首先定义一个抽象接口DuplicateCheck接口,作为论文查重多种实现算法都要继承的父接口,接口里定义了一个抽象方法:String getSimilarity(String originalStr, String plagiarizeStr),用于让多种算法的实现类来实现,方便于将来扩展以及将来更换算法实现论文查重。
5.2 核心代码:获取simhash和海明距离得到相似度
在这定义一个子实现类SimHashAndHammingImpl类实现上述所说的DuplicateCheck接口,
核心是实现的方法 String getSimilarity(String originalStr, String plagiarizeStr)
方法内容如下:核心逻辑即为获取两个长文本的simhash以及计算海明距离得到相似度。
@Override
public String getSimilarity(String originalStr, String plagiarizeStr) {
//由字符串得出对应的simHash值
String orSimHash = getSimHash(originalStr);
String plSimHash = getSimHash(plagiarizeStr);
//由simHash值求出相似度
String similarity = gotSimilarity(orSimHash, plSimHash);
return similarity;
}
其中调用了同样定义在本实现类的方法:getSimHash方法和gotSimilarity方法,在这里不列出源代码赘述了。
5.3 代码设计的优点
1、针对这一种Simhash+海明距离的算法的实现方法进行了代码封装,使得易于调用。
2、同时,定义了父接口,也为将来进行代码水平扩展,其他算法的试验进行了准备,将来只需要更换实现类即可,实现了开闭原则,即对修改关闭,对扩展开放。
6.计算模块接口部分的性能改进
6.1 JProfiler分析
概览图
程序中消耗最大的函数:
是计算长文本的simHash值的函数
6.2 性能改进策略:
1、simhash用于比较大文本,适用于论文查重,但是如果是小文本,则应该更换算法。
2、使用多线程技术。
7.计算模块部分单元测试展示
7.1 整体功能测试:采用老师任务里的样例。
public class TestPaperSelect {
private DuplicateCheck check = new SimHashAndHammingImpl();
/**
* 测试不同的文件
*/
@Test
public void testdifferent() {
List<String> plagiar = new ArrayList<>();
// 源文件
String originalStr = FileUtil.readUtf8String("C:\\Users\\26411\\Desktop\\test\\orig.txt");
plagiar.add("C:\\Users\\26411\\Desktop\\test\\orig_0.8_add.txt");
plagiar.add("C:\\Users\\26411\\Desktop\\test\\orig_0.8_del.txt");
plagiar.add("C:\\Users\\26411\\Desktop\\test\\orig_0.8_dis_1.txt");
plagiar.add("C:\\Users\\26411\\Desktop\\test\\orig_0.8_dis_10.txt");
plagiar.add("C:\\Users\\26411\\Desktop\\test\\orig_0.8_dis_15.txt");
String plagiarizeStr = null; //抄袭文本
String similarity = null; //相似度
for (String str : plagiar) {
plagiarizeStr = FileUtil.readUtf8String(str);
similarity = check.getSimilarity(originalStr, plagiarizeStr);
System.out.println(str + " ==>" + similarity);
}
}
}
结果如下:
C:\Users\26411\Desktop\test\orig_0.8_add.txt ==>0.82
C:\Users\26411\Desktop\test\orig_0.8_del.txt ==>0.75
C:\Users\26411\Desktop\test\orig_0.8_dis_1.txt ==>0.77
C:\Users\26411\Desktop\test\orig_0.8_dis_10.txt ==>0.73
C:\Users\26411\Desktop\test\orig_0.8_dis_15.txt ==>0.61
7.2 测试核心逻辑求解simhash值
@Test
public void testSimhash() {
String originalStr = FileUtil.readUtf8String("C:\\Users\\26411\\Desktop\\test\\orig.txt");
String plagiarizeStr = FileUtil.readUtf8String("C:\\Users\\26411\\Desktop\\test\\orig_0.8_add.txt");
String orSimHash = check.getSimHash(originalStr);
String plSimHash = check.getSimHash(plagiarizeStr);
System.out.println(" orSimHash => "+orSimHash);
System.out.println(" plSimHash => "+plSimHash);
}
结果如下:
orSimHash => 11110000111111010111111100110101010011010110011001111011001010000111110100000010000101011100011010100000111110100100111111110000
plSimHash => 11110001011111000111110001100011010010010010010101111011001010000011111100000010010101010100011010100110110111110100111010110000
7.3 单元测试覆盖率截图:
8.计算模块部分异常处理说明
public class Error {
//错误的传参个数
public static final Exception BadArgsException = new RuntimeException("错误的传参个数!");
//文件校验
public static final Exception BadFileException = new RuntimeException("文件格式不正确!");
public static final Exception LengthException = new RuntimeException("文本长度需多于300字!");
}
在出现错误的传参个数或校验文件不符合要求时,抛出异常。
8.1 测试错误的传参个数
当命令行传参个数少于3个时,会抛出异常 BadArgsException
public class TestError {
private DuplicateCheck check = new SimHashAndHammingImpl();
@Test
public void testBadArgsException(){
String[] args = new String[2];
args[0] = "C:\\Users\\26411\\Desktop\\test\\orig.txt";
args[1] = "C:\\Users\\26411\\Desktop\\test\\orig_0.8_add.txt";
PaperSelect.main(args);
}
}
结果如下:
java.lang.RuntimeException: 错误的传参个数!
at com.hwg.common.Error.<clinit>(Error.java:5)
at com.hwg.PaperSelect.main(PaperSelect.java:13)
8.2 测试文件格式
当文件格式有误时,会抛出异常 BadFileException
@Test
public void testBadFileException(){
String[] args = new String[3];
args[0] = "C:\\Users\\26411\\Desktop\\test\\orig.txy";
args[1] = "C:\\Users\\26411\\Desktop\\test\\orig_0.8_add.txz";
args[2] = "C:\\Users\\26411\\Desktop\\test\\ans.txw";
PaperSelect.main(args);
}
结果如下:
java.lang.RuntimeException: 文件格式不正确!
at com.hwg.common.Error.<clinit>(Error.java:8)
at com.hwg.PaperSelect.main(PaperSelect.java:21)