第一次个人编程作业
作业信息
这个作业属于哪个课程 | 软件工程 |
---|---|
这个作业要求在哪里 | 作业要求 |
这个作业的目标 | 熟悉和掌握Git、Github的使用,熟悉性能改进工具JProfiler,学习文本相似度算法 |
-
jar
包使用示例 :java -Dfile.encoding=utf-8 -jar similarityCheck.jar C:/test1/orig.txt C:/test1/orig_0.8_del.txt C:/test1/answer.txt
CMD默认编码为GBK
,所以需指定JVM的编码为UTF-8
,如果不加上-Dfile.encoding=utf-8
,会造成中文乱码,导致相似率计算不准确。- 运行结果
计算模块接口的设计与实现过程
-
代码组织
-
Main
主函数 -
ConvertString
将输入的文本转化为字符串 -
LevenshteinEdit
计算最短编辑距离 -
Outcome
把结果输入答案文本 -
checkTest
在单元测试,实现对main()的传参
-
-
流程图
-
算法关键
-
采用最短编辑距离
-
莱文斯坦距离,又称Levenshtein距离,是编辑距离的一种。指两个字串之间,由一个转成另一个所需的最少编辑操作次数。允许的编辑操作包括:
-
将一个字符替换成另一个字符
-
插入一个字符
-
删除一个字符
-
-
-
-
最短编辑距离表达
-
算法实现
把文本转化为字符串后,用两个字符串建立一个二维矩阵,利用双重循环,计算上面表达式,选取最小值即为最短编辑距离。
-
独到之处
利用最小编辑距离可以计算从一个字符串到另一个字符串的修改距离,可以减少语句倒装时造成的误差。
计算模块接口部分的性能改进
-
在改进计算模块性能上所花费的时间
- 大概花费了2小时
-
改进思路
- 因为实现的算法共含有2个单循环,1个嵌套循环,时间复杂度为O(n^2),减少字符串的长度可提高运行速度,所以才去掉空格和标点符号
Str1 = Str1.replaceAll("[\\pP\\p{Space}]+", "")
,减少运行时间和误差率。
- 因为实现的算法共含有2个单循环,1个嵌套循环,时间复杂度为O(n^2),减少字符串的长度可提高运行速度,所以才去掉空格和标点符号
-
性能分析图
-
程序中消耗最大的函数
计算模块部分单元测试展示
-
下面为部分测试代码
import static org.junit.Assert.*; public class MainTest { checkTest t = new checkTest(); @Before public void setUp() { System.out.println("Start!"); } @After public void tearDown() { System.out.println("Finish!"); } /* * 超时监测,运行时间超过5s,不能通过测试*/ @Test public void time() throws IOException { try { t.checktest("src/test1/orig.txt", "src/test1/orig_0.8_dis_15.txt", "src/test1/answer.txt"); } catch (IOException e) { e.printStackTrace(); fail(); } } /* * 实际存在的路径*/ @Test public void existPath() throws IOException { try { t.checktest("src/test1/orig.txt", "src/test1/orig_0.8_add.txt", "src/test1/answer.txt"); } catch (IOException e) { e.printStackTrace(); } } /* * 空路径*/ @Test public void nullPath() throws IOException { try { t.checktest("", "src/test1/orig_0.8_add.txt", "src/test1/answer.txt"); } catch (IOException e) { e.printStackTrace(); fail(); } } /* * 不存在的路径*/ @Test public void nonExistPath() throws IOException { try { t.checktest("src/orig.txt", "src/test1/orig_0.8_add.txt", "src/test1/answer.txt"); } catch (IOException e) { e.printStackTrace(); fail(); } } /* * 相同的路径*/ @Test public void samePath() throws IOException { try { t.checktest("src/test1/orig.txt", "src/test1/orig.txt", "src/test1/answer.txt"); } catch (IOException e) { e.printStackTrace(); fail(); } } /* * 原始文本与增加字数的文本对比*/ @Test public void addtext() throws IOException { try { t.checktest("src/test1/orig.txt", "src/test1/orig_0.8_add.txt", "src/test1/answer.txt"); } catch (IOException e) { e.printStackTrace(); fail(); } } /* * 原文与orig_0.8_1文本对比*/ @Test public void dis1text() throws IOException { try { t.checktest("src/test1/orig.txt", "src/test1/orig_0.8_dis_1.txt", "src/test1/answer.txt"); } catch (IOException e) { e.printStackTrace(); fail(); } } }
-
共有11个测试函数,函数列表如下:
-
构造测试函数的思路:
- 一开始的思路是利用循环,用原文与其他的文件进行比对,计算并打印测试结果。
- 学习了单元测试以后,改用Java的一个单元测试框架
Junit
进行测试,在构造这个问题上,从作业要求中的测试点得到启发,设置超时(5s)监测,并通过是否抛出异常来测试程序是否能正常运行。以上的测试数据设置还存在一个大的缺点,没有针对测试结果(相似度)进行测试,因为文本字数较多,在测试前暂时没有办法得到一个百分百准确的数值进行比对。
-
测试覆盖率截图
计算模块部分异常处理说明
-
异常的设计目标(IO异常)
-
java.io.FileNotFoundException
-
单元测试样例
/* * 不存在的路径*/ @Test public void nonExistPath() throws IOException { try { t.checktest("src/orig.txt", "src/test1/orig_0.8_add.txt", "src/test1/answer.txt"); } catch (IOException e) { e.printStackTrace(); fail(); } }
-
错误对应的场景:输入的文件路径错误
-
-
java.lang.NullPointerException
-
单元测试样例
/* * 空路径*/ @Test public void nullPath() throws IOException { try { t.checktest("", "src/test1/orig_0.8_add.txt", "src/test1/answer.txt"); } catch (IOException e) { e.printStackTrace(); fail(); } }
-
错误对应的场景:输入的文件路径为空路径
-
-
PSP
*PSP2.1* | *Personal Software Process Stages* | *预估耗时(分钟)* | *实际耗时(分钟)* |
---|---|---|---|
Planning | 计划 | 30 | 30 |
· Estimate | · 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 1800 | 2410 |
· Analysis | · 需求分析 (包括学习新技术) | 360 | 600 |
· Design Spec | · 生成设计文档 | 60 | 30 |
· Design Review | · 设计复审 | 30 | 10 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 20 |
· Design | · 具体设计 | 120 | 100 |
· Coding | · 具体编码 | 600 | 800 |
· Code Review | · 代码复审 | 240 | 400 |
· Test | · 测试(自我测试,修改代码,提交修改) | 360 | 450 |
Reporting | 报告 | 210 | 160 |
· Test Repor | · 测试报告 | 60 | 120 |
· Size Measurement | · 计算工作量 | 30 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 120 | 30 |
· 合计 | 2040 | 2600 |
收获与感想
-
完成这个作业后,对Github和Git的使用更加地熟悉了,以前曾经接触过,但是使用的频率比较少,所以一直都不怎么会用,现在使用虽说不是非常熟练,但大概明白流程。
-
通过这个编程作业,学习了文本相似度的算法,目前了解到的有余弦相似度+分词,最小编辑距离。我采用的是最短编辑距离算法,这个算法实现的逻辑简单,但是有着致命的缺点,时间复杂度为O(n^2),在处理较大的文本时,所耗费的时间非常多。接下来打算用余弦相似度+分词实现一次这个功能。