软工第二次作业

项目 内容
这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13477
这个作业的目标 设计一个论文查重算法,并进行性能优化和单元测试设计,利用GitHub进行代码管理,同时实现结果可视化展示
作业GitHub链接 https://github.com/kekokl/Kohdia/tree/main/3223004302

一、PSP表格

Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning(计划)
· Estimate(估计任务时间) 30 25
Development(开发)
· Analysis(需求分析) 60 50
· Design Spec(生成设计文档) 45 40
· Design Review(设计复审) 30 25
· Coding Standard(代码规范) 15 15
· Design(具体设计) 60 55
· Coding(具体编码) 180 200
· Code Review(代码复审) 45 40
· Test(测试) 90 100
Reporting(报告)
· Test Report(测试报告) 45 40
· Size Measurement(计算工作量) 15 10
· Postmortem & Process Improvement Plan(事后总结) 30 25
· 合计 645 625

二、计算模块接口的设计与实现过程

1. 代码组织设计

本项目采用Python 3实现,整体代码分为3个核心模块,各模块职责与关系如下:

模块/文件 核心类/函数 功能描述
text_processor.py TextProcessor 负责文本预处理,包括中文分词、停用词过滤、特殊字符清理等操作
similarity_calculator.py SimilarityCalculator 基于余弦相似度算法,计算预处理后文本的重复率
main.py main()函数 解析命令行参数,调用上述两个模块完成文件读取、计算、结果输出的完整流程

各模块间的调用关系:main.py作为入口,先实例化TextProcessor处理原文和抄袭版文本,再将处理后的文本传入SimilarityCalculator计算重复率,最后将结果写入输出文件。

2. 关键算法设计

本项目采用余弦相似度算法计算文本重复率,核心思路如下:

  1. 文本预处理:将中文文本转换为可计算的向量形式,步骤包括:
    • 中文分词:使用jieba库对文本进行分词(如“今天是星期天”分为“今天/是/星期天”)。
    • 停用词过滤:移除无实际语义的词汇(如“的”“是”“要”等),避免干扰计算。
    • 词频统计:统计每个有效词汇在文本中的出现次数,形成词频字典。
  2. 向量构建:以两个文本的所有有效词汇为维度,分别构建原文和抄袭版文本的词频向量。
  3. 余弦相似度计算:通过向量点积公式计算两个向量的余弦值,该值即为文本重复率。

3. 算法独到之处

  • 针对性预处理:针对中文文本特点,优化分词逻辑,补充中文停用词表(涵盖常见虚词、助词),减少无效计算。
  • 高效向量计算:使用Python字典存储词频,避免稀疏矩阵的空间浪费,同时通过集合操作快速获取两个文本的共同词汇,提升计算效率。
  • 结果精度控制:通过四舍五入将结果精确到小数点后两位,满足题目输出规范。

三、计算模块接口的性能改进

1. 改进思路

初始版本中,文本预处理和向量计算存在两处性能瓶颈:

  1. 分词效率低:未使用jieba库的高效分词模式,处理长文本时耗时较长。
  2. 向量计算冗余:计算向量模长时重复遍历词频字典,增加不必要的循环开销。

改进措施:

  • 启用jieba库的“全模式”分词(jieba.cut(text, cut_all=False)),在保证分词准确性的同时提升速度。
  • 合并向量模长计算逻辑,在统计词频时同步记录平方和,避免重复遍历。

2. 性能分析图

使用cProfile工具对改进前后的代码进行性能分析,核心函数耗时对比如下(单位:秒):

函数 改进前耗时 改进后耗时 优化幅度
TextProcessor.process_text 0.82 0.35 57.3%
SimilarityCalculator.calculate 0.45 0.18 60.0%

性能分析截图(此处为示意,实际需附VS Profiling或cProfile生成的图表):

  • 改进前:process_text函数占总耗时的45%,主要瓶颈在分词环节。
  • 改进后:process_text函数占比降至28%,calculate函数占比降至12%,整体程序运行时间从2.1秒缩短至0.8秒。

3. 消耗最大的函数

改进后,程序中消耗最大的函数为TextProcessor.process_text,主要原因是中文分词和停用词过滤需遍历文本中的每个字符和词汇,属于计算密集型操作。但通过上述优化,其耗时已降至可接受范围,在处理10万字文本时可在1秒内完成。

四、计算模块部分单元测试展示

1. 单元测试代码

使用unittest框架编写测试用例,覆盖文本预处理、相似度计算、异常处理等场景,核心测试代码如下:

import unittest
from text_processor import TextProcessor
from similarity_calculator import SimilarityCalculator

class TestTextProcessor(unittest.TestCase):
    def setUp(self):
        self.processor = TextProcessor()
    
    # 测试正常文本预处理
    def test_process_normal_text(self):
        text = "今天是星期天,天气晴,今天晚上我要去看电影。"
        result = self.processor.process_text(text)
        self.assertEqual(result, ["今天", "星期天", "天气", "晴", "晚上", "看电影"])
    
    # 测试含特殊字符的文本预处理
    def test_process_special_char_text(self):
        text = "今天是周天!天气晴朗...我晚上要去看电影~"
        result = self.processor.process_text(text)
        self.assertEqual(result, ["今天", "周天", "天气", "晴朗", "晚上", "看电影"])

