软工作业二:论文查重系统

news/2024/9/19 17:44:09/文章来源:https://www.cnblogs.com/bbhscc/p/18412806
这个作业属于哪个课程 <计科22级34班>
这个作业要求在哪里 [<作业要求>](个人项目 - 作业 - 计科22级34班 - 班级博客 - 博客园 (cnblogs.com))
这个作业的目标 通过实际编程任务,全面提升学生在编程、算法、项目管理、性能优化、代码测试和版本控制等方面的能力,为学生未来处理复杂开发项目打下扎实的基础。

项目 GitHub 链接:https://github.com/Tracywu1/3222004892

🧩一、PSP表格

PSP各个阶段 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 50 50
· Estimate · 明确需求和其他因素,估计以下的各个任务需要多少时间 50 50
Development 开发 390 435
· Analysis · 需求分析 (包括学习新技术、新工具的时间) 60 50
· Design Spec · 生成设计文档(整体框架的设计,各模块的接口,用时序图,快速原型等方法) 60 60
· Design Review · 设计复审 10 5
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 20 10
· Design · 具体设计 30 20
· Coding · 具体编码 120 200
· Code Review · 代码复审 30 30
· Test · 测试(自我测试,修改代码,提交修改) 60 60
Reporting 报告 160 180
· Test Report · 测试报告(发现了多少bug,修复了多少) 30 50
· Size Measurement · 计算工作量(多少行代码,多少次签入,多少测试用例,其他工作量) 10 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划(包括写文档、博客的时间) 120 120
· 合计 570 665

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

1.类

  • Main:程序的入口点,处理命令行参数并协调整个查重过程
  • PlagiarismChecker:核心类,负责检查抄袭
  • SimHashCalculator:计算文本的SimHash值
  • FileUtil:处理文件读写
  • HashUtil:计算字符串哈希值
  • Constants:存储系统常量和配置

2.函数

  • Main.main():程序入口,处理命令行参数并执行查重流程
  • PlagiarismChecker.checkPlagiarism():检查两段文本的抄袭程度
  • SimHashCalculator.getSimHash():计算文本的SimHash值
  • FileUtil.readFile():读取文件内容
  • FileUtil.writeResult():写入检测结果
  • HashUtil.hash():计算字符串哈希值

3.类之间的关系

  • Main 类是整个程序的入口,它使用 FileUtil 读取输入文件,调用 PlagiarismChecker 进行抄袭检测,然后再次使用 FileUtil 写入结果。
  • PlagiarismChecker 依赖于 SimHashCalculator 来计算SimHash值。
  • SimHashCalculator 又依赖于 HashUtil 来计算单词的哈希值,并使用 Constants 中定义的常量和停用词列表。

4.关键函数流程图

image-20240912094357450

5.算法关键点和独到之处

(1)关键点:
  • 命令行接口:
    • 程序通过命令行参数接收输入和输出文件路径,提高了灵活性和可集成性。
  • SimHash算法:
    • 使用SimHash算法来快速估计文本相似度,特别适用于大规模文本比较。
    • SimHash将长文本转换为固定长度的指纹(在这里是64位),通过比较指纹来判断相似度。
  • 分词和停用词过滤:
    • 使用结巴分词(JiebaSegmenter)进行中文分词,这对于处理中文文本很重要。
    • 使用预定义的停用词列表(在Constants类中定义)进行过滤,提高了相似度计算的准确性。
  • 词频加权:
    • 在计算SimHash时考虑了词频,这使得重要词语对最终的指纹产生更大影响。
  • 汉明距离:
    • 使用汉明距离来衡量两个SimHash的差异,这是一种简单但有效的相似度度量方法。
  • 可配置性:
    • 通过Constants类集中管理配置参数(如哈希位数和停用词列表),便于未来调整和优化。
  • 错误处理:
    • Main类中包含了基本的错误处理机制,可以捕获并报告在文件读写或相似度计算过程中可能发生的异常。
(2)独到之处:
  • 使用Java 8 Stream API进行词频统计和SimHash计算,代码简洁高效。
  • 采用BigInteger来处理哈希值,提供了更大的数值范围和更精确的计算。
  • 将相似度结果规范化到0-1范围,便于理解和比较。
  • 系统设计考虑了中文文本的特点,包括使用专门的中文分词工具和中文停用词列表。

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

image-20240913170336274

由 IDEA 自带的 profiler 功能生成了以上图表,它展示了程序运行时的方法调用树和每个方法所的执行时间。

对图表的分析:

  • 性能瓶颈:
    • SimHashCalculator.getSimHash 和 JiebaSegmenter.sentenceProcess 是查重过程中的主要耗时点。

改进思路:

  • 使用延迟初始化和双重检查锁定模式来初始化 JiebaSegmenter,这样可以避免在不需要时进行昂贵的初始化操作。
  • 在 SimHashCalculator 中使用传统的 for 循环替代流操作,并使用 long[] 数组代替 int[],可能会略微提高性能。

SimHashCalculator 类原代码:

public class SimHashCalculator {private static final JiebaSegmenter SEGMENTER = new JiebaSegmenter();public static String getSimHash(String text) throws NoSuchAlgorithmException {if (text == null || text.trim().isEmpty()) {return String.join("", Collections.nCopies(Constants.HASH_BITS, "0"));}// 使用jieba分词对文本进行分词处理List<String> words = SEGMENTER.sentenceProcess(text);// 过滤掉停止词,并统计剩余词汇的词频Map<String, Long> wordFrequency = words.stream().filter(word -> !Constants.STOP_WORDS.contains(word)).collect(Collectors.groupingBy(w -> w, Collectors.counting()));// 如果过滤后没有剩余的词,返回全0的SimHashif (wordFrequency.isEmpty()) {return String.join("", Collections.nCopies(Constants.HASH_BITS, "0"));}// 初始化用于存储每个哈希位的权重和的数组int[] v = new int[Constants.HASH_BITS];// 遍历词频映射,根据每个词的哈希值更新数组vfor (Map.Entry<String, Long> entry : wordFrequency.entrySet()) {BigInteger hash = HashUtil.hash(entry.getKey());long weight = entry.getValue();// 遍历哈希值的每一位for (int i = 0; i < Constants.HASH_BITS; i++) {if (hash.testBit(Constants.HASH_BITS - 1 - i)) {v[i] += weight;} else {v[i] -= weight;}}}// 将数组v中的每个元素转换为二进制字符串,并拼接成最终的SimHash值StringBuilder simHashBuilder = new StringBuilder(Constants.HASH_BITS);for (int i = 0; i < Constants.HASH_BITS; i++) {if (v[i] >= 0) {simHashBuilder.append("1");} else {simHashBuilder.append("0");}}return simHashBuilder.toString();}
}

改进后代码:

public class SimHashCalculator {private static volatile JiebaSegmenter SEGMENTER;private static JiebaSegmenter getSegmenter() {if (SEGMENTER == null) {synchronized (SimHashCalculator.class) {if (SEGMENTER == null) {SEGMENTER = new JiebaSegmenter();}}}return SEGMENTER;}/*** 计算给定文本的 SimHash 值。* @param text 输入的文本字符串* @return 文本的 SimHash 值,以二进制字符串形式表示*/public static String getSimHash(String text){// 同之前// 使用数组来存储词频,避免使用流操作,并使用 long[] 数组代替 int[]long[] v = new long[Constants.HASH_BITS];for (String word : words) {if (!Constants.STOP_WORDS.contains(word)) {BigInteger hash = HashUtil.hash(word);for (int i = 0; i < Constants.HASH_BITS; i++) {if (hash.testBit(Constants.HASH_BITS - 1 - i)) {v[i]++;} else {v[i]--;}}}}// 使用StringBuilder构建最终的SimHashStringBuilder simHashBuilder = new StringBuilder(Constants.HASH_BITS);for (long bit : v) {simHashBuilder.append(bit >= 0 ? "1" : "0");}return simHashBuilder.toString();}
}

使用延迟初始化和双重检查锁定模式来初始化 JiebaSegmenter:

private static volatile JiebaSegmenter SEGMENTER;private static final int SHORT_TEXT_THRESHOLD = 50; // 定义短文本的阈值// 使用延迟初始化和双重检查锁定模式来初始化JiebaSegmenter,这样可以避免在不需要时进行昂贵的初始化操作。private static JiebaSegmenter getSegmenter() {if (SEGMENTER == null) {synchronized (PlagiarismChecker.class) {if (SEGMENTER == null) {SEGMENTER = new JiebaSegmenter();}}}return SEGMENTER;}

改进后的程序运行时的方法调用树:

image-20240913172841895

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

展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路。并将单元测试得到的测试覆盖率截图,发表在博客中。(12')

PlagiarismCheckerTest:

  • testCheckPlagiarismWithSimilarTexts: 测试相似但不完全相同的文本,验证相似度在合理范围内。

    public void testCheckPlagiarismWithSimilarTexts() throws PlagiarismException.EmptyTextException {// 定义原始文本和涉嫌抄袭的文本String originalText = "今天是星期天,天气晴,今天晚上我要去看电影。";String plagiarizedText = "今天是周天,天气晴朗,我晚上要去看电影。";// 调用PlagiarismChecker类的checkPlagiarism方法检查两段文本的相似度double similarity = PlagiarismChecker.checkPlagiarism(originalText, plagiarizedText);System.out.println(similarity);// 验证返回的相似度是否在0到1之间assertTrue(similarity >= 0 && similarity <= 1);// 预期这两段文本的相似度应该很高,因此相似度应大于0.5assertTrue(similarity >= 0.5);
    }
    
  • testCheckPlagiarismWithIdenticalTexts: 测试完全相同的文本,验证相似度为1。

  • testCheckPlagiarismWithDifferentTexts: 测试完全不同的文本,验证相似度接近0。

  • testCheckPlagiarismWithLongTexts: 测试长文本,验证系统能处理大量数据。

  • testTextWithManyStopWords: 测试包含大量停用词的文本,验证相似度在合理范围内。

SimHashCalculatorTest:

  • testGetSimHashWithNormalText: 测试普通文本的SimHash计算。

    public void testGetSimHashWithNormalText() {// 测试普通文本的SimHash计算String text = "This is a test text for SimHash calculation.";String simHash = SimHashCalculator.getSimHash(text);assertNotNull("SimHash不应为空", simHash);assertEquals("SimHash长度应为64位", 64, simHash.length());assertTrue("SimHash应只包含0和1", simHash.matches("[01]+"));
    }
    
  • testGetSimHashWithChineseText: 测试中文文本的SimHash计算,验证多语言支持。

  • testGetSimHashConsistency: 测试SimHash计算的一致性,确保相同输入产生相同结果。

  • testGetSimHashWithSpecialCharacters: 测试包含特殊字符的文本,验证系统的鲁棒性。

FileUtilTest:

  • testReadFileWithNormalContent: 测试读取普通内容的文件。

    public void testReadFileWithNormalContent() throws Exception {// 测试读取普通内容的文件File file = tempFolder.newFile("normal.txt");String content = "This is a test file content.";try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {writer.write(content);}String readContent = FileUtil.readFile(file.getPath());assertEquals("读取的内容应与写入的内容相同", content, readContent);
    }
    
  • testReadFileWithEmptyContent: 测试读取空文件,验证边界情况处理。

  • testReadNonexistentFile: 测试读取不存在的文件,验证异常处理。

  • testWriteResultWithNormalValue: 测试写入正常的相似度值。

  • testWriteResultWithZeroValue: 测试写入零相似度值,验证边界情况处理。

HashUtilTest:

  • testHashWithNormalString: 测试普通字符串的哈希计算。

    public void testHashWithNormalString() {// 测试普通字符串的哈希计算String input = "test string";BigInteger hash = HashUtil.hash(input);assertNotNull("哈希值不应为空", hash);assertTrue("哈希值应大于0", hash.compareTo(BigInteger.ZERO) > 0);
    }
    
  • testHashConsistency: 测试哈希计算的一致性,确保相同输入产生相同哈希值。

  • testHashWithEmptyString: 测试空字符串的哈希计算,验证边界情况处理。

  • testHashWithLongString: 测试长字符串的哈希计算,验证系统对大量数据的处理能力。

image-20240913193705104

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

  • 空输入异常 (NullInputException)

设计目标: 防止空输入导致的潜在空指针异常,提高系统的稳定性。

// NullInputException.java
public class NullInputException extends RuntimeException {public NullInputException(String message) {super(message);}
}// PlagiarismCheckerTest.java
import org.junit.Test;
import static org.junit.Assert.*;public class PlagiarismCheckerTest {@Test(expected = NullInputException.class)public void testNullInput() {PlagiarismChecker.checkPlagiarism(null, "Some text");}
}

错误场景: 当用户提供空文本作为输入时,例如忘记上传文件或文件内容为空。

🧩附录:代码签入记录

image-20240913155800120

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

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

相关文章

面试-JS基础知识-作用域和闭包

问题this的不同应用场景 手写bind函数 实际开发中闭包的应用场景,举例说明 创建10个<a>标签,点击的时候弹出来对应的序号作用域:某个变量的合法使用范围全局 函数 块级** 自由变量上面图的最里面的红框————a a1 a2都是自由变量,因为都没有被定义。会一层一层往上…

学习高校课程-软件设计模式-软件设计原则(lec2)

软件设计原则Feature of Good Design (1) 优秀设计的特点(一) Code reuse 代码复用 – Challenge: tight coupling between components, dependencies on concrete classes instead of interfaces, hardcoded operations – Solution: design patterns – 挑战:组件之间的紧…

ATTCK红队评估(红日靶场4)

靶场介绍本次靶场渗透反序列化漏洞、命令执行漏洞、Tomcat漏洞、MS系列漏洞、端口转发漏洞、以及域渗透等多种组合漏洞,希望大家多多利用。 环境搭建 机器密码 WEB主机 ubuntu:ubuntuWIN7主机 douser:Dotest123(DC)WIN2008主机 administrator:Test2008网络配置111网段是web的网…

Markdown随笔

冰冻三尺非一日之寒,持之以恒方位始终。 Markdown语法讲解标题一共六级标题分别为Ctrl+1~6: 一级 二级 三级 四级 五级 六级字体 粗体 粗斜体 斜体 删除线引用一个大于号>分割线图片超链接 点击进入百度百科列表 数字加上空格(有序) 点加上空格(无序)表格姓名 性别 年…

tarjan里的定义

强连通分量 - OI Wiki (oi-wiki.org)从以u为根的子树中的任意点出发。单次到达(从这个点指向某个点,有一条边) 的这些点中的dfn的最小值以v为根的子树,包含在以u为根的子树中,low[v]所用的子节点,一定也可以被low[u],这个点一定在以u为根的子树里,所以用low[v] 从u这个…

南沙csp-j/s一对一家教陈老师解题:1317:【例5.2】组合的输出

​【题目描述】排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r≤n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。 现要求你用递归的方法输出所有组合。 例如n=5,r=3,所有组合为: 1 2 3 1 2 4 1 2 5 1 3 4 1 …

First day01

Markdown学习 二级标题 字体 Hello World Hello World Hello World Hello World Hello World 引用选择java走上人生巅峰分割线图片 ![截图](C:\Users\邢其俊\Pictures\Screenshots\屏幕截图 2024-09-13 180016.png)超链接 [点击跳转到狂神博客](仓库 - 狂神说 (kuangstudy) - G…

ThreeJS Shader的效果样例光影墙、扩散面(四)

一、实现一个光影墙1. 根据自定义坐标点,输出一个光影墙/*** 添加光影墙*/ function addLightWall() {const geometry = new THREE.BufferGeometry();const vertices = new Float32Array([5, 0, 2,3, 0, 5,-2, 0, 5,-4, 0, 2,-4, 5, 2,-2, 5, 5,3, 5, 5,5, 5, 2]);const indic…

2024年youtube 视频在线下载工具

1.youtube to wav这是一个将 YouTube 视频转换为 WAV 格式的在线工具的网站链接。根据提供的信息,使用该工具的步骤如下: 开始:将 YouTube 视频的 URL 粘贴到搜索框中,然后点击 “Start” 按钮。 转换:选择转换为 WAV 的质量(推荐使用默认选项),然后点击 “Convert” 按…

Idea 配置多端口启动参数

1、编辑配置2、在弹出配置中增加要使用端口接口,添加端口参数配置、设置VM options: 参数值为: -Dserver.port= portNumber下面以应用 TranSendServiceApplication 为例,增加两个端口:8099 和8091 为例。截图如下:

【闲话】假如我们都是猫娘

你是一袋猫粮猫娘驯化实录 ZHESHIWOYAOMOZHENGBEIDISANJIEMOZHENGXIANHUADASAIDECANSAIZUOPIN. (A:Chat-GPT 4.0) (另:因为某些纯魔怔原因,我们连皮下内容也回了)。 A 17:33:41 喵~主人你好呀!我是您的猫娘助手,挪威森林猫品种,身高148cm,梳着双马尾~需要我帮忙做…

chainLink vrf实验

目标 用vrf写一个随机红包 数据结构 红包: struct Envelope {Type t; // 类型,只是erc20 和eth红包ERC20 token; // erc20 ,如果是erc20红包,这里是erc2o的地址address sender; // 发红包的senderuint balance; // 金额bool allowAll; // 允许所有人领取uint32 maxRe…