目录
1.作业基本信息
这个作业属于哪个课程 | 软件工程3班 |
---|---|
这个作业要求在哪里 | 个人项目作业-论文查重 |
这个作业的目标 | 1.熟悉git操作 2.学习使用PSP表格 3.编写论文查重程序 4.学习使用JProfile改进程序 5.学习使用单元测试进行开发 |
代码仓库 | 仓库 |
参考文献 | Java实现余弦定理计算文本相似度 HanLP |
2.PSP表格
PSP | Personal Software Process Stages | 预计耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 25 |
Estimate | 估计这个任务需要多少时间 | 5 | 5 |
Development | 开发 | 510 | 980 |
· Analysis | 需求分析 (包括学习新技术) | 120 | 300 |
· Design Spec | 生成设计文档 | 20 | 10 |
· Design Review | 设计复审 | 20 | 10 |
· Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 20 | 10 |
· Design | 具体设计 | 30 | 30 |
· Coding | 具体编码 | 180 | 400 |
· Code Review | 代码复审 | 60 | 100 |
· Test | 测试(自我测试,修改代码,提交修改) | 60 | 120 |
Reporting | 报告 | 110 | 120 |
· Test Report | 测试报告 | 60 | 60 |
· Size Measurement | 计算工作量 | 20 | 30 |
· Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 655 | 1130 |
3.计算模块接口的设计与实现过程
3.1类和方法
类 | 方法 | 备注 |
---|---|---|
main |
|
|
TxtUtil |
|
|
CosineSimilarityUtil |
|
|
3.2类、方法之间的调用关系
main类调用CosineSimilarityUtil类进行计算两文本的相似度,调用TxtUtil类进行获取文本内容以及写内容到文档文件中。
在CosineSimilarityUtil类中,重点是computeTxtSimilar方法,它调用类中的其余方法,计算出余弦值。
3.3 关键方法computeTxtSimilar()
相似度度量(Similarity),即计算个体间的相似程度,相似度度量的值越小,说明个体间相似度越小,相似度的值越大说明个体差异越大。
对于多个不同的文本或者短文本对话消息要来计算他们之间的相似度如何,一个好的做法就是将这些文本中词语,映射到向量空间,形成文本中文字和向量数据的映射关系,通过计算几个或者多个不同的向量的差异的大小,来计算文本的相似度。下面介绍一个详细成熟的向量空间余弦相似度方法计算相似度
向量空间余弦相似度(Cosine Similarity)
余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小。余弦值越接近1,就表明夹角越接近0度,也就是两个向量越相似,这就叫"余弦相似性"。
利用 HanLP 中的 segment 方法对原文本进行中文分词,把关键词组(名词、动词、形容词、动名词等)加入到词库,根据单词数组建立频率表和词汇表,然后根据频率表得到词向量,最后根据词向量进行向量余弦值的计算。
使用余弦值作为向量相似度的一个衡量:余弦值越接近于1,两向量相似度越高;反之,余弦值越接近于-1,两向量相似度越低。
4.计算模块接口部分的性能改进
改进计算模块性能上所花费的时间:30min
主要优化了某些方法循环中的判断条件,避免不必要的判断。
JProfile性能分析图
Windows Async Profiler生成的方法调用图
程序中消耗最大的函数
/**
* 计算文档的相似度
*/
public static double computeTxtSimilar(String txtLeft, String txtRight){
List<String> totalWordList = new ArrayList<>();
//获取词频
Map<String, Integer> leftWordCountMap = getWordCountMap(txtLeft,totalWordList);
Map<String, Float> leftWordFrequency = calculateWordFrequency(leftWordCountMap);
Map<String, Integer> rightWordCountMap = getWordCountMap(txtRight,totalWordList);
Map<String, Float> rightWordFrequency = calculateWordFrequency(rightWordCountMap);
//获取特征值
List<Float> leftFeature = getTxtFeature(totalWordList,leftWordFrequency);
List<Float> rightFeature = getTxtFeature(totalWordList,rightWordFrequency);
float leftVectorSqrt = calculateVectorSqrt(leftWordFrequency);
float rightVectorSqrt = calculateVectorSqrt(rightWordFrequency);
float numerator = getNumerator(leftFeature,rightFeature);
double cosValue = 0;
if(numerator > 0){
cosValue = numerator / (leftVectorSqrt * rightVectorSqrt);
}
cosValue = Double.parseDouble(String.format("%.2f",cosValue*100));
return cosValue;
}
5.计算模块部分单元测试展示
5.1测试main方法
部分代码展示
public class mainTest {
@Test
public void maintest(){
//全部参数都正确填写
String originPath1= "C:\\Users\\15616\\Desktop\\orig.txt";
String copyPath1 = "C:\\Users\\15616\\Desktop\\copy2.txt";
String answerPath1 = "C:\\Users\\15616\\Desktop\\111.txt";
String[] args = new String[3];
args[0] = originPath1;
args[1] = copyPath1;
args[2] = answerPath1;
main.main(args);
//缺少某个参数
String originPath2= "C:\\Users\\15616\\Desktop\\orig.txt";
// String copyPath2 = "C:\\Users\\15616\\Desktop\\copy2.txt";
String answerPath2 = "C:\\Users\\15616\\Desktop\\111.txt";
args[0] = originPath2;
args[1] = "";
args[2] = answerPath2;
main.main(args);
//存在文件格式不合格
String originPath3= "C:\\Users\\15616\\Desktop\\orig";
String copyPath3 = "C:\\Users\\15616\\Desktop\\copy2.txt";
String answerPath3 = "C:\\Users\\15616\\Desktop\\111.txt";
args[0] = originPath3;
args[1] = copyPath3;
args[2] = answerPath3;
main.main(args);
}
}
测试覆盖率截图
6.计算模块部分异常处理说明
6.1文件不存在或不合格式时的异常处理
/**
*读取文档
*/
public static List<String> readTxt(String path) {
try {
if(! path.endsWith(".txt")){
System.out.println("文件错误,请检查路径!");
return null;
}
List<String> txt = new ArrayList<>();
FileReader fileReader = new FileReader(path);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = bufferedReader.readLine();
while(line != null){
txt.add(line);
line = bufferedReader.readLine();
}
bufferedReader.close();
fileReader.close();
return txt;
}catch (IOException e){
e.printStackTrace();
System.out.println("文件错误,请检查路径!");
}
return null;
}
单元测试
@Test
public void readTxtTest() throws IOException {
//路径正确
List<String> test1 = TxtUtil.readTxt("C:\\Users\\15616\\Desktop\\markdown.txt");
System.out.println(test1);
//文件格式错误
List<String> test2 = TxtUtil.readTxt("C:\\Users\\15616\\Desktop\\markdown.exe");
System.out.println(test2);
//路径为空
List<String> test3 = TxtUtil.readTxt("");
System.out.println(test3);
//文件不存在
List<String> test4 = TxtUtil.readTxt("C:\\Users\\15616\\Desktop\\mark.txt");
System.out.println(test4);
}