个人第一次编程作业

这个作业属于哪个课程 班级链接
这个作业要求在哪里 作业链接
这个作业的目标 论文查重算法设计+学习PSP表格+单元测试+JProfiler性能分析+Git管理

1 代码链接(Java)

1.1 Github链接

1.2 可运行的jar包已经发布至仓库的release包内


2 计算模块接口的设计与实现过程

2.1 主要的类

  • IoProcess类:进行文本输入输出的操作,文本和字符串的相互转换

  • MyApplication类:主程序

  • MyApplicationTest类:对程序进行单元测试

  • MySimHash类(关键类):对字符串进行分词、获取哈希值、加权、合并、降维操作,并计算两字符串的海明距离,计算相似度



2.2 关键类的关键函数

2.2.1 getSimHash()

  • 分词:调用HanLp包,对全部字符串进行分词

  • 获取哈希值:调用hash(),获取每个词的哈希值,256bits

  • 加权和合并:根据词语的索引进行加权,例如 0~9 的权重为10/-10, 10~19的权重为9/-9,依次递减,并将相同索引的hash值通过相加进行合并

  • 降维:对每个索引的哈希值大于0的置为1,小于0的置为0

2.2.2 例子

  • 分词,我们假设权重分为5个级别(1~5)。比如:“今天是星期天,天气晴,今天晚上我要去看电影 ” ==> 分词后为 “ 今天/ 是/星期/天/天气/晴/晚上/我/要去/看电影“,分词。

  • hash,通过hash算法把每个词变成hash值,比如“今天”通过hash算法计算为 110101(算法中是设置了196bit的值),“是”通过gethash算法计算为 101011。这样我们的字符串就变成了一串串数字。

  • 加权,通过 2步骤的hash生成结果,需要按照单词的权重(单词权重按照先后顺序划分,上面有说过)形成加权数字串,比如“今天”的hash值为“110101”,通过加权计算为“10 10 -10 10 -10 10”;“是”的hash值为“101011”,通过加权计算为 “ 9 -9 9 -9 9 9”。

  • 合并,把上面各个单词算出来的序列值累加,变成只有一个序列串。“10+9 10-9 -10+9 10-9 -10+9 10+9”,这里为了方便,没有将所有的都写出来,实际上是将所有的进行相加。

  • 降维,把4步算出来的 “19 1 -1 1 -1 19” 变成 0 1 串,形成我们最终的simhash签名。 如果每一位大于0 记为 1,小于0 记为 0。最后算出结果为:“1 1 0 1 0 1”。

2.2.3 getSembalance()

​ 对比两个哈希值相同索引,如果不同,海明距离加1,最后计算相似度,保留两位小数

2.3 算法的关键以及独到之处

2.3.1 介绍

​ 运用simHash,而不是直接运用Hash,simhash一种局部敏感的hash值,本文主要目的是为比较两文本的相似度,如果使用传统的hash,无法实现目的。

2.3.2 例子

​ “你妈妈喊你回家吃饭哦,回家罗回家罗” 和 “你妈妈叫你回家吃饭啦,回家罗回家罗”。
​ 通过simhash计算结果为:

  • 1000010010101101111111100000101011010001001111100001001011001011

  • 1000010010101101011111100000101011010001001111100001101010001011

    通过传统hash计算为:

  • 0001000001100110100111011011110

  • 1010010001111111110010110011101

    可见,simhash变化的只有很小一部分,而hash值发生了很大改变,因此不能用传统的hash

2.4 分包截图


2.5 实际命令行运行效果



3 计算模块接口部分的性能改进

执行单元测试,对各种情况使用JProfiler对性能进行监控

3.1 类的占用情况



3.2 CPU Load



3.3堆内存情况



3.4 耗时操作




可见,耗时最多的是hanlp分词操作,因此性能瓶颈在于Hanlp分词操作函数,所以我并没有对其他地方进行改动来提升程序的性能

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

4.1 采用白盒测试,对原文件改进,空文件、添加、删除、错别字,打乱等测试

单元测试代码如下

/**
 *Test 1:test the empty txtFile
 */
@Test
public void testForEmpty(){
try {
    MyApplication.process("test/orig.txt", "test/empty.txt", "test/ansAll.txt");
	} 
    catch (Exception e) {
    e.printStackTrace();
    Assert.fail();
}
}
/**
 * Test 3:Test 20% text addition: orig_0.8_add.txt
 */
  @Test
  public void testForAdd(){
  try {         			     	                                            			   MyApplication.process("test/orig.txt","test/orig_0.8_add.txt","test/ansAll.txt"); 
     }
     catch (Exception e) {
       e.printStackTrace();
       Assert.fail();
     }
  }
