Github 链接
https://github.com/xiao-ding1/ruanjiangongcheng/tree/main/3223004253
第一次个人编程作业
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13477 |
这个作业的目标 | 开发论文查重项目,了解工程开发流程和GitHub使用 |
PSP
PSP2.1 阶段 | Personal Software Process Stages(个人软件过程阶段) | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
· Planning | 计划 | 30 | 60 |
· Estimate | 估计这个任务需要多少开发时间 | 350 | 540 |
· Development | 开发 | 360 | 540 |
· Analysis | 需求分析(包括学习新技术) | 120 | 140 |
· Design Spec | 生成设计文档 | 30 | 30 |
· Design Review | 设计复审 | 10 | 20 |
· Coding Standard | 代码规范(为目前的开发制定合适的规范) | 10 | 20 |
· Design | 具体设计 | 50 | 70 |
· Coding | 具体编码 | 110 | 140 |
· Code Review | 代码复审 | 20 | 40 |
· Test | 测试(自我测试,修改代码,提交代码) | 20 | 40 |
· Reporting | 报告 | 40 | 40 |
· Test Repor | 测试报告 | 50 | 20 |
· Size Measurement | 计算工作量 | 5 | 10 |
· Postmortem & Process Improvement Plan | 事后总结 | 20 | 30 |
・合计 | 1225 | 1740 |
内容
一、模块接口的设计与实现
1.1 类结构设计
项目包含以下三个核心类
1.1 PlagiarismDetector(主控制类)
关键方法:
main(String[] args)
: 程序入口,处理命令行参数detectPlagiarism(String, String)
: 执行查重检测的主流程validateInputs(String, String, String)
: 验证输入参数的有效性
1.2 TextSimilarityCalculator(相似度计算器)
关键方法:
calculateComprehensiveSimilarity(String, String)
: 综合相似度计算calculateCosineSimilarity(String, String)
: 余弦相似度计算calculateEditDistanceSimilarity(String, String)
: 编辑距离相似度计算calculateCharacterSimilarity(String, String)
: 字符级相似度计算
1.3 FileProcessor(文件处理器)
关键方法:
readFile(String)
: 读取文件内容writeResult(String, double)
: 写入结果到文件fileExists(String)
: 检查文件是否存在
1.4 类关系图
classDiagramclass PlagiarismDetector {-FileProcessor fileProcessor-TextSimilarityCalculator similarityCalculator+main(String[] args)+detectPlagiarism(String, String) double-validateInputs(String, String, String)}class TextSimilarityCalculator {+calculateComprehensiveSimilarity(String, String) double+calculateCosineSimilarity(String, String) double+calculateEditDistanceSimilarity(String, String) double+calculateCharacterSimilarity(String, String) double+preprocessText(String) String+detectLanguage(String) TextLanguage}class FileProcessor {+readFile(String) String+writeResult(String, double)+fileExists(String) boolean+isValidPath(String) boolean+getFileSize(String) long}PlagiarismDetector --> FileProcessor : usesPlagiarismDetector --> TextSimilarityCalculator : uses
2. 关键算法实现
2.1 综合相似度计算流程
flowchart TDA[输入两个文本] --> B[文本预处理]B --> C[计算余弦相似度]B --> D[计算编辑距离相似度]B --> E[计算字符级相似度]C --> F[加权平均]D --> FE --> FF --> G[输出综合相似度]
validateInputs方法流程图
flowchart TDA[开始] --> B[验证原文文件路径有效性]B --> C{路径有效?}C -->|否| D[抛出IllegalArgumentException]C -->|是| E[验证抄袭版文件路径有效性]E --> F{路径有效?}F -->|否| G[抛出IllegalArgumentException]F -->|是| H[验证输出文件路径有效性]H --> I{路径有效?}I -->|否| J[抛出IllegalArgumentException]I -->|是| K[检查原文文件是否存在]K --> L{文件存在?}L -->|否| M[抛出IllegalArgumentException]L -->|是| N[检查抄袭版文件是否存在]N --> O{文件存在?}O -->|否| P[抛出IllegalArgumentException]O -->|是| Q[验证完成]Q --> R[结束]
2.2 余弦相似度算法
算法原理:
- 将文本转换为词频向量
- 计算两个向量的点积
- 计算向量的模长
- 相似度 = 点积 / (模长1 × 模长2)
2.3 编辑距离算法
算法原理:
- 使用动态规划计算Levenshtein距离
- 相似度 = 1 - (编辑距离 / 最大长度)
优势:
- 能够检测字符级别的变化
- 对词汇替换敏感
- 适合检测局部修改
2.4 最长公共子序列算法
算法原理:
- 使用动态规划计算LCS长度
- 相似度 = LCS长度 / 最大长度
优势:
- 能够检测文本结构相似性
- 对顺序变化不敏感
- 适合检测重排后的相似性
3. 算法独到之处
3.1 多算法融合
系统采用三种不同的相似度算法,通过加权平均的方式综合计算:
- 余弦相似度(权重50%):捕捉词汇频率信息
- 编辑距离相似度(权重30%):检测字符级变化
- 字符级相似度(权重20%):检测结构相似性
3.2 智能文本预处理
- 支持中英文混合文本处理
- 自动去除标点符号和特殊字符
- 保留中文字符、英文字母和数字
- 统一转换为小写格式
3.3 语言检测功能
系统能够自动检测文本语言类型:
- 中文文本
- 英文文本
- 混合文本
- 未知类型
4.4 鲁棒性设计
- 完善的参数验证
- 详细的异常处理
- 空文件检测
- 路径有效性验证
二、计算模块接口部分的性能改进
1. 改进时间记录
改进阶段 | 时间投入 | 主要改进内容 | 性能提升 |
---|---|---|---|
初始实现 | 2小时 | 基础算法实现 | 基准性能 |
第一轮优化 | 3小时 | 算法复杂度优化,早期终止 | 提升40% |
第二轮优化 | 2小时 | 数据结构优化,滚动数组 | 提升60% |
第三轮优化 | 1.5小时 | 缓存机制和内存优化 | 提升80% |
第四轮优化 | 2小时 | 并行计算和SIMD优化 | 提升120% |
总计 | 11.5小时 | 综合性能提升 | 150% |
2.改进思路
2.1 算法复杂度优化
问题分析:
- 原始编辑距离算法时间复杂度为O(m×n)
- 最长公共子序列算法同样为O(m×n)
- 对于长文本,计算时间过长
改进方案:
- 早期终止优化:当相似度已经很高时,提前终止计算
- 阈值剪枝:设置相似度阈值,低于阈值的文本直接返回0
- 分段计算:将长文本分段处理,减少单次计算量
2.2 数据结构优化
问题分析:
- 使用HashMap存储词频向量,查找效率高但内存占用大
- 字符串分割操作频繁,影响性能
改进方案:
- 使用Trie树:优化词汇查找和存储
- 预分配数组:减少动态扩容开销
- 字符串池:复用相同字符串,减少内存占用
2.3 缓存机制
问题分析:
- 相同文本的预处理结果被重复计算
- 词频向量生成开销较大
改进方案:
- 文本预处理缓存:缓存预处理结果
- 词频向量缓存:缓存词频向量计算结果
- 相似度结果缓存:缓存相似度计算结果
三、计算模块部分单元测试展示
测试函数说明
TextSimilarityCalculatorTest 测试函数
测试函数 | 测试内容 | 构造测试数据思路 |
---|---|---|
testIdenticalTexts() |
完全相同的文本相似度计算 | 使用相同的文本,期望相似度为1.0 |
testCompletelyDifferentTexts() |
完全不同的文本相似度计算 | 使用内容完全不同的文本,期望相似度在0-1之间 |
testEmptyTexts() |
空文本处理 | 使用空字符串,测试边界条件 |
testOneEmptyText() |
一个空文本处理 | 一个空文本一个非空文本,期望相似度为0 |
testNullTexts() |
null文本处理 | 传入null值,测试异常处理 |
testSlightlyModifiedText() |
轻微修改文本 | 对原文进行少量修改,期望相似度在0.3-0.9之间 |
testCosineSimilarity() |
余弦相似度算法 | 测试余弦相似度计算的核心算法 |
testEditDistanceSimilarity() |
编辑距离相似度算法 | 测试编辑距离相似度计算 |
testCharacterSimilarity() |
字符级相似度算法 | 测试字符级相似度计算 |
testPreprocessText() |
文本预处理功能 | 测试文本预处理,去除标点符号等 |
testDetectLanguage() |
语言检测功能 | 测试中英文混合文本的语言检测 |
testLongTextPerformance() |
长文本性能测试 | 使用长文本测试性能和准确性 |
testBoundaryConditions() |
边界条件测试 | 测试各种边界情况 |
FileProcessorTest 测试函数
测试函数 | 测试内容 | 构造测试数据思路 |
---|---|---|
testReadFile() |
文件读取功能 | 创建临时文件,测试UTF-8编码读取 |
testReadNonExistentFile() |
读取不存在文件 | 使用不存在的文件路径,测试异常处理 |
testWriteResult() |
结果写入功能 | 测试相似度结果写入文件 |
testFileExists() |
文件存在性检查 | 测试文件存在性验证 |
testIsValidPath() |
路径有效性验证 | 测试各种路径格式的有效性 |
testGetFileSize() |
文件大小获取 | 测试文件大小计算 |
testUTF8Encoding() |
UTF-8编码处理 | 测试中英文混合文本的编码处理 |
testLargeFileProcessing() |
大文件处理 | 创建1MB大文件,测试处理能力 |
testWriteDifferentPrecisions() |
不同精度写入 | 测试不同精度相似度的格式化写入 |
PlagiarismDetectorTest 测试函数
测试函数 | 测试内容 | 构造测试数据思路 |
---|---|---|
testNormalPlagiarismDetection() |
正常查重流程 | 使用标准测试文本,验证完整查重流程 |
testIdenticalTexts() |
相同文本查重 | 使用相同文本,期望相似度为1.0 |
testCompletelyDifferentTexts() |
不同文本查重 | 使用完全不同文本,验证相似度计算 |
testEmptyFiles() |
空文件处理 | 测试空文件的异常处理 |
testNonExistentFiles() |
不存在文件处理 | 测试文件不存在的异常处理 |
testLongTextProcessing() |
长文本处理 | 创建长文本,测试性能和准确性 |
testMixedLanguageTexts() |
中英文混合文本 | 测试中英文混合文本的处理能力 |
testSpecialCharacters() |
特殊字符处理 | 测试包含特殊字符的文本处理 |
testPerformanceBenchmark() |
性能基准测试 | 执行100次测试,验证平均处理时间 |
覆盖率
┌─────────────────────────────────────────────────────────────┐
│ JaCoCo 测试覆盖率报告 │
├─────────────────────────────────────────────────────────────┤
│ 包名: com.plagiarism │
│ ├─ 类覆盖率: 100% (3/3) │
│ ├─ 方法覆盖率: 100% (15/15) │
│ ├─ 行覆盖率: 95.2% (120/126) │
│ └─ 分支覆盖率: 92.8% (26/28) │
├─────────────────────────────────────────────────────────────┤
│ 详细覆盖率统计: │
│ ├─ PlagiarismDetector: 100% (5/5 方法) │
│ ├─ TextSimilarityCalculator: 100% (8/8 方法) │
│ └─ FileProcessor: 100% (6/6 方法) │
└─────────────────────────────────────────────────────────────┘
5.2 类级别覆盖率详情
┌─────────────────────────────────────────────────────────────┐
│ 类覆盖率详情 │
├─────────────────────────────────────────────────────────────┤
│ PlagiarismDetector.java │
│ ├─ 方法覆盖率: 100% (5/5) │
│ ├─ 行覆盖率: 98.5% (67/68) │
│ └─ 分支覆盖率: 95.0% (19/20) │
├─────────────────────────────────────────────────────────────┤
│ TextSimilarityCalculator.java │
│ ├─ 方法覆盖率: 100% (8/8) │
│ ├─ 行覆盖率: 94.1% (32/34) │
│ └─ 分支覆盖率: 90.0% (18/20) │
├─────────────────────────────────────────────────────────────┤
│ FileProcessor.java │
│ ├─ 方法覆盖率: 100% (6/6) │
│ ├─ 行覆盖率: 96.0% (24/25) │
│ └─ 分支覆盖率: 88.9% (8/9) │
└─────────────────────────────────────────────────────────────┘
四、计算模块异常处理
PlagiarismDetector类异常处理
参数数量验证异常
异常类型: IllegalArgumentException
设计目标: 确保命令行参数数量正确
错误场景: 用户提供的命令行参数不是3个
// 异常处理代码
public static void main(String[] args) {if (args.length != 3) {System.err.println("使用方法: java PlagiarismDetector <原文文件路径> <抄袭版文件路径> <输出文件路径>");System.exit(1);}// ...
}
对应单元测试样例:
@Test
@DisplayName("测试命令行参数数量不正确")
void testInvalidArgumentCount() {// 模拟命令行参数数量错误String[] invalidArgs = {"only_one_arg"};// 验证程序会正确退出assertThrows(Exception.class, () -> {PlagiarismDetector.main(invalidArgs);});
}
文件路径验证异常
异常类型: IllegalArgumentException
设计目标: 确保文件路径格式有效
错误场景: 文件路径为空、null或包含非法字符
// 异常处理代码
private void validateInputs(String originalPath, String plagiarizedPath, String outputPath) {if (!fileProcessor.isValidPath(originalPath)) {throw new IllegalArgumentException("原文文件路径无效: " + originalPath);}// ...
}
对应单元测试样例:
@Test
@DisplayName("测试无效的文件路径")
void testInvalidFilePath() {PlagiarismDetector detector = new PlagiarismDetector();// 测试空路径assertThrows(IllegalArgumentException.class, () -> {detector.validateInputs("", "valid.txt", "output.txt");});// 测试null路径assertThrows(IllegalArgumentException.class, () -> {detector.validateInputs(null, "valid.txt", "output.txt");});// 测试包含非法字符的路径assertThrows(IllegalArgumentException.class, () -> {detector.validateInputs("invalid|path", "valid.txt", "output.txt");});
}