个人项目
1.个人项目
github链接:https://github.com/pupaname/3124004431
| 这个作业属于哪一个课程 | https://edu.cnblogs.com/campus/gdgy/Class78-Grade2024-CS |
|---|---|
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class78-Grade2024-CS/homework/15702 |
| 这个作业的目标 | 1. 在 GitHub 仓库中新建以学号命名的文件夹,并使用 Git 管理源代码; 2. 使用 C++ / Java / Python 实现论文查重程序,通过命令行参数读取原文与抄袭版论文,输出重复率到指定文件; 3. 完成代码质量分析、性能分析、单元测试(至少10个测试用例)与异常处理; 4. 撰写博客,记录 PSP 表格、设计实现、性能改进、测试展示与异常处理等内容。 |
2. PSP 时间预估表格
| 模块 | 预估耗时(min) | 工作内容说明 |
|---|---|---|
| UTF8文本切分模块 | 30 | 实现UTF-8多字节汉字识别,处理中文3字节字符,防止汉字截断 |
| 文本规范化模块 | 40 | 过滤中英文标点、全角空格,英文转为小写,清洗文本 |
| N-Gram生成模块 | 30 | 滑动窗口生成5-gram字符串,存入unordered_set集合 |
| N-Gram比对模块 | 25 | 求集合交集,计算Jaccard、overlap、coverage指标 |
| 文件读写模块 | 20 | 二进制读取文件,自动去除UTF-8 BOM头,异常处理 |
| 主函数与命令行参数、结果输出 | 20 | 参数校验,调用各模块,写入结果文件 |
| 代码调试、边界用例测试 | 60 | 空文件、纯标点、短文本、中英文混合测试 |
| 合计 | 225 |
3. 计算模块接口设计与实现
3.1 代码组织
- 函数列表
- splitUTF8():将原始字符串切分为单个UTF-8字符(汉字/字母),返回字符vector
- isChinesePunct():判断UTF8字符串是否是中文标点
- isSpaceChar():判断是否为半角/全角空白字符
- normalize():文本规范化,过滤标点、空白,英文小写,返回清洗后的字符vector
- makeNGrams():滑动窗口生成N元字符片段集合,返回unordered_set
- compareNGrams():接收两个N-Gram集合,计算各项相似度指标,返回CompareResult结构体
- readFile():二进制读取文件,移除UTF8 BOM,返回读取成功与否
- 结构体
- CompareResult:封装计算结果:overlap、jaccard、coverage、公共片段数量、两个文档N-gram总数
- 调用关系
main() → readFile()读取两篇文档 → normalize()清洗文本 → makeNGrams()生成N-Gram集合 → compareNGrams()计算相似度 → 控制台+文件输出结果
3.2 算法关键
采用字符级5-Gram算法:
- 将中文、英文全部切分为单个UTF8字符;
- 使用长度为5的滑动窗口,依次截取连续5个字符组成N-Gram片段;
- 两篇文档各自生成N-Gram集合(集合自动去重);
- 求两个集合交集,计算:
- 原文覆盖率 overlap = \(\frac{|A∩B|}{|A|}\)
- 待查文档覆盖率 coverage = \(\frac{|A∩B|}{|B|}\)
- Jaccard相似度 Jaccard = \(\frac{|A∩B|}{|A∪B|}=\frac{|A∩B|}{|A|+|B|-|A∩B|}\)
3.3 独到之处
- 原生支持UTF-8中文:不是简单按
char遍历,专门实现splitUTF8,避免中文汉字被拆成多个乱码字节; - 同时过滤中文标点、全角空格,适配中文论文场景;
- 使用
unordered_set存储N-Gram,查找为O(1),比对速度远优于LCS; - 多维度相似度输出:同时输出Jaccard、原文被复制比例、待查文本来源比例,比单一重复率更适合论文查重;
- 文件读取自动去除UTF-8 BOM,解决记事本保存中文文件的乱码问题;
- 集合比对优化:遍历较小集合,减少哈希查找次数,降低时间开销。
4. 计算模块性能改进
4.1 性能优化花费时间:40分钟
4.2 改进思路
原始方案设想:采用LCS最长公共子序列,字符级DP。
- 问题:文本长度增大时,时间复杂度 \((O(nm))\),长论文会出现计算缓慢、内存占用大。
- 优化方案:替换为N-Gram+哈希集合。
- 时间复杂度:生成N-Gram是\((O(n))\),集合求交集是\(O(min(|A|,|B|))\);
- 使用
unordered_set哈希集合,利用哈希快速查找; - 比对时遍历元素更少的集合,减少哈希查询次数;
- N-Gram使用集合自动去重,避免重复片段多次参与计算;
- UTF8处理只做一次,避免重复解码。
4.3 性能分析结论
程序耗时最多的函数:
- makeNGrams:生成滑动窗口字符串,字符串拼接占用CPU;
- normalize:遍历全部UTF8字符,做标点、空白判断;
- compareNGrams:哈希集合循环查找。
5. 单元测试展示
5.1 测试目标函数
splitUTF8、normalize、makeNGrams、compareNGrams、readFile
5.2 测试数据构造思路
- 正常中英文混合文本:测试中文汉字、英文大小写、标点过滤;
- 空文本:边界测试,输入空字符串,返回空集合,相似度为0;
- 纯标点/空格文本:清洗后为空,验证过滤逻辑;
- 完全相同文本:Jaccard=100%;
- 完全无关文本:Jaccard=0%;
- 部分重合文本:少量句子重合,验证指标计算正确性;
- 带UTF8 BOM的文件:验证readFile自动剔除BOM。
5.3 单元测试示例代码
#include "checker.h"
#include <cassert>
#include <cmath>
#include <iostream>
#include <fstream>
// ---------- 1. UTF-8 切分 ----------
void testSplitUTF8()
{
// 纯 ASCII
auto a = splitUTF8("hello");
assert(a.size() == 5);
assert(a[0] == "h");
// 中英混合(1 个汉字 = 1 个元素)
auto b = splitUTF8("你好a");
assert(b.size() == 3);
assert(b[0] == "你");
assert(b[1] == "好");
assert(b[2] == "a");
// 空字符串
auto c = splitUTF8("");
assert(c.empty());
std::cout << "[PASS] splitUTF8\n";
}
// ---------- 2. 中英文标点与空白判断 ----------
void testPunctAndSpace()
{
assert(isChinesePunct("。") == true);
assert(isChinesePunct(",") == true);
assert(isChinesePunct("a") == false);
assert(isSpaceChar(" ") == true);
assert(isSpaceChar("\t") == true);
assert(isSpaceChar(" ") == true); // 全角空格
assert(isSpaceChar("a") == false);
std::cout << "[PASS] isChinesePunct / isSpaceChar\n";
}
// ---------- 3. 规范化 ----------
void testNormalize()
{
// 大小写 + 中英文标点 + 空格
// "Hello, 世界。 Test!" → "hello世界test"
// 共 5 + 2 + 4 = 11 个字符
auto r = normalize("Hello, 世界。 Test!");
assert(r.size() == 11);
assert(r[0] == "h");
assert(r[4] == "o");
assert(r[5] == "世");
assert(r[6] == "界");
assert(r[7] == "t");
// 空串
assert(normalize("").empty());
std::cout << "[PASS] normalize\n";
}
// ---------- 4. N-gram 生成 ----------
void testMakeNGrams()
{
std::vector<std::string> chars = {"a", "b", "c", "d"};
// N=3 → 2 个: abc, bcd
auto g1 = makeNGrams(chars, 3);
assert(g1.size() == 2);
assert(g1.count("abc") == 1);
assert(g1.count("bcd") == 1);
// N 大于字符数 → 空
auto g2 = makeNGrams(chars, 10);
assert(g2.empty());
// 恰好等于字符数 → 1 个
auto g3 = makeNGrams(chars, 4);
assert(g3.size() == 1);
std::cout << "[PASS] makeNGrams\n";
}
// ---------- 5. 相似度计算 ----------
void testCompareNGrams()
{
std::unordered_set<std::string> A = {"abc", "bcd", "cde"};
std::unordered_set<std::string> B = {"abc", "cde", "def"};
auto r = compareNGrams(A, B);
// 交集 {abc, cde} = 2
assert(r.commonGrams == 2);
assert(std::fabs(r.overlap - 2.0/3.0) < 1e-9);
assert(std::fabs(r.coverage - 2.0/3.0) < 1e-9);
assert(std::fabs(r.jaccard - 0.5) < 1e-9);
// 空集合分支
std::unordered_set<std::string> empty;
auto r2 = compareNGrams(A, empty);
assert(r2.commonGrams == 0);
assert(r2.overlap == 0.0);
assert(r2.coverage == 0.0);
assert(r2.jaccard == 0.0);
std::cout << "[PASS] compareNGrams\n";
}
// ---------- 6. 文件读取 + BOM 去除 ----------
void testReadFile()
{
// 正常读取
{
std::ofstream f("tmp_test.txt");
f << "hello world";
}
std::string s;
assert(readFile("tmp_test.txt", s) == true);
assert(s == "hello world");
// 文件不存在
assert(readFile("not_exist_12345.txt", s) == false);
// 带 UTF-8 BOM 的文件
{
std::ofstream f("tmp_bom.txt", std::ios::binary);
f << "\xEF\xBB\xBF" << "content";
}
assert(readFile("tmp_bom.txt", s) == true);
assert(s == "content");
std::cout << "[PASS] readFile\n";
}
int main()
{
testSplitUTF8();
testPunctAndSpace();
testNormalize();
testMakeNGrams();
testCompareNGrams();
testReadFile();
std::cout << "\n==== All tests passed ====\n";
return 0;
}

