软件工程第二次作业_个人项目

news/2025/9/21 10:17:29/文章来源:https://www.cnblogs.com/Ender0831/p/19103245

Github连接: Ender39831/3123004694: homework

](https://github.com/Ender39831/3123004694)

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience/homework/13468
这个作业的目标 体会整个软件的开发过程,同时记录开发过程中的信息
*PSP2.1* *Personal Software Process Stages* *预估耗时(分钟)* *实际耗时(分钟)*
Planning 计划 10 10
· Estimate · 估计这个任务需要多少时间 10 10
Development 开发 340 460
· Analysis · 需求分析 (包括学习新技术) 20 30
· Design Spec · 生成设计文档 30 35
· Design Review · 设计复审 20 15
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 10
· Design · 具体设计 30 45
· Coding · 具体编码 150 210
· Code Review · 代码复审 10 15
· Test · 测试(自我测试,修改代码,提交修改) 60 100
Reporting 报告 60 45
· Test Repor · 测试报告 20 15
· Size Measurement · 计算工作量 20 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 20 10
· 合计 410 515

一. 模块与对应函数

模块 对应函数 作用
核心 main() 控制整个流程
读取 string readFile(const string& filePath) 读取指定文件中的信息
预处理 string preprocessText(const string& text) 去除标点符号并正确处理汉字、字母、数字
相似度计算 double calculateCosineSimilarity(const map<string, int>& origFreq, const map<string, int>& copyFreq) 根据前面计算好的中间变量计算最终的相似度
文件写入 void writeResult(const string& filePath, double similarityPercent) 将结果写入指定文件

二.计算模块接口的设计

1.接口设计:

把每个功能都拆成了独立的函数,他们之间通过参数来链接,具体程序执行的函数顺序为:

  1. 预处理:string preprocessText(const string& text)

  2. 分词:vector splitText(const string& text)

  3. 计算词频:map<string, int> calculateWordFrequency(const vector& words)

  4. 计算相似度:double calculateCosineSimilarity(const map<string, int>& origFreq, const map<string, int>& copyFreq)

2.独到之处:

采用余弦相似度算法

核心是 将文本转化为词频向量,通过向量夹角衡量相似性,具体步骤为:

  1. 构建统一的词集合(确保向量维度一致)
  2. 精确计算点积和模长(反映向量的重合度和自身特征)

在命令行中输出计算的中间结果,方便调试(在面对大文本时,由于输出文本上限,默认将该功能注释掉)

本项目在执行过程中会输出中间变量以便调试,发现问题

3.计算模块源代码:

分词模块:

``// 分词 vector splitText(const string& text) { vector tokens; string currentWord;`

for (size_t i = 0; i < text.size();) {// 判断是否为汉字(UTF-8编码的汉字占3个字节)if ((unsigned char)text[i] >= 0xE0 && i + 2 < text.size()) {string chineseChar = text.substr(i, 3);tokens.push_back(chineseChar);i += 3;}// 处理空格(作为英文单词的分隔符)else if (text[i] == ' ') {if (!currentWord.empty()) {tokens.push_back(currentWord);currentWord.clear();}i++;}// 处理英文字母和数字else {currentWord += text[i];i++;}
}// 添加最后一个单词
if (!currentWord.empty()) {tokens.push_back(currentWord);
}return tokens;

`}``

词频计算模块:

// 计算词频(优化后使用unordered_map替代map): unordered_map<string, int> calculateWordFrequency(const vector<string>& words) { unordered_map<string, int> freq; for (const string& word : words) { freq[word]++; } return freq; }

总计算模块:
// 总计算
double calculateCosineSimilarity(const unordered_map<string, int>& origFreq,
const unordered_map<string, int>& copyFreq) {
// 用哈希集合收集所有词
unordered_set<string> allWords;
for (const auto& pair : origFreq) allWords.insert(pair.first);
for (const auto& pair : copyFreq) allWords.insert(pair.first);

// 计算分子:点积
double dotProduct = 0.0;
// 计算两个向量的模长
double origMagnitude = 0.0, copyMagnitude = 0.0;for (const string& word : allWords) {int origCount = 0, copyCount = 0;auto origIt = origFreq.find(word);if (origIt != origFreq.end()) origCount = origIt->second;auto copyIt = copyFreq.find(word);if (copyIt != copyFreq.end()) copyCount = copyIt->second;dotProduct += origCount * copyCount;origMagnitude += origCount * origCount;copyMagnitude += copyCount * copyCount;
}// 防止除零错误
if (origMagnitude == 0 || copyMagnitude == 0) {return 0.0;
}// 计算余弦相似度
return dotProduct / (sqrt(origMagnitude) * sqrt(copyMagnitude));

}

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

改进思路:

原程序中采用map实现,而map是基于红黑树实现的,插入和查找操作的时间复杂度为 O (log n),而unordered_map是基于哈希表实现,插入和查找操作的平均时间复杂度为 O (1),在处理大文本时,哈希表的效率优势会很明显;当然,使用哈希表必然会造成内存上的增加,经典的内存换性能。

改进前耗时 改进后耗时 提升幅度
58ms 46ms 26.09%

四.计算模块测试展示

  • 完全相同的文本
    • 原文:今天天气暴雨,不适合外出。
    • 抄袭版:今天天气暴雨,不适合外出。
    • 预期相似度:100%
    • 程序计算相似度:100%
  • 部分修改的文本
    • 原文:图形计算需要大量计算资源。
    • 抄袭版:图形渲染需要大量 GPU 资源。
    • 预期相似度:约 70%
    • 程序计算相似度:63.01%
  • 完全不同的文本
    • 原文:读书学习看文章是必要的。
    • 抄袭版:游戏真好玩。
    • 预期相似度:0%
    • 程序计算相似度:11.79%
  • 给的测试文本
    • 原文件名:orig
    • 抄袭版文件名:orig_0.8_del
    • 程序计算相似度:99.36%

五.异常处理

1.输入参数异常

应用场景:当用户的输入参数不符合要求时

处理逻辑代码:

if (argc != 4) { cerr << "参数错误:请提供3个文件路径参数" << endl; cerr << "正确用法:原文文件路径 抄袭版论文路径 输出结果路径" << endl; return 1; }

2.无法打开文件

应用场景:当用户由于输入错路径或其他原因导致无法打开文件时

处理逻辑代码:

if (!file.is_open()) { cerr << "错误:无法打开文件 '" << filePath << "',请检查路径是否正确。" << endl; exit(1); }

if (!file.is_open()) { cerr << "错误:无法打开输出文件 '" << filePath << "',请检查路径是否正确。" << endl; exit(1); }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/908710.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux命令大全(档案管理)

Linux命令大全(档案管理)pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", …

小狼毫雾凇拼音安装部署

I. 资源链接 小狼毫官网:RIME | 中州韻輸入法引擎 雾凇拼音仓库:iDvel/rime-ice

Chapter 3 Resize and Cropping

img = cv2.imread(images/lambo.png) # (462, 623, 3) print(img.shape)# 先是width(x轴),然后是height(y轴),可以看出 resize 是放大或缩小并不是裁剪 imgResized = cv2.resize(img, (300, 200)) # 这里是He…

详细介绍:java中常见的几种排序算法

详细介绍:java中常见的几种排序算法pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monac…

Linux 内核VRF

Linux 内核VRFVirtual Routing and Forwarding (VRF) 由路由表+网络设备组成。 加载vrf内核模块 modprobe vrf 基本操作 # 创建并拉起vrf设备 # 自动创建路由表1 ip link add vrf1 type vrf table 1 ip link set dev v…

使用FFmpeg转换m4a

在Linux下可以使用FFmpeg将m4a文件转换为mp3文件,如下 ffmpeg -i input.m4a -vn -acodec libmp3lame -ab 320k output.mp3其中-i 是输入文件 -vn 是不包括视频 -acodec libmp3lame 是指定 LAME MP3 解编码器 -ab 320k…

提升多屏监控体验/新增辅屏预览功能/轻松实现跨屏实时监控/支持高达500路多个屏幕同时显示

一、前言说明 随着监控系统规模的不断扩大,以及用户对多屏协同操作需求的日益增长,如何高效、灵活地预览大量实时视频通道,成为提升用户体验的关键。尤其是在拥有多个显示器的环境中,用户越来越希望能够在不同屏幕…

[Java SE/文件系统/IO] 核心源码精讲:java.io.File

1 概述:java.io.File Java 流(Stream)、文件(File)和IOJava 中的流(Stream)、文件(File)和 IO(输入输出)是处理数据读取和写入的基础设施它们允许程序与外部数据(如文件、网络、系统输入等)进行交互。java.io…

Linux 内核整体架构详解

Linux 内核整体架构详解Linux 内核是操作系统的核心,其精妙的设计使得它能够高效地管理硬件资源,并为上层应用程序提供稳定的运行环境。下面我将为你详细解析 Linux 内核的结构,包括其整体架构、核心子系统、模块化…

Ubuntu 磁盘扩容与扩容失败障碍解决( df -h 与 GParted 显示空间不一致的问题 -LVM)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

httplb 服务器

#include <iostream> #include "httplib.h" using namespace std; using namespace httplib; int main(){Server svr;svr.Get("/",[](const Request& req,Response& res){res.s…

atoi() - 字符串( ASCLL )转换为整数( int )

引言 atoi() 函数是 C 标准库中用于将字符串(ASCII 字符串)转换为整数(int 类型)的核心函数,其名称是 “ASCII to Integer” 的缩写。 一、基本信息 函数原型 #include <stdlib.h> // 必须包含的头文件 in…

02.Python:Flash初步使用

python,flask前端处理:Vue+Axios Flash:后端框架,默认端口 5000 需要注意:get与post方法,对应的传参与取参方式 --------------<!DOCTYPE html> <html lang="en"><head><meta cha…

解决Kubernetes集群中master节点无法与node节点通信的策略

检查基础网络连接: 使用 ping命令检测节点间网络连通性。若无法ping通,可能是云服务的安全组或本地防火墙规则限制,需对安全组规则和防火墙规则(如iptables)进行审查。 ping <node-IP-address>验证kubelet服…

从高版本的sqlserver向低版本的sqlserver上复制表和数据的方法

今天要从一台sqlserver 2016的电脑上把数据库复制到一台装有sqlserver2012的服务器上,使用备份加还原的时候报错,提示不支持,导出sql语句的方式也没有成功,用navicat也没成功。方法如下: 在目标服务器上打开SQL S…

【ROS2】 忽略局域网多机通信导致数据接收的bug - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

在Ubuntu18.04安装兼容JDK 8的Eclipse集成开发环境

更新系统包列表:首先,打开终端,并执行以下命令以更新Ubuntu系统的包列表: sudo apt update安装Java开发工具包 (JDK 8):在终端中运行以下命令安装OpenJDK 8: sudo apt install openjdk-8-jdk 安装完毕后,运行下…

【php】带数组的文件列表生成,返回数组

【php】带数组的文件列表生成,返回数组$list = gen_files_list(D:/novel/); for($i = 0; $i < count($lis); $i++) {echo "<li><a href=" . htmlspecialchars($lis[$i]) . ">" . …

配置Nginx以支持Websocket连接的方法

安装Nginx:首先,确保您安装了最新版本的Nginx,因为Websocket支持通常在较新版本中表现更佳。设置HTTP升级头:当Websocket客户端请求与服务器建立连接时,它会发送一个HTTP请求,请求中含有 Upgrade: websocket和 C…

Extundelete工具恢复数据

Extundelete 是一款专为 Ext 文件系统(Linux 系统中最常用的文件系统,如 Ext3、Ext4)设计的开源数据恢复工具,核心功能是恢复被意外删除(通过 rm 命令等操作)但尚未被新数据覆盖的文件或目录。它基于文件系统的底…