水每石帝

第一次个人编程作业

论文查重项目

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class56-Grade2024-CS
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class56-Grade2024-CS/homework/15693
这个作业的目标 让我们熟悉软件开发的流程

GitHub 仓库:https://github.com/csxy-boy/csxy

一、PSP 表格(开发前预估)

PSP2.1 Personal Software Process Stages 预估耗时(分钟)
Planning 计划 20
· Estimate · 估计这个任务需要多少时间 20
Development 开发 300
· Analysis · 需求分析(包括学习新技术) 50
· Design Spec · 生成设计文档 30
· Design Review · 设计复审 20
· Coding Standard · 代码规范 15
· Design · 具体设计 45
· Coding · 具体编码 90
· Code Review · 代码复审 20
· Test · 测试(自我测试,修改代码,提交修改) 40
Reporting 报告 70
· Test Report · 测试报告 30
· Size Measurement · 计算工作量 15
· Postmortem & Process Improvement Plan · 事后总结,并提出过程改进计划 25
合计 390

二、计算模块接口的设计与实现过程

1. 代码组织

项目采用 Maven 标准目录结构,按职责拆分为三个包:

  • com.papercheck.core:核心算法。包含 PaperSimilarityLcsSimilarityNgramSimilarityTextNormalizer
  • com.papercheck.io:输入输出。包含 CommandLineParserFileTextReaderAnswerWriter
  • com.papercheck.exception:自定义异常。包含 PaperCheckException 及四个子类。

Main 是唯一入口,负责把参数解析、文件读取、查重计算、答案写入串起来。这样的分层使核心算法不依赖命令行,便于单元测试。

2. 关键类与关系

Main
 ├─ CommandLineParser     校验 3 个命令行参数
 ├─ FileTextReader        读取原文和抄袭版文件
 ├─ PaperSimilarity       调度核心算法
 │   ├─ TextNormalizer    去除空白、标点并统一英文大小写
 │   ├─ LcsSimilarity     小文本:字符级 LCS + Dice 系数
│   └─ NgramSimilarity   大文本:字符 n-gram 的余弦相似度
 └─ AnswerWriter          写入两位小数的答案文件

3. 算法关键

程序先把中文标点和空白去掉,只保留字母和数字,使格式差异不影响结果。

对于普通长度文本,使用字符级最长公共子序列。设原文长度为 n、抄袭文本长度为 m,LCS 长度为 k,则:

相似度 = 2k / (n + m)

使用 Dice 系数而不是 k / max(n, m),能同时惩罚插入和删除带来的长度变化。动态规划用滚动数组实现,空间复杂度由 O(n*m) 降为 O(min(n,m))

n*m 超过阈值时,LCS 的双重循环会变慢,此时自动切换到字符二元组、三元组的余弦相似度,保证大文件也能在 5 秒内完成。

4. 独到之处

  • 核心算法与文件 IO 分离,PaperSimilarity 可直接被测试。
  • 使用 Unicode 码点而不是 char,对非 BMP 字符也能正确处理。
  • 设置大文本兜底策略,兼顾准确度和运行时间。
  • 自定义异常统一继承 PaperCheckException,入口只做一次捕获,错误信息清晰。

三、计算模块接口部分的性能改进

改进前

最初直接使用二维数组 dp[n+1][m+1] 实现 LCS,空间占用大,且大文件容易超时。

改进思路

  1. 用两个一维滚动数组替代二维数组,减少内存分配。
  2. 把较短的文本作为列,进一步降低空间占用。
  3. 设置 MAX_EXACT_PRODUCT = 500_000_000,超过后切换到线性时间的 n-gram 算法。

性能分析图

image

消耗最大的函数

从 Call Tree 可以看出,小文本场景下最耗时的是:

com.papercheck.core.LcsSimilarity.longestCommonSubsequenceLength

大文本场景下最耗时的是:

com.papercheck.core.NgramSimilarity.ngrams

实测结果

orig.txt 为原文,对提供的 5 个抄袭版文件运行 main.jar,答案如下:

抄袭版文件 重复率
orig_0.8_add.txt 0.91
orig_0.8_del.txt 0.89
orig_0.8_dis_1.txt 0.97
orig_0.8_dis_10.txt 0.84
orig_0.8_dis_15.txt 0.67

