个人项目
| <网工1234> | |
|---|---|
| 作业要求: 1.在Github仓库中新建一个学号为名的文件夹。 2.在开始实现程序之前,在PSP表格记录下你估计在程序开发各个步骤上耗费的时间,在你实现程序之后,在PSP表格记录下你在程序的各个模块上实际花费的时间。 3.语言不限,运行环境为64-bit Windows 10。需将编译好的程序发布到Github仓库中的releases中。 4.提交的代码要求经过Code Quality Analysis工具的分析并消除所有的警告。 5.完成项目的首个版本之后,请使用性能分析工具Studio Profiling Tools来找出代码中的性能瓶颈并进行改进。 6.使用Github来管理源代码和测试用例,代码有进展即签入Github。签入记录不合理的项目会被助教抽查询问项目细节。 7.使用单元测试对项目进行测试,并使用插件查看测试分支覆盖率等指标;写出至少10个测试用例确保你的程序能够正确处理各种情况。 |
<作业要求> |
| 作业目标: 设计一个论文查重算法,给出一个原文文件和一个在这份原文上经过了增删改的抄袭版论文的文件,在答案文件中输出其重复率。 |
|
| GitHub链接 |
1. PSP 表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | ||
| Estimate | 估计这个任务需要多少时间 | 1300 | 1690 |
| Development | 开发 | ||
| Analysis | 需求分析 (包括学习新技术) | 300 | 500 |
| Design Spec | 生成设计文档 | 120 | 100 |
| Design Review | 设计复审 | 40 | 30 |
| Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 20 |
| Design | 具体设计 | 200 | 400 |
| Coding | 具体编码 | 300 | 400 |
| Code Review | 代码复审 | 100 | 90 |
| Test | 测试(自我测试,修改代码,提交修改) | 100 | 60 |
| Reporting | 报告 | ||
| Test Repor | 测试报告 | 30 | 30 |
| Size Measurement | 计算工作量 | 30 | 30 |
| Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 50 | 30 |
| 合计 | 1300 | 1690 | |
2.模块接口设计与实现过程
2.1主要实现类
主类:
1.Application ,包含main方法,可以接收指定参数,传递给DocumentOperation类进行文件的输入输出,并调用其他工具类中的方法实现程序的运行。
工具类:
1.DocumentOperation,文件操作工具类,控制文件的操作,包括文件的输入输出。
2.WordCut,中文分词工具类,具有中文分词的方法。
3.SimilarityAnalysis,相似性分析工具类,其中包含通过余弦相似性进行相似性分析的方法。
测试类:
1.FunctionTest,测试各模块的功能是否能正常运行。
项目结构:

程序流程:

2.2关键方法的分析与实现
查重操作可分为以下四个步骤:读入文件,分词,查重,写入文件,其中分词和查重是难点。适当选择分词函数和相似度算法可有效提高查重效率和精确度。
编写程序前,查阅了许多资料,以下是参考的部分文章:
中文分词器的基本原理和简单实现
11大Java开源中文分词器的使用方法和分词效果对比
Java分布式中文分词组件 - word分词
计算文本相似度方法大全
TF-IDF与余弦相似性的应用(二):找出相似文章
经过考虑后,选择word分词器和余弦相似性算法来完成本次设计
假设有两个句子A,B
句子A:我喜欢跑步和打篮球,不喜欢游泳。
句子B:我不喜欢打篮球和游泳,也不喜欢踢足球。
第一步,分词。
句子A:我/喜欢/跑步/和/打/篮球,不/喜欢/游泳。
句子B:我/不/喜欢/打/篮球,也/不/喜欢/踢/足球。
实现:通过word中文分词器实现,即通过查询内置的词典进行分词
第二步,列出所有的词。
{"我","喜欢","跑步","和","打","篮球","不","游泳","也","踢","足球"}
第三步,计算词频。
句子A:我=1,喜欢=2,跑步=1,和=1,打=1,篮球=1,不=1,游泳=1,也=0,踢=0,足球=0。
句子B:我=1,喜欢=2,跑步=0,和=0,打=1,篮球=1,不=2,游泳=0,也=1,踢=1,足球=1。
第四步,写出词频向量。
句子A:[1, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0]
句子B:[1, 2, 0, 0, 1, 1, 2, 0, 1, 1, 1]
实现:可以通过遍历的方式得到所有的词并统计两个文本的词频
到这里,问题就变成了如何计算这两个向量的相似程度。
我们可以把它们想象成空间中的两条线段,都是从原点([0, 0, ...])出发,指向不同的方向。两条线段之间形成一个夹角,如果夹角为0°,意味着方向相同、线段重合;如果夹角为90°,意味着形成直角,方向完全不相似;如果夹角为180°,意味着方向正好相反。因此,我们可以通过夹角的大小,来判断向量的相似程度。夹角越小,就代表越相似。
则计算A, B向量间的夹角的余弦值
假定a向量是[x1, y1],b向量是[x2, y2],那么可以将余弦定理改写成下面的形式:

若从二维空间来看,则是

假定A和B是两个n维向量,A是 [A1, A2, ..., An] ,B是 [B1, B2, ..., Bn] ,则A与B的夹角θ的余弦等于:

余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似,因此可以通过余弦值来直观的了解到文本的相似性。
3.计算模块接口部分的性能改进
第一版程序使用的是word分词器1.3的版本,耗时较长:


第二版程序:在第一版程序中检查代码后发现相似性分析代码较冗余,于是对相似性代码进行了优化,耗时减少:


第三版程序:发现分词器自带的控制台输出日志占用时间太多,于是关闭了日志功能(不影响程序执行),同时将分词器版本下调至1.2:


第四版程序:在保证分析结果相似的前提下,再次将分词器的版本下调至1.0,以提高程序执行速度:


4.计算模块部分单元测试展示
4.1 WordCut 类的测试
测试代码:

测试结果:

有测试结果可以看出,word分词器可识别特殊符号,并将它们进行划分。
4.2 SimilarityAnalysis 类的测试
测试代码:


测试结果:

由结果可发现,当两个句子都为空串时,结果为NaN,因此需要对句子进行判空处理。
4.3代码覆盖率

5.模块部分异常处理说明
在单元测试模块中可知,当两个句子都为空时输出NaN,因此要进行异常处理,具体实现:
在Application类中添加句子的判空,如果都为空则输出提示信息并退出程序。

6.项目功能测试
当原文件和抄袭论文文件为UTF-8编码格式时测试代码为:
java -Dfile.encoding=utf-8 -jar CheckRepeat.jar 原文路径 抄袭文本路径 结果文本路径
当原文件和抄袭论文文件为ANSI编码格式时测试代码为:
java -jar CheckRepeat.jar 原文路径 抄袭文本路径 结果文本路径


结果文件:


浙公网安备 33010602011771号