第一次个人编程作业


第一次个人编程作业:论文查重

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

  • 类:

    1.readToString
    包含方法 String readToString(String fileName)
    读入文件名,把文件转为字符串。
    2.getCos
    包含方法 double getCos(String s1, String s2)
    计算两个字符串的余弦值。
    3.writeToAnswer
    包含方法 void writeIntoAnswer(double sum,String ans)
    将结果写入ans文件。
    
  • 流程图

  • 实现过程
     大致的过程在类中已经说明,这里主要写一下核心算法——余弦相似性的应用。
     先贴上参考资料: TF-IDF与余弦相似性的应用
     资料中是利用词频计算余弦相似性,这里不再赘述。讲讲踩的坑,我先尝试用字频方法写了一下,结果相当不理想:只有rep的结果接近0.8,其他大部分都在0.98左右,dis的几个文件结果甚至达到1。打开文件一看发现dis文本基本就是原文换个顺序或者加个回车(顺便一提一开始写的是对比每行的重复率,发现dis文本的重复率都奇低,因为中间插了回车= =直接拉胯)。到这里我想着用词频判断会不会好一点,于是又把字换成词(感谢安利我hanlphxd)。but,情况没有改善……我冷静下来仔细想想,这整个文本去掉标点,还不计顺序,词频和字频的区别也确实不大……
     总之待在坑里我想到另一个方法,原理还是算余弦,这次把所有词的出现位置用Map<String,Vector<Integer> >记录下来,于是每个词在两个文本中都对应有一个向量,也就是获得了这个词在两个文本中的位置向量,接下来可以计算它们的余弦值了。

  • 例:

    句子A:普通的DISCO我们普通的摇
    
    句子B:旁边普通的路人在普通的瞧
    

如何计算以上两个句子的余弦值?

  • 第一步:分词
    在maven中配置hanlp,网上能搜到教程就不多说了,以及这里也踩到坑了,jdk版本太高居然还会配置失败……附个pom.xml文件的图吧

    搞半天终于成功了,泪目。

  • 第二步:列出所有词,存入它们出现的位置,注意只判断汉字

    句子A:普通0 的1 我们2 普通3 的4 摇5
    句子B:旁边0 普通1 的2 路人3 在4 普通5 的6 瞧7

  • 第三步:得到两个文本中所有词的位置向量

举个栗子:

  普通:句子A:[0,3] 句子B:[1,5]
  • 第四步:计算两个文本中“普通”的余弦值:cosθ=\(\frac {0×1+3×5}{\sqrt {0+3^2}×\sqrt {1^2+5^2}}\)≈0.98

