作业2

仓库地址:https://github.com/KIMNorthFroce/3123007654

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023/homework/13324
这个作业的目标 <学习如何编写人项目>
PSP2.1 Personal Software Process Stages 预估耗时(分钟) 预估耗时(分钟)
Planning 计划
· Estimate · 估计这个任务需要多少时间 100 90
Development 开发
· Analysis · 需求分析 (包括学习新技术) 120 100
· Design Spec · 生成设计文档 40 45
· Design Review · 设计复审 40 30
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 60 60
· Design · 具体设计 180 170
· Coding · 具体编码 90 60
· Code Review · 代码复审 60 60
· Test · 测试(自我测试,修改代码,提交修改) 120 120
Reporting 报告
· Test Repor · 测试报告 100 90
· Size Measurement · 计算工作量 40 40
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 60 80
· 合计 1100 945

实现思路

该程序通过读取两篇论文的文本内容,使用正则表达式提取单词并统计词频生成词频向量,然后计算两个向量的余弦相似度来衡量文本相似性。最后,根据预设阈值判断是否存在抄袭,并将相似度结果输出到控制台和指定文件中。核心步骤包括文件读取、词频统计、余弦相似度计算和结果输出。

代码

点击查看代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <cmath>
#include <regex>

// 函数声明
std::unordered_map<std::string, int> createWordFrequencyVector(const std::string& text); // 生成词频向量
double calculateCosineSimilarity(const std::unordered_map<std::string, int>& vector1, const std::unordered_map<std::string, int>& vector2); // 计算余弦相似度

int main() {
    // 接收用户输入的文件路径
    std::string filePath1, filePath2, filePath3;
    std::cout << "请输入第一篇论文的文件路径: ";
    std::cin >> filePath1; // 读取第一篇论文的文件路径
    std::cout << "请输入第二篇论文的文件路径: ";
    std::cin >> filePath2; // 读取第二篇论文的文件路径
    std::cout << "请输入查重结果的文件路径: ";
    std::cin >> filePath3; // 读取查重结果的文件路径

    // 打开文件并检查是否成功
    std::ifstream file1(filePath1); // 打开第一篇论文文件
    std::ifstream file2(filePath2); // 打开第二篇论文文件

    if (!file1.is_open() || !file2.is_open()) { // 检查文件是否成功打开
        std::cerr << "无法打开文件,请检查文件路径是否正确。" << std::endl;
        return 1; // 如果文件打开失败,程序退出
    }

    // 读取文件内容
    std::stringstream buffer1, buffer2; // 定义字符串流,用于存储文件内容
    buffer1 << file1.rdbuf(); // 将第一篇论文的内容读入buffer1
    buffer2 << file2.rdbuf(); // 将第二篇论文的内容读入buffer2
    std::string paper1 = buffer1.str(); // 将buffer1的内容转换为字符串
    std::string paper2 = buffer2.str(); // 将buffer2的内容转换为字符串

    // 生成词频向量
    auto vector1 = createWordFrequencyVector(paper1); // 生成第一篇论文的词频向量
    auto vector2 = createWordFrequencyVector(paper2); // 生成第二篇论文的词频向量

    // 计算余弦相似度
    double similarity = calculateCosineSimilarity(vector1, vector2); // 计算两篇论文的余弦相似度
    double threshold = 0.7; // 设置相似度阈值

    // 输出相似度
    std::cout << "相似度: " << similarity << std::endl; // 打印相似度到控制台

    // 将相似度写入输出文件
    std::ofstream outfile(filePath3); // 打开输出文件
    if (outfile.is_open()) { // 检查文件是否成功打开
        outfile << similarity << '\n'; // 将相似度写入文件
        outfile.close(); // 关闭文件
    } else {
        std::cerr << "无法打开输出文件" << std::endl; // 如果文件打开失败,输出错误信息
    }

    // 输出检测结果
    if (similarity >= threshold) { // 如果相似度大于阈值
        std::cout << "这两篇论文可能存在相似性或抄袭!" << std::endl; // 输出可能存在抄袭
    } else {
        std::cout << "这两篇论文不太可能存在相似性或抄袭。" << std::endl; // 输出不太可能存在抄袭
    }

    return 0; // 程序正常退出
}

/**
 * 创建词频向量
 * @param text 输入的文本
 * @return 词频向量(哈希表,键为单词,值为词频)
 */
std::unordered_map<std::string, int> createWordFrequencyVector(const std::string& text) {
    std::unordered_map<std::string, int> vector; // 定义词频向量(哈希表)
    std::regex pattern(R"([\w'-]+)", std::regex_constants::icase); // 定义正则表达式,匹配单词(忽略大小写)
    auto words_begin = std::sregex_iterator(text.begin(), text.end(), pattern); // 定义正则表达式迭代器,指向文本开头
    auto words_end = std::sregex_iterator(); // 定义正则表达式迭代器,指向文本结尾

    // 遍历所有匹配的单词
    for (std::sregex_iterator i = words_begin; i != words_end; ++i) {
        std::string word = (*i).str(); // 获取当前匹配的单词
        // 将单词转换为小写
        for (char& c : word) {
            c = tolower(c); // 将每个字符转换为小写
        }
        vector[word]++; // 更新词频向量中的词频
    }

    return vector; // 返回生成的词频向量
}

/**
 * 计算余弦相似度
 * @param vector1 第一个词频向量
 * @param vector2 第二个词频向量
 * @return 余弦相似度
 */
double calculateCosineSimilarity(const std::unordered_map<std::string, int>& vector1, const std::unordered_map<std::string, int>& vector2) {
    double dotProduct = 0.0; // 点积
    double norm1 = 0.0;      // 向量1的模长平方
    double norm2 = 0.0;      // 向量2的模长平方

    // 计算点积和向量1的模长平方
    for (const auto& entry : vector1) { // 遍历向量1中的每个单词
        const std::string& word = entry.first; // 获取单词
        int freq1 = entry.second; // 获取单词在向量1中的词频
        if (vector2.count(word)) { // 如果向量2中也包含该单词
            int freq2 = vector2.at(word); // 获取单词在向量2中的词频
            dotProduct += freq1 * freq2; // 更新点积
        }
        norm1 += freq1 * freq1; // 更新向量1的模长平方
    }

    // 计算向量2的模长平方
    for (const auto& entry : vector2) { // 遍历向量2中的每个单词
        int freq2 = entry.second; // 获取单词在向量2中的词频
        norm2 += freq2 * freq2; // 更新向量2的模长平方
    }

    // 避免除以零
    if (norm1 == 0.0 || norm2 == 0.0) {
        return 0.0; // 如果任一向量模长为0,返回相似度0
    }

    // 计算余弦相似度
    return dotProduct / (std::sqrt(norm1) * std::sqrt(norm2)); // 返回余弦相似度
}
posted @ 2025-03-07 20:04  Kim_NorthFroce  阅读(38)  评论(0)    收藏  举报