第一次个人编程作业
第一次个人编程作业
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience |
|---|---|
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13477 |
| 这个作业的目标 | 实现一个3000字以上论文查重程序 |
| github连接: | https://github.com/LancyPluto/3123004789 |
一、psp表格如下
| Process Stages | Process Stages (中文) | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 20 | 25 |
| · Estimate | · 估计这个任务需要多少时间 | 180 | 240 |
| Development | 开发 | 200 | 180 |
| · Analysis | · 需求分析 (包括学习新技术) | 120 | 120 |
| · Design Spec | · 生成设计文档 | 15 | 20 |
| · Design Review | · 设计复审 | 25 | 25 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 10 |
| · Design | · 具体设计 | 40 | 50 |
| · Coding | · 具体编码 | 50 | 80 |
| · Code Review | · 代码复审 | 30 | 55 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 20 | 35 |
| Reporting | 报告 | 80 | 100 |
| · Test Report | · 测试报告 | 40 | 70 |
| · Size Measurement | · 计算工作量 | 20 | 20 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 10 |
| · 合计 | 880 | 1040 |
二、计算模块接口的设计与实现过程
2.1 程序整体结构
2.1.1 核心模块
| 模块名称 | 主要函数 | 功能职责 |
|---|---|---|
| 文件读写模块 | read_text_file(path) | 负责读取输入文件,返回字符串 |
| 文本预处理模块 | clean_text(text) | 清洗文本:去掉多余符号,统一空格 |
| 分词模块 | tokenize(text) | 中文用 jieba 分词,英文或含空格文本直接用空格分割。如果 jieba 不可用,退化为单字切分 |
| 算法核心模块 | lcs_length(a, b) cosine_similarity(a_tokens, b_tokens) compute_duplicate_rate(orig, copy) |
LCS 算法:用动态规划求最长公共子序列长度,衡量顺序相似度(慢)。 -余弦相似度算法:基于词频向量计算整体相似度,效率高,适合长文本。<br/- compute_duplicate_rate:主接口,负责调用清理、分词、相似度计算,并输出重复率结果。 |
| 主流程模块 | main() | 命令行参数处理和程序入口 |
2.1.2 函数关系框架图

2.2 算法核心
2.2.1 核心思想
论文改写一般不是每句话都完全一致,而是部分修改。如果直接使用字符串匹配,则有些稍微更改的地方就会查重失败。因此本程序使用最长公共子序列(LCS)算法来计算相似度。
2.2 算法流程图

三、计算模块性能优化
3.1 改进前存在的问题
在最初版本的算法中,最长公共子序列(LCS)计算模块采用了标准二维动态规划实现,时间复杂度为 O(n×m),空间复杂度也为 O(n×m)。当文本长度较长(如数千词以上)时,DP 表规模非常大,运行时间过长且内存消耗明显,无法适应大规模文本比对需求。
3.2 改进思路
-
空间优化:
- 原二位DP表改为滚动数组,只保留上一行和当前行。
- 空间复杂度从O(nm) 降为 O(min(n,m)),空间开销减少明显。
-
时间优化:
-
在 预处理阶段 合并清洗与分词操作,减少正则替换和多次字符串扫描的开销。
-
引入可替代算法:
- 小规模文本可继续使用 LCS(保证顺序敏感度)。
- 大规模文本切换为 余弦相似度 或 Jaccard 相似度,复杂度约 O(n+m),比 O(n×m) 大幅提升。
-
通过这种“按规模选择算法”的方式,在不同文本规模下取得 精度与性能的平衡。
-

性能分析图:

四、计算模块部分单元测试
测试代码:

测试覆盖率:

五、计算模块异常处理说明
1.FileNotFoundError
当用户提供的文件路径不存在时,应该抛出 FileNotFoundError,而不是让程序在打开文件时直接崩溃。这样用户能明确知道是 文件路径错误。
单元测试样例
import pytest
from plagiarism_checker import read_text_file
def test_file_not_found():
with pytest.raises(FileNotFoundError):
read_text_file("C:/Lancy/.txt")
错误场景
- 用户输入了一个不存在的路径,例如
C:/Lancy/.txt - 或者文件被删除、移动,导致读取失败
2.RuntimeError — 分词器加载失败
设计目标
jieba 中文分词库在某些情况下可能初始化失败(例如缺少词典文件)。
为了避免程序静默失败,我们在 tokenize() 中增加检查,如果 jieba 初始化失败则抛出 RuntimeError。
单元测试样例
import pytest
from plagiarism_checker import tokenize
def test_tokenizer_fail(monkeypatch):
# 模拟 jieba 分词器不可用
monkeypatch.setattr("plagiarism_checker.USE_JIEBA", False)
text = "今天天气好"
tokens = tokenize(text)
# 即使不用 jieba,tokenize 也应返回非空结果
assert len(tokens) > 0
(这个例子用 monkeypatch 模拟了失败场景,在真实环境中可能需要更复杂的异常触发)
错误场景
- jieba 词典缺失或损坏
- 分词器未正确初始化
3.极短文本(过滤后<10词)
设计目标
避免短文本因少量词重合虚高,返回0.05-0.1相似度。
错误场景
上传单句文本、摘要片段。
import unittest
from similarity_calculator import calculate_plagiarism_similarity
import jieba
from word_filter import filter_stop_words
class TestShortText(unittest.TestCase):
def test_short_input(self):
short1 = filter_stop_words(jieba.lcut("人工智能助力教育发展"))
short2 = filter_stop_words(jieba.lcut("人工智能推动教育进步"))
sim = calculate_plagiarism_similarity(short1, short2)
self.assertTrue(0.05 <= sim <= 0.1)
if __name__ == "__main__":
unittest.main()

浙公网安备 33010602011771号