余弦值可以表示两个向量之间的夹角大小,夹角越小,也就是余弦值越大,两个向量就越相似。(只是我这个不是二维的向量,其实我也不知道能不能叫向量……

使用TreeMap存储词位置向量也是因为要确保是两个文本中相同的词对比,如果某个词只在一个文本中出现就不会被计算到。

  • 第五步:计算所有词的余弦值,同时记录词数。所有余弦值相加除以词数就是最终结果了。

结果总算能看了。

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

主要的改进思路都在实现过程↑里说了,也就是把词频向量改成词位置向量。如果没用词位置而是字位置应该会更慢,也更耗内存。少踩了一个坑导致现在不知道性能改进怎么写。

性能分析

  • Overview
  • Live memory

毕竟每个词都要开两个Vector ……理所当然的吃内存。

  • CPU views

没想到是matches消耗最大。主要是用来判断汉字了,也就是两个文本的每个字符都要判断一遍……这样想想也挺正常,但是这个好像无法避免啊……

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

新增了重复率0和1的测试,其他几个用的是作业提供的样例。主要测试getCos方法。

  • 测试代码
import org.junit.Assert;
import static org.junit.Assert.*;
public class mainTest {

    @org.junit.Test
    public void origAndOrig() {
        String s1=readToString.readToString("testfile/orig.txt");
        String s2=readToString.readToString("testfile/orig.txt");
        double sum=getCos.getCos(s1,s2);
        Assert.assertEquals(1.0,sum,0);
    }
    @org.junit.Test
    public void completeDiff(){
        String s1="乘风破浪会有时";
        String s2="直挂云帆济沧海";
        double sum=getCos.getCos(s1,s2);
        Assert.assertEquals(0,sum,0);
    }
    @org.junit.Test
    public void origAndAdd() {
        String s1=readToString.readToString("testfile/orig.txt");
        String s2=readToString.readToString("testfile/orig_0.8_add.txt");
        double sum=getCos.getCos(s1,s2);
        Assert.assertEquals(0.8,sum,0.2);
    }
    @org.junit.Test
    public void origAndDel() {
        String s1=readToString.readToString("testfile/orig.txt");
        String s2=readToString.readToString("testfile/orig_0.8_del.txt");
        double sum=getCos.getCos(s1,s2);
        Assert.assertEquals(0.8,sum,0.2);
    }
    @org.junit.Test
    public void origAndMix() {
        String s1=readToString.readToString("testfile/orig.txt");
        String s2=readToString.readToString("testfile/orig_0.8_mix.txt");
        double sum=getCos.getCos(s1,s2);
        Assert.assertEquals(0.8,sum,0.2);
    }

后面的几个代码大同小异,就是改了个文件名,这里不放了。

  • 测试结果

  • 测试覆盖率


核心方法getCos覆盖率为100%,readIntoString没覆盖到的都是catch块。

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

参考资料

  • 空文本异常
      //测试
        try{
            throw new EmptyTextException("Empty Text!");
        }catch(EmptyTextException e){
            e.printStackTrace();
        }
public class EmptyTextException extends Exception{
    public EmptyTextException(){
        super();
    }
    public EmptyTextException(String message){
        super(message);
    }
    public EmptyTextException(String message,Throwable cause){
        super(message,cause);
    }
    public EmptyTextException(Throwable cause){
        super(cause);
    }
}

  • 测试结果

5.PSP表格

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

6.遇到的问题

  • 问题1
    创建测试的方法培训文档里讲的有点模糊,点击类名(我找了半天Solve在哪,原来是就是它的类名……),alt+enter就可以创建测试了。

  • 问题2

    照着预培训文档里面写,结果编译器报不能比较浮点数……?

  • 解决方法:应该用assertEquals(expected, actual, delta) 比较浮点数大小,只要期望值和声明值之间的差值小等于delta,那么就断言相等。Math.abs(expected - actual) <= delta

    这样写就没问题了

  • 问题3
    导jar包是最费劲的……好几次导出来结果命令窗口中执行出错。
    提示没有主清单解决方法:用WinRAR打开jar包,找到这个文件
    打开添加Main-Class: main,注意冒号后有空格。

    还有这个问题
    至今没明白具体原因,搜索到的答案基本都是说路径配置出错,然而我编译器里就运行的好好的……这种情况一般是导出的时候没设置好,就猜+搜+搞,莫名其妙就行了。

7.总结

我变秃了,也变强了

  • 第一次用jprofiler,第一次用git,第一次配置maven……学到了很多东西,主要是增强了查资料的能力……整个过程走下来写代码反而是最轻松的,学习新东西是最难的,单一个git的用法就研究了一下午。找到的教程总是走到一半就出问题,然后又去百度解决方法,怎么导jar包,怎么配置maven,怎么用git push,于是浏览器常常挂着好几个窗口。在过程中也意识到自己跟别人的差距,在别人眼里可能是常识的东西我得搞几小时,好在最后都顺利解决了。
  • 编码过程中同样遇到了一些困难,因为Java语言还不太熟练,写代码的时候下意识的想用C的东西,有时候写到一半开始面向百度编程。比如存词向量的存储,开始写了个map<string,vector >offset1,编译器直接报错,在编译器的提示下蒙出了正确写法Map<String, Vector > Offset1=new TreeMap<String,Vector >();
    ……害,积累的东西太少,现在开始吃亏了,好在这也是一个积累的过程,今后的学习和工作中总会派上用场吧。

“你的负担将变成礼物,你受的苦将照亮你的路。”


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM