第二次作业-个人项目
个人项目
Github链接:https://github.com/Linee128/paper-checker-system
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience |
|---|---|
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience/homework/13468 |
| 这个作业的目标 | 完成第一个个人项目,熟悉项目开发流程 |
一、PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) |
|---|---|---|
| Planning | 计划 | 20 |
| Estimate | 估计这个任务需要多少时间 | 10 |
| Development | 开发 | 200 |
| Analysis | 需求分析(包括学习新技术) | 40 |
| Design Spec | 生成设计文档 | 25 |
| Design Review | 设计复审 | 20 |
| Coding Standard | 代码规范(为目前的开发制定合适的规范) | 15 |
| Design | 具体设计 | 50 |
| Coding | 具体编码 | 100 |
| Code Review | 代码复审 | 20 |
| Test | 测试(自我测试,修改代码,提交修改) | 100 |
| Reporting | 报告 | 30 |
| Test Report | 测试报告 | 20 |
| Size Measurement | 计算工作量 | 10 |
| Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 30 |
二、模块接口的设计与实现过程
1、模块划分
本系统采用模块化设计,共包含3个核心类和1个测试类:
PaperChecker(主程序类):程序入口,负责协调文件处理和相似度计算流程
FileHandler(文件处理类):封装文件读写操作,提供统一的文件访问接口
SimilarityCalculator(相似度计算类):实现编辑距离算法,提供文本相似度计算功能
PaperCheckerTest(测试类):验证各模块功能正确性
2、类与类的关系

PaperChecker是主控类,依赖于FileHandler和SimilarityCalculator。FileHandler和SimilarityCalculator各自独立,遵循单一职责原则。PaperCheckerTest测试时可直接调用FileHandler、SimilarityCalculator,也能间接验证PaperChecker。
3、关键函数流程图

4、关键算法
编辑距离计算法

先计算两个文本的编辑距离(将 text1 转换为 text2 所需的最少插入、删除和替换操作次数),再进行归一化处理:similarity = 1 - editDistance / maxLength
(其中 maxLength 是两个文本长度的最大值),确保相似度在 [0.0, 1.0] 范围内。
5、独到之处
(1)空间优化的编辑距离算法:采用滚动数组替代传统的二维数组,将空间复杂度从 O (n*m) 降至 O (min (n,m)),特别适合处理大文本比较。
(2)完善的边界处理:妥善处理 null 和空字符串输入,确保相似度计算结果严格在 [0.0, 1.0] 范围内;异常处理机制保证文件操作的稳定性。
(3)接口设计的高内聚低耦合:文件操作与算法计算完全分离,便于单独扩展;统一的异常处理机制,便于上层调用。
三、性能分析与改进
性能分析图


缺点:String、byte[]、HashMap/节点、int[] 的实例数极高,表明存在大量短寿命对象和集合节点;内存使用稳定但偏高(~230MB),GC有波动但无严重Full GC,说明瓶颈是大量对象分配与内存使用而不是单方法CPU瓶颈。
改进:(1)MinHash:用固定长度(例如 128 或 256)的一组哈希签名近似表示一篇文档或段落的 n-gram 集合。签名长度远小于原始集合大小,显著减小内存(减少 HashMap/String 节点与 byte[] 数量)。
(2)LSH(局部敏感哈希):把 MinHash 签名分成若干 band,快速把可能相似的文档对筛出来,避免在候选空间上做昂贵的精确比较(避免 O(N^2) 全比较)。
(3)可变/多尺度 n-gram 窗口 k:小 k(比如 3)能捕获局部相似(更敏感,对抄袭改写鲁棒);但会产生更多 n-gram(集合更大);大 k(比如 7)更保守,集合更小,对任意改变更敏感;使用多尺度(例如同时用 k=3、k=5)对结果进行融合,能在速度与准确度之间取得平衡:先用小 k 的 MinHash + LSH 快速召回候选,再用大 k 或精确相似度(如 Jaccard 或 Levenshtein)对候选复核。
四、部分单元测试
测试覆盖率


