软工第一次编程作业

这个作业属于哪个课程 计科23级34班
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13477
这个作业的目标 通过设计论文查重系统,体会工程开发流程,实践工程化开发相关知识

1. GitHub仓库地址:

https://github.com/xoaop/3123004536

2. PSP表格

PSP阶段 预估耗时(分钟) 实际耗时(分钟)
计划 20 25
需求分析 30 35
设计 40 45
代码实现 120 130
测试 30 40
报告撰写 30 35
事后总结 20 25
合计 290 335

3. 模块接口的设计和实现

代码组织

程序主要由一个Python文件main.py组成,包含两个核心函数:

  • get_text_fingerprint(text, top_n=20): 将文本转换为SimHash指纹
  • calculate_similarity(simhash1, simhash2): 计算两个指纹的相似度

主程序部分处理命令行参数,读取文件,调用上述函数并输出结果。

函数关系

  • 主程序读取两个文件内容
  • 调用get_text_fingerprint生成两个指纹
  • 调用calculate_similarity计算相似度
  • 输出结果到文件

算法关键

算法使用SimHash算法:

  1. 对文本进行中文分词(使用jieba库)
  2. 统计词频,取频率最高的top_n个词作为特征
  3. 生成SimHash指纹(64位)
  4. 计算两个指纹的海明距离,转换为相似度百分比

独到之处

  • 使用中文分词确保准确处理中文文本
  • 通过词频排序选择最具代表性的特征词,提高查重准确性
  • 简单高效,适合大规模文本比较

关键函数流程图(简化):

文本输入 -> 分词 -> 词频统计 -> 选择top_n -> 生成SimHash -> 计算距离 -> 输出相似度

4. 性能改进

优化前

img

优化后

img


程序中消耗最大的函数

本项目中,消耗时间最多的函数为 get_text_fingerprint,其核心耗时在于分词(jieba.lcut)和特征提取。分词算法复杂度高,且需遍历大量文本,成为性能瓶颈。即使优化了数据结构和流程,分词与SimHash指纹生成的复杂度决定了其在大文本下仍为主要耗时环节。

原因分析:

  • 分词需遍历和匹配词典,耗时显著。
  • 特征词频统计和SimHash计算涉及多次遍历和位运算。
  • 算法复杂度近似 $O(n)$,文本越大耗时越明显。

6. 计算模块部分单元测试展示

覆盖率

img

主要测试代码

def test_get_text_fingerprint(self):
  text = "这是一个测试文本"
  fp = get_text_fingerprint(text)
  self.assertIsNotNone(fp)
  # SimHash 64位,检查hash值
  self.assertIsInstance(fp.value, int)

def test_calculate_similarity_identical(self):
  text = "相同的文本内容"
  fp1 = get_text_fingerprint(text)
  fp2 = get_text_fingerprint(text)
  similarity = calculate_similarity(fp1, fp2)
  self.assertAlmostEqual(similarity, 100.0, delta=1.0)

def test_calculate_similarity_different(self):
  text1 = "第一篇测试文章"
  text2 = "第二篇完全不同的文章"
  fp1 = get_text_fingerprint(text1)
  fp2 = get_text_fingerprint(text2)
  similarity = calculate_similarity(fp1, fp2)
  self.assertLess(similarity, 70.0)  # 调整阈值

def test_calculate_similarity_similar(self):
  text1 = "这是一个相似的文本"
  text2 = "这是一个相似的文章"
  fp1 = get_text_fingerprint(text1)
  fp2 = get_text_fingerprint(text2)
  similarity = calculate_similarity(fp1, fp2)
  self.assertGreater(similarity, 80.0)

def test_empty_text(self):
  text1 = ""
  text2 = ""
  fp1 = get_text_fingerprint(text1)
  fp2 = get_text_fingerprint(text2)
  similarity = calculate_similarity(fp1, fp2)
  self.assertAlmostEqual(similarity, 100.0, delta=1.0)

def test_one_empty_text(self):
  text1 = "非空文本, 包含一些内容"
  text2 = ""
  fp1 = get_text_fingerprint(text1)
  fp2 = get_text_fingerprint(text2)
  similarity = calculate_similarity(fp1, fp2)
  self.assertLess(similarity, 1.0)  # 调整阈值

7. 计算模块部分异常处理说明

  1. 文件读取异常
    • 设计目标:文件不存在或编码错误时,能捕获异常并提示,防止程序崩溃。
    • 测试代码:
      with self.assertRaises(FileNotFoundError):
          read_utf8_file("nonexistent.txt")
      
  2. 文件写入异常
    • 设计目标:写入路径无权限或磁盘满时,能捕获异常并提示。
    • 测试代码:
      with self.assertRaises(Exception):
          write_result("/root/forbidden.txt", 0.9)
      
  3. 空文本/参数异常
    • 设计目标:输入参数不足或文本为空时,能优雅处理并给出提示。
    • 测试代码:
      self.assertEqual(calculate_similarity(0, 0), 100.0)
      
  4. 编码转换异常
    • 设计目标:读取非UTF-8文件时,能捕获UnicodeDecodeError。
    • 测试代码:
      with self.assertRaises(UnicodeDecodeError):
          read_utf8_file("gbk_encoded.txt")
      

posted @ 2025-09-21 17:37  xoaop  阅读(10)  评论(0)    收藏  举报