第一次个人编程作业

这个作业属于哪个课程 软件工程
这个作业要求在哪里 作业要求
这个作业的目标 完成一个论文查重系统,并学会使用Git、JProfiler、JUnit等工具
作业的GitHub地址

一、PSP表格
====
PSP2.1 Personal Software Process Stages
:-- :--
Planning 计划
Estimate · 估计这个任务需要多少时间
Development 开发
Analysis 需求分析 (包括学习新技术)
Design Spec 生成设计文档
Design Review 设计复审
Coding Standard · 代码规范 (为目前的开发制定合适的规范)
Design 具体设计
Coding 具体编码
Code Review 代码复审
Test 测试(自我测试,修改代码,提交修改)
Reporting 报告
Test Repor 测试报告
Size Measurement 计算工作量
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划
Total 合计

二、模块接口的设计与实现
====

(一)读写文件模块

  在main方法中提取数组参数args,来获得在命令行输入的文件地址。再用简单的IO接口即可实现。此处需要注意与cmd的编码是否一致,若不一致,需要在输入的时候转换编码。

(二)文本过滤

  为了让文本更好地进行分词操作,需要对文本进行过滤。主要是用Jsoup来过滤html标签,再利用String的replaceAll方法与正则表达式过滤无关内容。

(三)核心算法(SimHash)

1、算法思路

  将原始文本映射为64位二进制数字串(因此选择了BigInteger,BigDecimal更高精度的类),然后通过对比两文本映射后的数字串的差异来计算相似度。

2、具体步骤

1、分词。把文本分解出多个关键词,并为每个关键词加上权重。在本项目中,我用Jsoup类等对文本进行过滤后,再用StandardTokenizer类对文本进行分词,同时该类会根据每个词的词性来进行赋权。
2、计算hash值。通过hash算法,为每个分词赋予一个hash值。
3、加权。在得到分词的hash值后,需要按照分词的权重形成加权数字串。比如“计算机”的hash值是“100101”,权重为5,通过加权得到“5 -5 -5 5 -5 5”。
4、合并。在每个分词都进行加权运算后,把得到的值相累加,得到一个数字串。比如“计算机”加权后的值是“5 -5 -5 5 -5 5”,“键盘”加权后的值是“4 -4 -4 4 -4 4”,两次通过合并运算后得到“9 -9 1 -1 1 9”。
5、降维。在所有分词都进行合并运算后,把得到的值变成01串。每一位如果大于0,则该位变为1,否则为0。


 经过以上步骤,就能得到一份文本的SimHash值。

3、具体代码

    //计算simhash值
    public BigInteger simHash(String content) {
        content = cleanWords(content);
        setWeight();

        //content内各关键字权值数组,用于合并运算
        int[] array = new int[hashBits];

        //把内容进行分词操作
        List<Term> terms = StandardTokenizer.segment(content);

        for (Term term : terms) {

            //判断该分词是否该被过滤
            boolean flag = wordFilter(term);
            if (flag == true) {
                continue;
            }

            //计算关键词的哈希值
            BigInteger hash = hash(term.word);
            //关键词的权值
            Integer weight = natureWeight.get(term.nature.toString());
            if (null == weight) {
                weight = 1;
            }

            //加权与合并操作
            addWeight(hash, weight, array);

        }

        //降维操作后返回simHash值
        return subDimension(array);
    }

(四)计算相似度

  两文本在经过以上步骤后,得到各自的SimHash值。若两者内容相似,则SimHash值也相近,两者相除即可得到相似度。

//文本对比
    public String compare(String content1, String content2) {

        BigDecimal simHash1 = new BigDecimal(simHash(content1));
        BigDecimal simHash2 = new BigDecimal(simHash(content2));

        BigDecimal result;
        //较小的simhash作为被除数,较大的作为除数,商即为相似度,越接近1越相似。
        if (simHash1.compareTo(simHash2) > -1) {
            result = simHash2.divide(simHash1, 4, RoundingMode.HALF_UP);
        } else {
            result = simHash1.divide(simHash2, 4, RoundingMode.HALF_UP);
        }
        return result.toString();
    }

三、性能分析 ==== ![](https://img2020.cnblogs.com/blog/2148079/202009/2148079-20200924225751426-1978353744.png) ![](https://img2020.cnblogs.com/blog/2148079/202009/2148079-20200924225757359-7664009.png) **能改进的地方:**计算的准确度还有待提高。
四、单元测试 ==== ##1、测试过滤 ![](https://img2020.cnblogs.com/blog/2148079/202009/2148079-20200924214954250-1591033566.png)

2、SimHash计算测试

3、文本相似度测试

4、异常处理测试


五、异常处理 ====  主要是输入和输出文件时进行IO操作时容易出现IOException,比如输入了不存在文件地址或者文件正在被访问。在该部分用try-catch即可解决。 ![](https://img2020.cnblogs.com/blog/2148079/202009/2148079-20200924214110119-386737463.png)
个人心得 ====   这份作业难点主要是在算法上。一开始我自己也想过很多算法,但都过于复杂而不能实现。这份代码参考了别人的项目,有些部分实在不懂(hash的算法),就照着写一次,虽然仍有地方不太懂,但照着写了一遍感觉很多地方都想通了。而且在这次作业中,我也学到了一些工具的使用,能为我今后的学习带来很大的便利。
posted @ 2020-09-24 23:41  Oldlynn  阅读(142)  评论(0编辑  收藏  举报