第一次个人编程作业
GitHub仓库链接:https://github.com/Sleepy-li/3224004192
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class56-Grade2024-CS |
|---|---|
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class56-Grade2024-CS/homework/15693 |
| 这个作业的目标 | 写一个论文查重算法 |

| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) |
|---|---|---|
| Planning | 计划 | 300 |
| · Estimate | · 估计这个任务需要多少时间 | 300 |
| Development | 开发 | 305 |
| · Analysis | · 需求分析 (包括学习新技术) | 45 |
| · Design Spec | · 生成设计文档 | 15 |
| · Design Spec | · 设计复审 | 30 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 |
| · Design | · 具体设计 | 30 |
| · Coding | · 具体编码 | 120 |
| · Code Review | · 代码复审 | 20 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 15 |
| Reporting | 报告 | 70 |
| · Test Repor | · 测试报告 | 20 |
| · Size Measurement | · 计算工作量 | 20 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 |
| · 合计 | 375 |
计算模块接口的设计与实现过程:
1、代码组织,类与函数关系
该项目一共有4个Java类文件,分别是:
(1)SimHash类(核心算法):
作用:实现文本清洗、2-gram分词、simhash指纹计算、相似度计算。
cleanText(String text):文本清洗,过滤多余符号、空格,保留中文;返回清洗后的干净字符串。computeSimHash(String text):2‑gram 滑动窗口,生成 64 位 SimHash long 指纹。calcSimilarity(long hash1, long hash2):计算两个指纹海明距离,换算得到 0‑1 之间文本相似度。
(2)FileUtil类(文件工具类):
作用:负责磁盘文件读写。
readFile(String path):读取文本文件,读取失败返回 null。writeFile(String path, double rate):将相似度保留两位小数写入输出文件。
(3)PaperCheckMain类(程序入口主类):
作用:命令行程序入口,接受3个命令行参数:原文路径、待检测文本路径、输出结果路径;串联文本读取-文本清洗-simhash计算-结果写入全流程。
(4)SimhashTest类(JUnit单元测试类):
作用:对SimHash全部接口做单元测试,覆盖正常文本、空字符串、短文本、无关文本等边界场景。
调用流程:PaperCheckMain调用FileUtil读取两个文本 → 调用SimHash.cleanText()清洗文本 → 调用computeSimHash()生成两个 64 位指纹 → calcSimilarity()计算相似度 → FileUtil.writeFile()输出结果。
2、算法关键与独到之处
- 采用中文2-gram双字符滑动窗口做分词,不再使用单字符。中文语义依赖相邻汉字,2‑gram 可以抵抗字符调换、局部改写、少量增删,对于作业的
dis字符打乱测试集鲁棒性更强。 - 64 位 long 存储 simhash 指纹,计算海明距离直接位运算,运算效率高。
- 边界兼容:处理输入空字符串;当文本长度不足 2,自动退化为单字符统计权重,避免数组越界异常。
- 输出约束:相似度强制保留两位小数,输出文件只输出纯数字,无多余提示文字,满足作业命令行输出规范。
计算模块接口部分的性能改进:
计算模块部分单元测试展示:
单元测试覆盖率截图如下图所示,使用 EclEmma 工具采集代码覆盖率。
核心类SimHash.java行覆盖率 77.5%,FileUtil.java行覆盖率 85.7%,PaperCheckMain.java行覆盖率 77.8%。
单元测试覆盖空文本、短文本、完全相同文本、无关文本等边界场景,覆盖 SimHash 算法核心函数cleanText()、computeSimHash()、calcSimilarity(),保证算法模块逻辑正确性。
未覆盖代码主要是主类中文件读取失败、命令行参数异常的分支,这些属于 IO 异常分支,可单独构造用例触发。

计算模块部分异常处理说明:
| 异常场景 | 设计目标 | 对应处理逻辑 | 单元测试思路 |
|---|---|---|---|
| 文件路径错误、文件不存在 | 捕获读取文件异常,程序不直接崩溃,控制台打印错误提示 | FileUtil.readFile 捕获 IO 异常,返回 null;主类判断如果返回 null,打印 “读取文件失败” 直接 return 结束程序,不继续执行 simhash 计算 | 单元测试传入不存在的文件路径,验证返回 null |
| 命令行传入参数数量不等于 3 | 防止用户命令行参数传错,程序友好提示用法 | PaperCheckMain 判断args.length !=3,打印使用提示,return 退出 | 单元测试模拟 args 长度为 2,验证输出提示 |
| 输出目录没有写入权限,写文件失败 | 文件写入失败不抛出崩溃,控制台提示写入失败 | FileUtil.writeFile 捕获 IO 异常,返回 boolean;主类接收返回值,打印写入失败提示 | 单元测试传入无权限路径,验证返回 false |
| 输入文本为空字符串 | 空文本不产生数组越界,算法可以正常返回指纹 | computeSimHash 增加文本长度判断,文本不足 2 字符退化为单字符处理 | test5OneEmpty 单元测试,输入空字符串 |
| 测试组合 | 输出相似度 |
|---|---|
| orig.txt vs orig.txt | 1.00 |
| orig.txt vs orig_0.8_add.txt | 0.98 |
| orig.txt vs orig_0.8_del.txt | 0.83 |
| orig.txt vs orig_0.8_dis_1.txt | 0.83 |
| orig.txt vs orig_0.8_dis_10.txt | 0.83 |
| orig.txt vs orig_0.8_dis_15.txt | 0.83 |
最后的PSP表格:
| PSP2.1 | Personal Software Process Stages | 实际耗时(分钟) |
|---|---|---|
| Planning | 计划 | 300 |
| · Estimate | · 估计这个任务需要多少时间 | 300 |
| Development | 开发 | 415 |
| · Analysis | · 需求分析 (包括学习新技术) | 45 |
| · Design Spec | · 生成设计文档 | 20 |
| · Design Spec | · 设计复审 | 30 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 |
| · Design | · 具体设计 | 60 |
| · Coding | · 具体编码 | 120 |
| · Code Review | · 代码复审 | 35 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 45 |
| Reporting | 报告 | 125 |
| · Test Repor | · 测试报告 | 60 |
| · Size Measurement | · 计算工作量 | 20 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 45 |
| · 合计 | 540 |
浙公网安备 33010602011771号