四、计算模块部分单元测试展示

项目共 26 个单元测试,覆盖核心算法、参数校验、文件读取、答案写入和异常处理,满足“至少 10 个测试用例”的要求。

核心算法测试节选:

class PaperSimilarityTest {

    @Test
    void modifiedSampleShouldHaveHighSimilarity() {
        double value = similarity.compute(
                "今天是星期天,天气晴,今天晚上我要去看电影。",
                "今天是周天,天气晴朗,我晚上要去看电影。");
        assertTrue(value > 0.7 && value <= 1.0, "期望得到较高相似度,实际为 " + value);
    }

    @Test
    void oneEmptyTextShouldThrow() {
        assertThrows(EmptyTextException.class, () -> similarity.compute("今天天气好", ""));
    }
}

测试数据构造思路:

  • 相同文本、完全无关文本、大小写差异、标点差异,验证边界和归一化。
  • 固定 LCS 长度的字符串 ABCBDAB / BDCABA,验证算法正确性。
  • 超长文本触发 n-gram 兜底分支。
  • 缺失文件、目录作为答案路径、空参数等,验证异常路径。

覆盖率截图

覆盖率截图

五、计算模块部分异常处理说明

1. CommandLineArgumentException

设计目标:当参数数量不是 3 个,或存在空路径时,阻止程序继续执行。

测试样例:

@Test
void tooFewArgumentsShouldThrow() {
    assertThrows(CommandLineArgumentException.class,
            () -> CommandLineParser.validate(new String[]{"C:/tests/orig.txt"}));
}

对应场景:用户漏传抄袭版文件路径或答案文件路径。

2. FileAccessException

设计目标:原文或抄袭版文件不存在、无权限、路径是目录时,给出明确错误,而不是抛出难以理解的底层异常。

测试样例:

@Test
void missingFileShouldThrowFileAccessException() {
    assertThrows(FileAccessException.class,
            () -> FileTextReader.read(tempDir.resolve("not-exist.txt")));
}

对应场景:输入文件路径拼写错误或文件已被删除。

3. AnswerWriteException

设计目标:答案路径无法创建或不可写时,明确报告写入失败。

测试样例:

@Test
void directoryAsTargetShouldThrowAnswerWriteException() {
    assertThrows(AnswerWriteException.class, () -> AnswerWriter.write(tempDir, 0.5));
}

对应场景:把目录误当作答案文件,或答案目录没有写权限。

4. EmptyTextException

设计目标:其中一个输入文件规范化后没有任何字母或数字时,提示无法比较。

测试样例:

@Test
void oneEmptyTextShouldThrow() {
    assertThrows(EmptyTextException.class, () -> similarity.compute("今天天气好", ""));
}

对应场景:文件中只有标点、空格,或者文件为空。

六、附录:PSP 表格(实际耗时)

PSP2.1 Personal Software Process Stages 实际耗时(分钟)
Planning 计划 15
· Estimate · 估计这个任务需要多少时间 20
Development 开发 320
· Analysis · 需求分析(包括学习新技术) 60
· Design Spec · 生成设计文档 25
· Design Review · 设计复审 25
· Coding Standard · 代码规范 20
· Design · 具体设计 40
· Coding · 具体编码 80
· Code Review · 代码复审 30
· Test · 测试(自我测试,修改代码,提交修改) 40
Reporting 报告 75
· Test Report · 测试报告 25
· Size Measurement · 计算工作量 20
· Postmortem & Process Improvement Plan · 事后总结,并提出过程改进计划 30
合计 410

七、项目总结

本项目把论文查重拆分为参数校验、文件 IO、文本归一化、相似度计算、答案写入五个可独立测试的部分。算法上先用字符级 LCS 保证准确性,再通过滚动数组和大文本 n-gram 兜底保证性能,单元测试覆盖了正常路径和四类异常路径。
d12d441b0c742b59a2e4842805b7d220

070bf3b54e7e0ffcfc173d42d8660ade

posted on 2026-09-13 14:50  水每石帝  阅读(11)  评论(0)    收藏  举报

导航