6.实际psp
| 阶段 | 实际时间 (min) | 工作内容 |
|---|---|---|
| 需求分析与方案设计 | 18 | 放弃慢速 LCS,确定使用 UTF8+N-Gram 查重架构,确定三项相似度指标 |
| UTF8 字符切分模块开发 | 35 | 实现完整 UTF8 汉字切分,解决中文多字节乱码问题 |
| 文本清洗规范化开发 | 38 | 完成中英文标点、全角空格过滤、英文小写归一化 |
| N-Gram 生成算法开发 | 32 | 实现 5-gram 滑动窗口、集合去重逻辑 |
| 相似度计算模块开发 | 28 | 实现 Jaccard、Overlap、Coverage 三项公式计算 |
| 文件读写模块开发 | 15 | 实现二进制读取、自动去除 UTF-8 BOM、异常处理 |
| 命令行与结果输出 | 16 | 适配标准命令行参数、双文件输入、多指标文件输出 |
| 单元测试编写 | 45 | 编写全功能测试用例:空文本、纯标点、完全相同、无交集、边界测试 |
| Gcov 覆盖率调试 | 42 | 反复清理 gcda、解决优化导致覆盖率异常、提升分支覆盖率 |
| 性能优化与耗时统计 | 30 | 移除 O (n²) LCS、改用哈希集合、添加 chrono 高精度计时 |
| 整体调试、Bug 修复 | 36 | 解决中文分句 bug、空集合崩溃、浮点精度问题 |
| 合计 | 335 | 完整项目开发、测试、优化、文档整理 |

浙公网安备 33010602011771号