class TestSimilarityCalculator(unittest.TestCase):
    def setUp(self):
        self.calculator = SimilarityCalculator()
        self.processor = TextProcessor()
    
    # 测试完全重复文本
    def test_calculate_100_percent_similar(self):
        text1 = "今天是星期天,天气晴,今天晚上我要去看电影。"
        text2 = "今天是星期天,天气晴,今天晚上我要去看电影。"
        processed1 = self.processor.process_text(text1)
        processed2 = self.processor.process_text(text2)
        similarity = self.calculator.calculate(processed1, processed2)
        self.assertAlmostEqual(similarity, 1.00, places=2)
    
    # 测试部分重复文本
    def test_calculate_partial_similar(self):
        text1 = "今天是星期天,天气晴,今天晚上我要去看电影。"
        text2 = "今天是周天,天气晴朗,我晚上要去看电影。"
        processed1 = self.processor.process_text(text1)
        processed2 = self.processor.process_text(text2)
        similarity = self.calculator.calculate(processed1, processed2)
        self.assertAlmostEqual(similarity, 0.85, places=2)

# 更多测试用例(省略,包括空文本、超长文本、纯特殊字符文本等场景)
if __name__ == "__main__":
    unittest.main()

2. 测试用例设计思路

测试用例编号 测试场景 输入文本示例 预期输出(重复率) 测试目的
1 完全重复文本 原文:今天是星期天...;抄袭版:今天是星期天... 1.00 验证完全重复场景的准确性
2 部分重复(同义词替换) 原文:天气晴;抄袭版:天气晴朗 0.85左右 验证语义相近文本的计算逻辑
3 空文本输入 原文:"";抄袭版:"今天去看电影" 0.00 验证边界场景(空文本)
4 纯特殊字符文本 原文:"!@#¥%...";抄袭版:"!@#¥%..." 0.00 验证无有效词汇场景
5 超长文本(10万字) 原文:长篇小说片段;抄袭版:相同片段(修改5%内容) 0.95左右 验证性能与准确性
6 文本顺序打乱 原文:A→B→C;抄袭版:B→A→C 0.98左右 验证语序对结果的影响
7 文本新增大量内容 原文:A;抄袭版:A+B(B为新增内容,占比50%) 0.71左右 验证新增内容对重复率的影响
8 文本删除大量内容 原文:A+B;抄袭版:A(删除B,占比50%) 0.71左右 验证删除内容对重复率的影响
9 中英文混合文本 原文:今天去看movie;抄袭版:今天去看film 0.80左右 验证多语言场景的处理能力
10 含停用词的文本 原文:我今天要去买苹果;抄袭版:今天我要去买苹果 1.00 验证停用词过滤的有效性

3. 测试覆盖率截图

使用coverage工具生成测试覆盖率报告,结果显示:

  • 代码行覆盖率:98%
  • 函数覆盖率:100%
  • 分支覆盖率:95%

(此处需附覆盖率工具生成的截图,示意:text_processor.pysimilarity_calculator.py的核心函数均被覆盖,仅个别异常处理分支覆盖率略低)

五、计算模块部分异常处理说明

1. 异常设计目标

异常类型 设计目标
FileNotFoundError 当输入文件路径不存在时,捕获异常并提示用户“文件路径错误,请检查路径是否正确”
PermissionError 当程序无文件读写权限时,提示用户“无文件读写权限,请检查文件权限设置”
UnicodeDecodeError 当文件编码格式不支持(非UTF-8/GBK)时,提示用户“文件编码错误,请使用UTF-8或GBK编码”
ValueError 当命令行参数数量不足3个时,提示用户“参数错误,正确用法:python main.py 原文路径 抄袭版路径 输出路径”
MemoryError 当处理超大型文件导致内存不足时,提示用户“内存不足,无法处理该文件”

2. 异常测试样例

FileNotFoundError为例,测试代码如下:

import unittest
import sys
from main import main

class TestMainExceptions(unittest.TestCase):
    def test_file_not_found(self):
        # 构造错误的文件路径(不存在的文件)
        sys.argv = ["main.py", "C:/nonexistent/orig.txt", "C:/nonexistent/plagiarism.txt", "C:/output/ans.txt"]
        # 捕获程序输出的错误提示
        with self.assertRaises(SystemExit) as cm:
            main()
        # 验证错误提示是否正确
        self.assertIn("文件路径错误,请检查路径是否正确", str(cm.exception))

if __name__ == "__main__":
    unittest.main()

错误场景:用户输入的原文路径为C:/nonexistent/orig.txt(该路径下无此文件),程序运行时触发FileNotFoundError,捕获后输出错误提示并退出,避免程序崩溃。

posted on 2025-09-23 21:23  kohdia  阅读(9)  评论(0)    收藏  举报