个人项目
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/ |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13229 |
这个作业的目标 | 设计一个论文查重算法,给出一个原文文件和一个在这份原文上经过了增删改的抄袭版论文的文件,在答案文件中输出其重复率。 |
一、项目GitHub链接
https://github.com/Blueww8/Blueww
二、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
· Estimate | · 估计这个任务需要多少时间 | 40 | 48 |
Development | 开发 | 220 | 240 |
· Analysis | · 需求分析 (包括学习新技术) | 60 | 60 |
Design Spec | · 生成设计文档 | 40 | 60 |
· Design Review | · 设计复审 | 60 | 60 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 | 60 |
· Design | · 具体设计 | 60 | 60 |
· Coding | · 具体编码 | 30 | 30 |
· Code Review | · 代码复审 | 30 | 30 |
· Test | · 测试(自我测试,修改代码,提交修改) | 90 | 100 |
Reporting | 报告 | 60 | 60 |
· Test Repor | · 测试报告 | 60 | 60 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
· 合计 | 880 | 908 |
三、计算模块接口的设计与实现过程
总体设计:
分为四个类:Main , MainTest , SimilarityMain , SimilarityMainTest .
包含的方法:main(String[] args) 方法,testSimilarity() 方法,setUp() 方法,calculateVector(String str, int index) 方法,cosineSimilarity() 方法,similarity()方法,testMain 方法等
类的执行流程:
1.用户首先运行 Main 类中的 main 方法。
2.main 方法中创建了一个 Scanner 对象,用于从控制台获取用户输入的文件路径。
3.用户输入原始论文文件路径、抄袭版论文文件路径和要保存相似度结果的文件路径。
4.如果用户输入的文件路径为空,则程序输出相应的提示信息并结束。
5.否则,程序会创建一个 DecimalFormat 实例,然后使用输入的文件路径创建 SimilarityMain 实例。
6.SimilarityMain 实例的 similarity() 方法被调用来计算两篇论文的相似度。
7.计算得到的相似度结果会被打印到控制台。
8.程序将相似度结果写入用户指定的输出文件,并在控制台上显示操作结果。
9.如果在写入文件时出现异常(IOException),则捕获异常并输出错误信息。
四、计算模块接口部分的性能改进
改进计算模块思路:
在这个地方花费的时间较多,改进时我增加了异常处理机制,引入了语义信息。
性能分析图:
Memory占用图:
五、单元测试
1.主测试 MainTest
代码:
package org.example;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import java.text.DecimalFormat;
import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MainTest {
@Test
@Timeout(value = 2000, unit = TimeUnit.MILLISECONDS)
public void testMain() {
DecimalFormat df = new DecimalFormat("0.00");
double d1 = 0.342453;
String formattedValue = df.format(d1);
// 检查格式化后的值是否符合预期
assertEquals("0.34", formattedValue);
}
}
测试结果:
2.测试 SimilarityMainTest
代码:
package org.example;
import org.junit.Before;
import org.junit.Test;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
public class SimilarityMainTest {
private SimilarityMain similarityMainUnderTest;
@Before
public void setUp() throws NoSuchFieldException, IllegalAccessException {
similarityMainUnderTest = new SimilarityMain("string1", "string2");// 初始化待测试的 SimilarityMain 对象,并传入两个字符串
// 创建实际的 vectorMap 对象
Map<Character, int[]> vectorMap = new HashMap<>();
vectorMap.put('s', new int[]{1, 0});// 将字符's'的向量加入Map
vectorMap.put('t', new int[]{1, 1});
vectorMap.put('r', new int[]{1, 0});
vectorMap.put('i', new int[]{1, 1});
vectorMap.put('n', new int[]{1, 0});
vectorMap.put('g', new int[]{1, 0});
vectorMap.put('1', new int[]{1, 0});
vectorMap.put('2', new int[]{0, 1});
// 使用反射将 vectorMap 设置为 SimilarityMain 类中的 vectorMap
Field vectorMapField = similarityMainUnderTest.getClass().getDeclaredField("vectorMap");// 获取 SimilarityMain 类中的 vectorMap 字段
vectorMapField.setAccessible(true);// 设置字段可访问
vectorMapField.set(similarityMainUnderTest, vectorMap);// 将创建的实际 vectorMap 对象设置到 similarityMainUnderTest 对象中的 vectorMap 字段
}
@Test
public void testSimilarity() {
// 运行测试
final double result = similarityMainUnderTest.similarity();// 调用待测试对象的 similarity 方法得到相似度计算结果
// 验证结果
double expected = 0.4364357804719848;// 预期的相似度计算结果
assertEquals(expected, result, 0.001);// 使用断言验证实际结果和预期结果是否一致,允许误差为0.001
// 打印实际结果和预期结果
System.out.println("实际结果:" + result);
System.out.println("预期结果:" + expected);
}
}
测试思路:
1.在setUp方法中,首先创建一个vectorMap对象,并使用put方法将字符及其对应的向量加入到vectorMap中。
2.使用反射将创建的实际vectorMap对象设置到SimilarityMain类中的vectorMap字段中。
3.在testSimilarity方法中,通过调用待测试对象的similarity方法得到实际的相似度计算结果。
4.将预期结果赋值给expected变量,即预计的相似度计算结果。
5.使用assertEquals断言来比较实际结果和预期结果是否相等,允许误差为0.001。
6.打印实际结果和预期结果,用于查看测试结果。
测试结果:
3.代码覆盖率:
六、运行结果