第一次个人编程作业
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class78-Grade2024-CS |
|---|---|
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class78-Grade2024-CS/homework/15702 |
| 这个作业的目标 | 完成个人项目:论文查重 |
代码仓库: https://github.com/RettoST/Retto/
1.概述
- 1.项目名称: 论文查重
- 2.语言:python3
- 3.运行方式:
python main.py <原文文件> <抄袭版文件> <结果文件>
- 目标:给定原文文件和抄袭版论文文件,计算重复率,并将结果写入结果文件,保留两位小数
2. 需求分析
2.1 功能需求
- 1.从命令行读取三个参数:原文路径、抄袭版路径、答案输出路径
- 2.读取原文与抄袭文件内容
- 3.计算文件重复率
- 4.将重复率写入答案文件,保留两位小数
- 5.异常处理(空文件、路径不存在等)
2.2 其他需求
- 1.不能出现内存泄漏严重
- 2.五秒内未给出答案
- 3.占用的内存超过2048MB
- 4.不能发生异常退出
- 5.尝试连接网络、尝试读写其他文件或影响使用
- 6.代码的可读性(注释),变量、函数、类命名的规范化
3. 总体设计分析
3.1 模块划分
计算模块
├── 文件读取
├── 文本预处理
├── 相似度计算
└── 结果输出
3.2 核心函数
| 函数名 | 所在文件 | 功能 | 输入 | 输出 |
|---|---|---|---|---|
read_file(path) |
src/plagiarism.py |
读取文件内容 | 文件路径 | 文本字符串 |
normalize_text(text) |
src/plagiarism.py |
去除空白字符 | 原始文本 | 规范化文本 |
calc_similarity(orig, plag) |
src/plagiarism.py |
计算相似度 | 两段文本 | 浮点数0.00~1.00 |
main() |
main.py |
调用计算模块 | 无 | 无 |
4. 核心算法
4.1 算法选择
本程序使用 Python 标准库 difflib.SequenceMatcher 计算相似度
对于大文本(超过5w字符)采用 n-gram + Jaccard 进行优化
SequenceMatcher 相似度公式:
similarity = 2 * M / (len(orig) + len(plag))
其中:
M:两段文本匹配的字符数;len(orig):原文长度;len(plag):抄袭版长度。
n-gram 就是连续的 n 个字符,以 n = 4 为例,对文本今天天气很好切分:
今天天气
天天气很
天气很好
每个片段就是一个 4-gram。
把所有 4-gram 收集起来,放进一个集合(自动去重)
Jaccard 算法相似度公式:
J(A, B) = |A ∩ B| / |A ∪ B|
其中:
-
A ∩ B:两个集合共有的碎片数量; -
A ∪ B:两个集合合并后一共有多少种不同碎片; -
结果范围是 0 到 1。
4.2 算法流程
5. 异常处理设计
考虑到特殊编码导致的读取混乱的错误,只对常见编码进行读取(UTF-8与GBK),若无法读取则强制以UTF-8进行读取
在main中对参数进行异常处理,并对异常打印输出
6. 性能
1.测试用例
常规文本测试:常规文本的抄袭修改
古诗测试:涵盖原文与译文的测试
大文本测试:20w字的大幅测试
2.测试结果
常规文本与古诗测试均正常输出结果,时间极短

而大文本测试使用大约一分钟,需要优化

即使是大文本的内存占用依旧很小,不考虑内存优化

6.1 目标
- 运行时间 < 5 秒
- 内存占用 < 2048MB
6.2 性能瓶颈
观察对大文本查重的cProfile得到是 calc_similarity 中的计算相似度算法使用时间过高
6.3 优化思路
网上查找得知 difflib.SequenceMatcher 的时间复杂度是 O(n2)
查找到算法 n-gram 与 Jaccard 复杂度为 O(n)
短文本用 difflib 保证精度,长文本(5w)使用 n-gram + Jaccard,复杂度从 O(n2) 降到 O(n)
具体优化思路详见4.1算法选择
6.4 性能测试结果
由图可得满足了对大文本的时间优化

7.测试
非常幽默的,官方测试用例的orig_0.8_del与全部的orig_0.8_dis部分全部都是以html发送的,并不是实际的文本内容,这样会导致相似度奇低无法正常测试,在源代码中已经对这部分纰漏进行修改并测试。
为了节省篇幅,不放出具体测试的截图:
| 测试文件 | 相似度 | 实际用时(ms) |
|---|---|---|
| orig_0.8_add.txt | 0.91 | 172.939 |
| orig_0.8_del.txt | 0.87 | 175.8819 |
| orig_0.8_dis_1.txt | 0.97 | 78.9586 |
| orig_0.8_dis_10.txt | 0.81 | 115.2905 |
| orig_0.8_dis_15.txt | 0.58 | 155.4837 |
单元测试得到的测试覆盖率截图

测试用例的编写主要是实际需求的测试,以及基本上完全地测试算法的所有情况(因为实际需求的编写问题,覆盖率好像不能正确计算)
测试部分花费特别特别多时间
附录
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 15 | 20 |
| · Estimate | · 估计这个任务需要多少时间 | 15 | 20 |
| Development | 开发 | 250 | 181 |
| · Analysis | · 需求分析 (包括学习新技术) | 20 | 10 |
| · Design Spec | · 生成设计文档 | 30 | 30 |
| · Design Review | · 设计复审 | 20 | 8 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 20 |
| · Design | · 具体设计 | 30 | 32 |
| · Coding | · 具体编码 | 60 | 48 |
| · Code Review | · 代码复审 | 30 | 18 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 30 | 15 |
| Reporting | 报告 | 90 | 75 |
| · Test Report | · 测试报告 | 30 | 20 |
| · Size Measurement | · 计算工作量 | 30 | 10 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 105 |
| 合计 | 355 | 336 |
浙公网安备 33010602011771号