论文查重

作业所属班级 软件工程2024
作业要求 个人项目
作业目标 实现查重功能

这是我的GitHub仓库链接

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 30
Estimate* 估计这个任务需要多长时间 20 15
Development 开发 720 880
Analysis 需求分析(包括学习新技术) 30 120
Design Spec 生成设计文档 60 40
Design Review 设计复审 20 30
Coding Standard 代码规范 10 15
Design 具体设计 60 80
Coding 具体编码 300 180
Code Review 代码复审 20 15
Test 测试 100 60
Reporting 报告 40 30
Test Report 测试报告 20 10
Size Measurement 计算工作量 20 15
Postmortem & Process Improvment Plan 事后总结,并提出过程的改进计划 40 30
合计 740 910

计划

先实现文件的读取操作,再进行查重率的算法实现

开发

开发环境

编程语言:Java
IDE:Intellij IDEA
项目构建工具:maven
单元测试:JUnit
性能分析工具:JProfiler
依赖的外部 jar 包:汉语言处理包

点击查看代码
<dependency>
            <groupId>com.hankcs</groupId>
            <artifactId>hanlp</artifactId>
            <version>portable-1.5.4</version>
        </dependency>

开发流程

1、读取txt文件,转化为string类型的字符串
2、分词:取出所有关键词
3、计算出 SimHash值,计算出海明距离,求相似度
4、将结果写入指定文件

Main:main 方法所在的类
HammingUtils:计算海明距离的类
SimHashUtils:计算 SimHash 值的类
TxtInOutUtils:读写 txt 文件的工具类
ShortStringException:处理文本内容过短的异常类

核心算法

求相似度步骤分为五步 分词->Hash->加权->合并->降维
参考资料

接口设计

读写Txt文件的模块

类:TxtInOutUtils
包含了两个方法
1、readTxt:读取txt文件
2、writeTxt:写入txt文件

SimHash模块(核心模块)

包含了两个方法
1、getHash:传入String,计算出它的hash值,并以字符串形式输出,(使用了MD5获得hash值)
2、getSimHash:传入String,计算出它的simHash值,并以字符串形式输出,(需要调用 getHash 方法)
getSimHash 是核心算法,主要流程如下:
1、分词

点击查看代码
List<String> keywordList = HanLP.extractKeyword(str, str.length());
2、获取Hash
点击查看代码
 StringBuilder keywordHash = new StringBuilder(getHash(keyword));
            if (keywordHash.length() < 128) {
                // hash值可能少于128位,在低位以0补齐
                int dif = 128 - keywordHash.length();
                for (int j = 0; j < dif; j++) {
                    keywordHash.append("0");
                }
            }
3、加权、合并
点击查看代码
  for (int j = 0; j < v.length; j++) {
                // 对keywordHash的每一位与'1'进行比较
                if (keywordHash.charAt(j) == '1') {
                    //权重分10级,由词频从高到低,取权重10~0
                    v[j] += (10 - (i / (size / 10)));
                } else {
                    v[j] -= (10 - (i / (size / 10)));
                }
            }
            i++;
        }
4、降维
点击查看代码
  StringBuilder simHash = new StringBuilder();
        for (int k : v) {
            // 从高位遍历到低位
            if (k <= 0) {
                simHash.append("0");
            } else {
                simHash.append("1");
            }
        }

海明距离模块

1、getHammingDistance:输入两个 simHash 值,计算出它们的海明距离 distance

点击查看代码
 int distance = 0;
        if (simHash1.length() != simHash2.length()) {
            // 出错,返回-1
            distance = -1;
        } else {
            for (int i = 0; i < simHash1.length(); i++) {
                // 每一位进行比较
                if (simHash1.charAt(i) != simHash2.charAt(i)) {
                    distance++;
                }
            }
        }
2、getSimilarity:输入两个 simHash 值,调用 getHammingDistance 方法得出海明距离 distance,在由 distance 计算出相似度。
点击查看代码
return 0.01 * (100 - (double) (distance * 100) / 128);

性能分析

从分析图可以看到:
调用次数最多的是com.hankcs.hanlp包提供的接口, 即分词、取关键词与计算词频花费了最多的时间。
在性能上优化取决于该算法的突破

单元测试

读写TXt文件模块的测试

基本思路:
1、测试正常读取
2、测试正常写入
3、测试错误读取
4、测试错误写入

点击查看代码

    @Test
    public void readTxt() {
        // 路径存在,正常读取

        String str = TxtInOutUtils.readTxt("D:/test/orig.txt");
        String[] strings = str.split(" ");
        for (String string : strings) {
            System.out.println(string);
        }
    }

    @Test
    public void writeTxt() {
        // 路径存在,正常写入
        double[] elem = {0.11, 0.22, 0.33, 0.44, 0.55};
        for (double v : elem) {
            TxtInOutUtils.writeTxt(v, "D:/test/ans.txt");
        }
    }
    @Test
    public void readTxtFailTest() {
        // 路径不存在,读取失败
        TxtInOutUtils.readTxt("D:/test/none.txt");
    }
    @Test
    public void writeTxtFailTest() {
        // 路径错误,写入失败
        double[] elem = {0.11, 0.22, 0.33, 0.44, 0.55};
        for (double v : elem) {
            TxtInOutUtils.writeTxt(v, "User:/test/ans.txt");
        }
    }
![](https://img2024.cnblogs.com/blog/3398068/202403/3398068-20240311220247221-98409912.png)

SimHash模块的测试

点击查看代码

    @Test
    public void getHashTest() {
        String[] strings = {"原神", "是", "一款", "好玩", "的", "游戏"};
        for (String string : strings) {
            String stringHash = SimHashUtils.getHash(string);
            System.out.println(stringHash.length());
            System.out.println(stringHash);
        }
    }

    @Test
    public void getSimHashTest() {
        String str0 = TxtInOutUtils.readTxt("D:/test/orig.txt");
        String str1 = TxtInOutUtils.readTxt("D:/test/orig_0.8_add.txt");
        System.out.println(SimHashUtils.getSimHash(str0));
        System.out.println(SimHashUtils.getSimHash(str1));
    }
![](https://img2024.cnblogs.com/blog/3398068/202403/3398068-20240311220554470-1058570725.png)

Hamming模块的测试

Main模块测试

结果文件

异常错误处理

设计与实现

当文本长度太短或文本为空时,HanLp无法取得关键字,需要抛出异常

点击查看代码
 public ShortStringException(String message) {
        super(message);
    }


##测试
点击查看代码
 @Test
    public void shortStringExceptionTest(){
        //测试str.length()<200的情况
        System.out.println(SimHashUtils.getSimHash("原神,启动!"));
    }
![](https://img2024.cnblogs.com/blog/3398068/202403/3398068-20240311221715763-1391826302.png)

总结

本项目的最大难点在于查重算法的实现,如何进行高效地、准确地进行查重是这个项目最核心的点。
目前网上主流的有四种算法,这里只挑选了其中一种,查重率可能与其他算法求出的不同。
收获:过了一遍个人项目流程,学会了一些工具的初步使用,但是仍未熟练掌握个人项目开发的技巧

posted @ 2024-03-11 22:22  雨落的瞬间  阅读(107)  评论(0编辑  收藏  举报