作业信息
作业属于哪个课程 | 软件工程https://edu.cnblogs.com/campus/gdgy/CSGrade22-12 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-12/homework/13220 |
这个作业的目标 | 准备、创建、开发、管理、测试个人项目 |
1.PSP表格
2.题目描述
题目:论文查重
描述如下:
设计一个论文查重算法,给出一个原文文件和一个在这份原文上经过了增删改的抄袭版论文的文件,在答案文件中输出其重复率。
3.算法实现基本思路
3.1simHash算法原理
1.分词:现在有很多可供使用的包来进行文本的分词,本篇所使用的分词器是IKAnalysis,需要安装 IKAnalyzer2012_u6.jar包
2.hash:通过hash函数计算各个特征向量(这里为划分好的词)的hash值,hash值为二进制数01组成的n-bit签名。
3.加权:权重:就是词频;把第2步生成的hash值从左至右与权重进行运算;如果该bit的数值为1,则将权重赋给该位;如果该bit的数值为0,则将权重的负值赋给该位。example:“我”,hash = 101011,weight(词频) = 5;则加权后的结果为:5 -5 5 -5 5 5;
4.合并:经过上述的三个步骤,我们可以得到全部词(word)的加权hash值,此时需要将全部的加权后的hash值进行累加;
5.降维:将第四步计算出来的序列串变为01串;具体规则:如果该位的数值>0,则置为1;反之则置为0.
6.海明距离(Hamming Distance):在信息编码中,两个合法代码对应位上编码不同的位数称为码距,又称海明距离;
3.2余弦定理查找相似度
(1)找出两篇文章的关键词;
(2)每篇文章各取出若干个关键词,合并成一个集合,计算每篇文章对于这个集合中的词的词频
(3)生成两篇文章各自的词频向量;
(4)计算两个向量的余弦相似度,值越大就表示越相似。
4.具体代码设计部分
4.1 设计顶层接口,选用一种算法作为实现类
由于计算论文相似度、重复度的算法较多,这里也只采用了一种方法,即simhash+海明距离。因此我首先定义一个抽象接口DuplicateCheck接口,作为论文查重多种实现算法都要继承的父接口,接口里定义了一个抽象方法:String getSimilarity(String originalStr, String plagiarizeStr),用于让多种算法的实现类来实现,方便于将来扩展以及将来更换算法实现论文查重。
4.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;
}
4.3 代码设计的优点
1、针对这一种Simhash+海明距离的算法的实现方法进行了代码封装,使得易于调用。
2、同时,定义了父接口,也为将来进行代码水平扩展,其他算法的试验进行了准备,将来只需要更换实现类即可,实现了开闭原则,即对修改关闭,对扩展开放。
5.计算模块接口部分的性能改进
5.1 JProfiler分析
程序中消耗最大的函数:
是计算长文本的simHash值的函数
5.2 性能改进策略:
1.simhash用于比较大文本,适用于论文查重,但是如果是小文本,则应该更换算法。
2.使用多线程技术。
6.计算模块部分单元测试展示
6.1 整体功能测试
public class TestPaperSelect {
private DuplicateCheck check = new SimHashAndHammingImpl();
/**
* 测试不同的文件
*/
@Test
public void testdifferent() {
List
// 源文件
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
6.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
6.3 单元测试覆盖率
7.计算模块部分异常处理说明
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字!");
}
在出现错误的传参个数或校验文件不符合要求时,抛出异常。
7.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.
at com.hwg.PaperSelect.main(PaperSelect.java:13)
7.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.
at com.hwg.PaperSelect.main(PaperSelect.java:21)