第一次个人编程作业

第一次个人编程作业 - 论文查重

Github 仓库地址:

https://github.com/zzy685/ZZY685
(代码位于仓库中学号命名的文件夹下)

一、PSP 表格

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

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

2.1 功能划分与版本控制

本项目按照开发阶段划分为初始完整版本性能优化版本两个阶段,每个阶段完成后分别 commit 到 Github,共两次主要 commit:

阶段 包含文件 功能说明 Commit Message
初始完整版本(Commit 1) main.pyplagiarism_checker.pyrequirements.txttest_plagiarism_checker.pytest_data/ 实现论文查重核心逻辑(文本预处理、jieba 分词、词频向量余弦相似度、字符级 bigram 相似度、加权融合、命令行参数解析、文件读写),并配套 24 个单元测试用例和测试样例数据 Add files via upload
性能优化版本(Commit 2) plagiarism_checker.py(更新) 使用 cProfile 分析性能瓶颈后,优化文本预处理(避免重复 clean_text 调用)、优化余弦相似度计算(单次遍历同时计算点积和模长),性能提升约 48% refactor: 性能优化,避免重复文本预处理,优化余弦相似度计算

初始版本完成后程序已能正常运行并通过全部测试;优化版本在其基础上进行性能剖析和针对性改进,体现了 "先实现、后优化" 的开发流程。

2.2 整体架构

main.py                    # 主程序入口,处理命令行参数和文件IO

plagiarism\_checker.py      # 核心查重算法模块

test\_plagiarism\_checker.py # 单元测试

requirements.txt           # 依赖声明

2.3 核心函数

函数名 功能说明
clean_text(text) 文本预处理,去除标点符号和空白字符
segment_words(text) 中文分词,优先使用 jieba,不可用时退化为单字分词
get_char_ngrams(text, n) 生成字符级 n-gram 列表
cosine_similarity(vec1, vec2) 计算两个词频向量的余弦相似度
word_level_similarity(text1, text2) 词级相似度计算
ngram_level_similarity(text1, text2, n) n-gram 级相似度计算
calculate_similarity(orig_text, plag_text) 综合相似度计算(主接口)

2.4 算法设计

采用词级余弦相似度 + 字符级 bigram 相似度的加权融合算法:

  1. 文本预处理:去除所有标点符号、空白字符,统一转为小写。

  2. 词级相似度(权重 0.6):使用 jieba 精确模式分词,统计词频向量,计算余弦相似度。

  3. 字符级 bigram 相似度(权重 0.4):将预处理后的文本切分为相邻字符对,统计频率向量,计算余弦相似度。

  4. 加权融合最终重复率 = 0.6 × 词级相似度 + 0.4 × bigram 相似度

2.5 算法独到之处

  • 双维度融合:词级相似度捕捉语义重复,bigram 相似度捕捉字面重复,对增删改、语序调整等抄袭手段均有较好检测能力。

  • 优雅降级:jieba 不可用时自动退化为单字分词,保证程序在任何环境下都能运行。

  • 多编码兼容:文件读取支持 UTF-8、GBK、GB2312、UTF-16 等多种编码。

  • 边界保护:对空文本、纯标点文本等边界情况完善处理,不会出现除零错误或异常退出。

2.6 关键流程图

开始

  │

  ▼

读取原文文件和抄袭版文件

  │

  ▼

文本预处理(去标点、去空白、转小写)

  │

  ├─────────────────┐

  ▼                 ▼

jieba 分词        字符级 bigram 切分

  │                 │

  ▼                 ▼

统计词频向量      统计 bigram 频率向量

  │                 │

  ▼                 ▼

计算余弦相似度    计算余弦相似度

  │                 │

  └────────┬────────┘

           ▼

     加权融合 (0.6/0.4)

           │

           ▼

     结果保留两位小数

           │

           ▼

     写入答案文件

           │

           ▼

          结束

2.7 程序运行效果

使用作业示例进行测试:

程序运行结果

原文与抄袭版的重复率为 0.79,结果已正确写入答案文件。

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

3.1 性能分析工具

使用 Python 内置的 cProfile 性能分析工具,对长文本样例(约 300 字)重复运行 100 次进行分析。

3.2 性能分析结果(改进前)

性能分析图

3.3 性能瓶颈分析

  1. clean_text(文本预处理):被调用 400 次,累计 0.020 秒。原因是 word_level_similarityngram_level_similarity 各自内部调用 clean_text,导致同一文本被预处理两次。

  2. cosine_similarity(余弦相似度):累计 0.017 秒。原始实现分别计算点积、模 1、模 2,需要遍历向量多次,且创建了 common_keys 集合。

  3. get_char_ngrams(n-gram 生成):累计 0.013 秒。

3.4 改进思路与措施

改进一:避免重复预处理

  • calculate_similarity 中统一调用 clean_text 一次,将预处理后的文本传入内部函数 _segment_words_cleaned_get_char_ngrams_cleaned,消除重复的预处理开销。