测试样例说明
| 测试函数 | 测试目的 | 说明 |
|---|---|---|
testCalculateSimilarityWithSameText |
完全相同文本检测 | 两个字符串内容完全一致,验证相似度计算函数返回 100%(即相似度值为 1.0)。 |
testCalculateSimilarityWithDifferentText |
完全不同文本检测 | 原文和待检测文本内容无关联,验证相似度计算函数返回较低相似度(小于 0.8)。 |
testCalculateSimilarityWithModifiedText |
部分相似文本检测 | 原文经过部分改写,存在部分语义和表述相似性,验证相似度计算函数返回中间范围相似度(0.5 到 1.0 之间)。 |
testCalculateSimilarityWithEmptyText |
原文为空检测 | 原文为空字符串,待检测文本非空,验证相似度计算函数返回 0%(即相似度值为 0.0)。 |
testCalculateSimilarityWithBothEmptyText |
两个空字符串检测 | 原文和待检测文本均为空字符串,验证相似度计算函数返回 100%(即相似度值为 1.0)。 |
testGetEditDistanceWithSameText |
相同文本编辑距离检测 | 两个完全相同的字符串,验证编辑距离计算函数返回 0。 |
testGetEditDistanceWithDifferentText |
不同文本编辑距离检测 | 两个有差异的字符串(如“kitten”和“sitting”),验证编辑距离计算函数返回合理的编辑距离值(如示例中返回 3)。 |
testReadFileWithValidPath |
有效文件路径读取检测 | 对存在且内容已知的文件,验证文件读取函数能正确读取文件内容。 |
testReadFileWithInvalidPath |
无效文件路径读取检测 | 对不存在的文件路径,验证文件读取函数会抛出 IOException 异常。 |
testWriteFileWithValidPath |
有效文件路径写入检测 | 向有效文件路径写入指定相似度值,验证文件写入函数能成功写入且内容正确(如写入 0.85,文件内容为“0.85”)。 |
五、异常处理说明
1、文件不存在
传入了错误的文件路径(例如 invalid_path.txt),或运行环境无权限读文件时,readFile 捕获底层IO错误并向上抛出IOException,main在外层捕获并打印错误信息/返回非 0状态。
@Test(expected = IOException.class)
public void testReadFileWithInvalidPath() throws IOException {
FileHandler fileHandler = new FileHandler();
fileHandler.readFile("this_path_should_not_exist_12345.txt");
}
2、空输入
输入文件为空、或者 readFile 返回空字符串时,函数入口对空串做显式判断并返回固定值。
@Test
public void testCalculateSimilarityWithBothEmptyText() {
SimilarityCalculator calc = new SimilarityCalculator();
double result = calc.calculateSimilarity("", "");
assertEquals(1.0, result, 0.01);
}
@Test
public void testCalculateSimilarityWithEmptyText() {
SimilarityCalculator calc = new SimilarityCalculator();
double result = calc.calculateSimilarity("", "今天是星期天,天气晴。");
assertEquals(0.0, result, 0.01);
}
3、超长输入
一次性比较多 MB / GB 的文本,可能导致长时间运行甚至内存耗尽。此时,在 calculateSimilarity 开头做长度检查,如果超过阈值(可配置,如 MAX_LEN=5_000_000),抛出受检异常 CalculationException 并提示分段或使用近似算法。
@Test(expected = CalculationException.class)
public void testCalculateSimilarityWithTooLargeInput() throws CalculationException {
SimilarityCalculator calc = new SimilarityCalculator();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 20000; i++) sb.append('a'); // 假设测试环境阈值较小
calc.calculateSimilarity(sb.toString(), "short");
}
六、PSP实际花费时间
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 20 | 20 |
| Estimate | 估计这个任务需要多少时间 | 10 | 10 |
| Development | 开发 | 200 | 240 |
| Analysis | 需求分析(包括学习新技术) | 40 | 50 |
| Design Spec | 生成设计文档 | 25 | 30 |
| Design Review | 设计复审 | 20 | 15 |
| Coding Standard | 代码规范(为目前的开发制定合适的规范) | 15 | 20 |
| Design | 具体设计 | 50 | 45 |
| Coding | 具体编码 | 100 | 110 |
| Code Review | 代码复审 | 20 | 15 |
| Test | 测试(自我测试,修改代码,提交修改) | 100 | 120 |
| Reporting | 报告 | 30 | 30 |
| Test Report | 测试报告 | 20 | 15 |
| Size Measurement | 计算工作量 | 10 | 10 |
| Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 30 | 40 |
| 总计 | 690 | 730 |
浙公网安备 33010602011771号