/**
 * Test 4:Test 20% text deletion: orig_0.8_del.txt
 */
     @Test
     public void testForDel(){
       try {
        MyApplication.process("test/orig.txt","test/orig_0.8_del.txt","test/ansAll.txt");
       }
       catch (Exception e) {
           e.printStackTrace();
       }
         }
/**
 * Test 5:Test 20% out-of-order text: orig_0.8_dis_1.txt
 */
   @Test
   public void testForDis1(){
   	try {
     MyApplication.process("test/orig.txt","test/orig_0.8_dis_1.txt","test/ansAll.txt");
     }
     catch (Exception e) {
         e.printStackTrace();
     }
    }
 /**
  * Test 6:Test 20% out-of-order text: orig_0.8_dis_10.txt
  */
 @Test
 public void testForDis10(){
   try {         MyApplication.process("test/orig.txt","test/orig_0.8_dis_10.txt","test/ansAll.txt");
   }
   catch (Exception e) {
     e.printStackTrace();
    }
 }
/**
 * Test 7:Test 20% out-of-order text: orig_0.8_dis_15.txt
 */

    @Test
    public void testForDis15(){
        try {
            MyApplication.process("test/orig.txt","test/orig_0.8_dis_15.txt","test/ansAll.txt");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
/**
 * Test 8:Test the same text: orig.txt
 */
     @Test
     public void testForSame(){
     try {
         MyApplication.process("test/orig.txt","test/orig.txt","test/ansAll.txt");
       }
       catch (Exception e) {
           e.printStackTrace();
       }
         }

4.2 测试结果



4.3 代码覆盖率



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

5.1 测试文件输入参数出错的情况

代码如下(只展示部分代码):

/**
*Test 2:The case where the entered comparison text path parameter is an incorrect parameter
*/
public void testForWrongOriginArgument(){
    try {
    MyApplication.process("test/123.txt","test/orig_0.8_add.txt","test/ansAll.txt");
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

测试结果:


5.2 测试文件输出参数出错的情况

代码如下(只展示部分代码):

/**
 *Test14: Test if the output file path parameter is an error parameter
 */
@Test
public void testForWrongOutputArgument(){
    try {
        MyApplication.process("test/orig.txt","test/orig.txt","test/testAWrongArgumentResult.txt");
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

测试结果:



5.3 测试文件为空的情况

代码如下(只展示部分代码):

/**
 * Test 1:test the empty txtFile
 */
@Test
public void testForEmpty(){
    try {
        MyApplication.process("test/orig.txt", "test/empty.txt", "test/ansAll.txt");

    } catch (Exception e) {
        e.printStackTrace();
        Assert.fail();
    }
}

测试结果:



6 PSP表格

PSP2.1 Personal Software Process Stage 预估耗时(分钟 实际耗时(分钟)
Planning 计划 20 30
Estimate 估计这个任务需要多少时间 60 60
Development 开发 600 600
Analysis 需求分析 (包括学习新技术) 240 300
Design Spec 生成设计文档 70 60
Design Review 设计复审 30 30
Coding Standard 代码规范 (为目前的开发制定合适的规范) 30 20
Design 具体设计 60 70
Coding 具体编码 200 240
Code Review 代码复审 40 60
Test 测试(自我测试,修改代码,提交修改) 240 300
Reporting 报告 160 180
Test Repor 测试报告 60 60
Size Measurement 计算工作量 40 40
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 60 60
合计 1910 2110

7 代码质量检查Code Quantity Analysis

代码质量检查,用的是codacy,主要问题出现在类名和方法名的命名上,复习了正确的命名方式

对于类名,大驼峰式,符合正则表达式:[A-Z]a-zA-Z0-9

对于方法名,小驼峰式,符合正则表达式:[a-z]a-zA-Z0-9


8 总结

  • 因为本人没有完全写过一个类似于这种的开发程序,所以耗时比较久,主要耗时在于Git和Github的学习上,如何将本地文件上传到仓库上;
  • 耗时比较多的地方还有对于jar包的打包上,因为没有将Maven等依赖包一起打包进去,所以导致出现错误,最后询问了朋友才解决了问题;
  • 以及之前没有接触过的codacy、Jprofiler和单元测试,所以学习新的工具也需要一定的时间
posted @ 2021-09-14 19:21  Beamice  阅读(260)  评论(0编辑  收藏  举报