第一次个人编程作业
第一次编程作业
1.PSP表格
PSP2.1 | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|
计划 | 60 | 30 |
* 估计这个任务需要多少时间 | 60 | 30 |
开发 | 1250 | 1700 |
* 需求分析 (包括学习新技术) | 60 | 120 |
* 生成设计文档 | 30 | 30 |
* 设计复审 | 10 | 30 |
* 代码规范 (为目前的开发制定合适的规范) | 10 | 60 |
* 具体设计 | 300 | 400 |
* 具体编码 | 600 | 800 |
* 代码复审 | 180 | 200 |
* 测试(自我测试,修改代码,提交修改) | 60 | 60 |
报告 | 180 | 200 |
* 测试报告 | 60 | 60 |
* 计算工作量 | 10 | 10 |
* 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1310 | 1730 |
2.模块接口的设计与实现过程
实现关键
设计的主要思路是通过计算两个文本的 SimHash 值,然后通过 汉明距离 (Hamming Distance) 来比较不同文本的相似度。
模块设计类
SimHash 类
该类的主要职责是生成输入文本的 SimHash 值。
HammingUtils 类
该类的主要职责是计算两个 SimHash 值之间的汉明距离以及相似度。
函数
getHash 函数
使用 MD5 算法为输入的字符串生成一个 128 位的哈希值,并将其转换为二进制字符串表示。
getSimHash 函数
计算文本的 SimHash。
getHammingDistance 函数
计算两个 SimHash 值之间的汉明距离。
getSimilarity 函数
根据汉明距离计算文本的相似度。
readFile 函数
读取文件内容并返回其内容的 std::wstring 表示。
charToWstring 函数
将 std::string 转换为 std::wstring,以处理宽字符(如中文)文件路径。
主程序 (main 函数)
各模块之间的关系
SimHash 类
1.SimHash 生成涉及到 MD5 哈希函数,通过 gethash() 函数获取文本片段的哈希值,然后将其组合成一个全局的 SimHash 值。
2.与 main 函数:main 函数会调用 SimHash::getSimHash 来生成原文和抄袭文本的 SimHash 值。
HammingUtils 类
与 main 函数:main 函数调用 HammingUtils::getHammingDistance 来比较原文和抄袭文本的 SimHash 值,接着调用 HammingUtils::getSimilarity 来计算两个文本的相似度。
readFile函数
与 main 函数:main 函数调用 readFile 来读取原文和抄袭文本。
与 SimHash:readFile 读取的文本内容传递给 SimHash::getSimHash,用于生成 SimHash 值。
charToWstring函数
与 main 函数:main 函数用此模块将命令行参数(argv,是 std::string 类型)转换为 std::wstring 类型,以便后续处理。
3.计算模块接口部分的性能改进
记录在改进计算模块性能上所花费的时间,描述你改进的思路,并展示一张性能分析图(由VS 2017/JProfiler的性能分析工具自动生成),并展示你程序中消耗最大的函数。
性能模块的改进思路
改进SimHash生成模块:
优化分词:当前代码的 SimHash 生成对每个字符进行处理,如果是中文或英文的复杂文本,字符级别的分词可能导致大量不必要的哈希计算。通过优化分词机制(例如基于单词而不是字符),可以减少不必要的计算。
缓存MD5计算:如果文本中有重复片段,可以引入缓存机制,避免对相同内容反复计算 MD5 哈希值。
多线程并行化:若文本长度较大,SimHash 计算可以并行化处理。将文本分块,并为每个块生成哈希值,然后汇总。
优化汉明距离计算模块:
并行化计算:如果文件数量较大,且需要计算多次汉明距离,可以通过多线程并发执行,减少等待时间。
算法改进:汉明距离的计算可以通过位操作来加速。C++ 内置的 __builtin_popcount 可以计算两个整数的汉明距离,将字符串转换为整数后使用位操作会比逐字符比较更快。
性能分析图
各个函数的CPU占用
由图分析可知占用CPU较多的函数主要是getHash()函数
4.计算模块部分单元测试展示
SimHash 模块的单元测试
点击查看代码
TEST(SimHashTest, HashLengthTest) {
std::wstring testString = L"hello world";
std::string hash = SimHash::getHash(testString);
ASSERT_EQ(hash.length(), 128); // 验证生成的哈希值长度
}
// 测试生成SimHash是否正确
TEST(SimHashTest, SimHashGenerationTest) {
std::wstring testString = L"test";
std::string simHash = SimHash::getSimHash(testString);
ASSERT_EQ(simHash.length(), 128); // 验证生成的SimHash值长度
}
// 测试空字符串的SimHash生成
TEST(SimHashTest, EmptyStringTest) {
std::wstring emptyString = L"";
std::string simHash = SimHash::getSimHash(emptyString);
ASSERT_EQ(simHash.length(), 128); // 即使输入为空,SimHash仍应生成128位
}
// 测试较长字符串的SimHash生成
TEST(SimHashTest, LongStringTest) {
std::wstring longString(1000, L'a'); // 长度为1000的字符 'a'
std::string simHash = SimHash::getSimHash(longString);
ASSERT_EQ(simHash.length(), 128); // 超长字符串的SimHash仍应生成128位
}
// 测试特殊字符输入
TEST(SimHashTest, SpecialCharacterTest) {
std::wstring specialChars = L"@#&*!";
std::string simHash = SimHash::getSimHash(specialChars);
ASSERT_EQ(simHash.length(), 128); // 特殊字符的输入应生成128位SimHash
}
// 测试两个相同的字符串是否生成相同的SimHash
TEST(SimHashTest, IdenticalStringsTest) {
std::wstring text1 = L"hello world";
std::wstring text2 = L"hello world";
std::string simHash1 = SimHash::getSimHash(text1);
std::string simHash2 = SimHash::getSimHash(text2);
ASSERT_EQ(simHash1, simHash2); // 相同字符串的SimHash应相等
}
// 测试不同字符串生成的SimHash是否不同
TEST(SimHashTest, DifferentStringsTest) {
std::wstring text1 = L"hello world";
std::wstring text2 = L"goodbye world";
std::string simHash1 = SimHash::getSimHash(text1);
std::string simHash2 = SimHash::getSimHash(text2);
ASSERT_NE(simHash1, simHash2); // 不同字符串的SimHash应不相等
}
HammingUtils 模块的单元测试
点击查看代码
#include <gtest/gtest.h>
#include "HammingUtils.h" // 假设HammingUtils被封装在HammingUtils.h中
// 测试相同字符串的汉明距离应为0
TEST(HammingUtilsTest, SameHashDistanceTest) {
std::wstring text = L"hello world";
std::string simHash1 = SimHash::getSimHash(text);
std::string simHash2 = SimHash::getSimHash(text);
int distance = HammingUtils::getHammingDistance(simHash1, simHash2);
ASSERT_EQ(distance, 0); // 相同字符串的汉明距离应为0
}
// 测试不同字符串的汉明距离大于0
TEST(HammingUtilsTest, DifferentHashDistanceTest) {
std::wstring text1 = L"hello world";
std::wstring text2 = L"goodbye world";
std::string simHash1 = SimHash::getSimHash(text1);
std::string simHash2 = SimHash::getSimHash(text2);
int distance = HammingUtils::getHammingDistance(simHash1, simHash2);
ASSERT_GT(distance, 0); // 不同字符串的汉明距离应大于0
}
// 测试相似度计算,完全相同的哈希值相似度应为1.0
TEST(HammingUtilsTest, SimilarityIdenticalTest) {
std::wstring text = L"hello world";
std::string simHash1 = SimHash::getSimHash(text);
std::string simHash2 = SimHash::getSimHash(text);
int distance = HammingUtils::getHammingDistance(simHash1, simHash2);
double similarity = HammingUtils::getSimilarity(distance);
ASSERT_DOUBLE_EQ(similarity, 1.0); // 相同字符串的相似度应为1.0
}
// 测试相似度计算,不同的哈希值相似度应小于1.0
TEST(HammingUtilsTest, SimilarityDifferentTest) {
std::wstring text1 = L"hello world";
std::wstring text2 = L"goodbye world";
std::string simHash1 = SimHash::getSimHash(text1);
std::string simHash2 = SimHash::getSimHash(text2);
int distance = HammingUtils::getHammingDistance(simHash1, simHash2);
double similarity = HammingUtils::getSimilarity(distance);
ASSERT_LT(similarity, 1.0); // 不同字符串的相似度应小于1.0
}
// 测试异常处理:不同长度的SimHash输入
TEST(HammingUtilsTest, HammingDistanceLengthMismatchTest) {
std::wstring text1 = L"hello";
std::wstring text2 = L"goodbye";
std::string simHash1 = SimHash::getSimHash(text1);
std::string simHash2 = SimHash::getSimHash(text2);
simHash2 = simHash2.substr(0, 127); // 故意将其中一个SimHash截短
EXPECT_THROW(HammingUtils::getHammingDistance(simHash1, simHash2), std::invalid_argument);
}
5.计算模块部分异常处理说明
在博客中详细介绍每种异常的设计目标。每种异常都要选择一个单元测试样例发布在博客中,并指明错误对应的场景
输入文件读取失败
点击查看代码
std::wstring readFile(const std::wstring& filePath) {
std::wifstream file(filePath);
if (!file.is_open()) {
std::wcerr << L"无法打开文件: " << filePath << std::endl;
exit(1); // 退出程序
}
// 读取文件内容
}
SimHash计算过程中输入为空
点击查看代码
static std::string getSimHash(const std::wstring& str) {
if (str.empty()) {
throw std::invalid_argument("输入文本为空,无法计算SimHash。");
}
// 正常处理逻辑
}
汉明距离计算中的长度不匹配
点击查看代码
static int getHammingDistance(const std::string& simHash1, const std::string& simHash2) {
if (simHash1.length() != simHash2.length()) {
throw std::invalid_argument("SimHash长度不匹配,无法计算汉明距离。");
}
// 正常处理逻辑
}
内存溢出或超时
点击查看代码
const size_t MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
std::wstring readFile(const std::wstring& filePath) {
std::wifstream file(filePath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
std::wcerr << L"无法打开文件: " << filePath << std::endl;
exit(1);
}
std::streamsize size = file.tellg();
if (size > MAX_FILE_SIZE) {
std::wcerr << L"文件过大,无法处理: " << filePath << std::endl;
exit(1);
}
// 读取文件内容
}
文件编码格式错误
点击查看代码
std::wstring readFile(const std::wstring& filePath) {
std::wifstream file(filePath);
if (!file.is_open()) {
std::wcerr << L"无法打开文件: " << filePath << std::endl;
exit(1);
}
try {
file.imbue(std::locale(file.getloc(), new std::codecvt_utf8<wchar_t>));
} catch (const std::exception& e) {
std::wcerr << L"文件编码格式错误: " << filePath << std::endl;
exit(1);
}
// 读取文件内容
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类