改进二:余弦相似度计算优化

  • 原始实现需要遍历向量 3 次(点积、模 1、模 2),并创建交集集合。

  • 改进后在一次遍历中同时累加点积和 vec1 的模长,选择较短的向量遍历以减少循环次数,消除了集合创建开销。

改进三:内部函数封装

  • 新增 _segment_words_cleaned_get_char_ngrams_cleaned 两个内部函数,接受已预处理的文本,避免重复清洗。

3.5 改进前后对比

指标 改进前 改进后
100 次运行总耗时 0.046 秒 0.024 秒
单次平均耗时 0.00046 秒 0.00024 秒
函数调用次数 211,222 次 56,822 次
性能提升 约 48%

改进后程序在 300 字文本上单次运行仅约 0.24 毫秒,远低于 5 秒的时间限制,内存占用也远低于 2048MB 限制。

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

4.1 测试框架

使用 Python 标准库 unittest 编写单元测试,共设计 24 个测试用例,远超要求的 10 个。

4.2 测试用例分类

测试类 测试用例数 覆盖范围
TestCleanText 4 标点去除、空白去除、空输入、纯标点输入
TestSegmentWords 2 正常分词、空文本分词
TestCharNgrams 3 bigram 生成、短文本、空文本
TestCosineSimilarity 3 相同向量、正交向量、空向量
TestCalculateSimilarity 9 相同文本、不同文本、作业示例、空输入、空白文本、部分重叠、语序调整、长文本、范围校验
TestFileOperations 2 结果写入读取、UTF-8 文件读取
TestMainIntegration 1 完整文件输入输出流程

4.3 核心测试用例说明

测试 15:作业示例验证

def test\_homework\_example(self):

    orig = "今天是星期天,天气晴,今天晚上我要去看电影。"

    plagiarized = "今天是周天,天气晴朗,我晚上要去看电影。"

    sim = calculate\_similarity(orig, plagiarized)

    self.assertGreater(sim, 0.5)  # 应有较高相似度

    self.assertLess(sim, 1.0)     # 但不应完全相同

构造思路:直接使用作业题目给出的示例,验证算法对典型增删改抄袭的检测能力。

测试 13:完全相同文本

def test\_identical\_texts(self):

    text = "今天是星期天,天气晴,今天晚上我要去看电影。"

    sim = calculate\_similarity(text, text)

    self.assertAlmostEqual(sim, 1.0, places=2)

构造思路:验证自比较时重复率应为 1.0(100%)。

测试 19:语序调整

def test\_word\_order\_change(self):

    text1 = "我喜欢吃苹果和香蕉。"

    text2 = "香蕉和苹果我喜欢吃。"

    sim = calculate\_similarity(text1, text2)

    self.assertGreater(sim, 0.4)

构造思路:词语完全相同但语序颠倒,验证词级相似度对语序变化的鲁棒性。

测试 24:完整集成测试

def test\_full\_pipeline(self):

    # 使用 tempfile 创建临时文件,模拟命令行调用 main()

    # 验证输出文件存在且结果在合理范围内

    ...

构造思路:端到端验证文件读写、命令行参数解析和结果输出。

4.4 测试运行结果

屏幕截图 2026-09-13 203206
屏幕截图 2026-09-13 203129

全部 24 个测试用例通过,覆盖了正常场景、边界条件和异常输入。

4.5 测试覆盖率

image

测试设计评价:24 个测试用例从单元测试到集成测试层层递进,覆盖文本预处理、分词、相似度计算、文件 IO 等所有核心功能,包含空输入、纯标点、短文本、长文本等边界情况,能够满足该程序的测试要求。

五、附录

5.1 项目文件结构

学号/

├── main.py                    # 主程序入口

├── plagiarism\_checker.py      # 查重算法核心模块

├── test\_plagiarism\_checker.py # 单元测试

├── requirements.txt           # 依赖(jieba)

└── test\_data/                 # 测试样例

    ├── orig.txt

    ├── orig\_add.txt

    ├── long\_orig.txt

    └── long\_plag.txt

5.2 运行方式

\# 安装依赖

pip install -r requirements.txt

\# 运行查重

python main.py \[原文文件路径] \[抄袭版论文文件路径] \[答案文件路径]

\# 运行单元测试

python -m unittest test\_plagiarism\_checker -v

5.3 事后总结与改进计划

本次作业通过实现论文查重算法,加深了对文本相似度计算、模块化设计和单元测试的理解。未来可以从以下方面改进:

  1. 算法优化:引入 TF-IDF 权重,降低常见词的影响;尝试 SimHash 算法支持大规模文档查重。

  2. 性能提升:对超大规模文本,使用哈希技巧和稀疏向量进一步降低内存和计算开销。

  3. 测试完善:引入 coverage.py 生成精确覆盖率报告,补充更多真实论文样例测试。

posted @ 2026-09-13 19:44  zzy314  阅读(14)  评论(0)    收藏  举报