第一次个人编程作业

第一次编程作业

这个作业属于哪个课程 软件工程
这个作业要求在哪里 作业要求
这个作业的目标 实现设计一个论文查重算法

github链接

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占用


1
由图分析可知占用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);
    }

    // 读取文件内容
}

posted @   我是张继凯  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示