Java实现文本查重

一. 作业概述

作业课程软件工程
作业要求软件工程作业_2
作业任务完成一个论文查重系统
GitHub链接3120006301-duplicate_checking

二.需求分析

题目:论文查重

描述如下:

设计一个论文查重算法,给出一个原文文件和一个在这份原文上经过了增删改的抄袭版论文的文件,在答案文件中输出其重复率。

原文示例:今天是星期天,天气晴,今天晚上我要去看电影。 抄袭版示例:今天是周天,天气晴朗,我晚上要去看电影。
要求输入输出采用文件输入输出,规范如下:

从命令行参数给出:论文原文的文件的绝对路径。 从命令行参数给出:抄袭版论文的文件的绝对路径。 从命令行参数给出:输出的答案文件的绝对路径。
我们提供一份样例,课堂上下发,上传到班级群,使用方法是:orig.txt是原文,其他orig_add.txt等均为抄袭版论文。

注意:答案文件中输出的答案为浮点型,精确到小数点后两位

三.PSP表格

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划2020
·Estimate· 估计这个任务需要多少时间2020
Development开发300380
· Analysis· 需求分析 (包括学习新技术)120180
· Design Spec· 生成设计文档2030
· Design Review· 设计复审1010
· Coding Standard· 代码规范 (为目前的开发制定合适的规范)1010
· Design· 具体设计6080
· Coding· 具体编码6090
· Code Review· 代码复审2015
· Test· 测试(自我测试,修改代码,提交修改)3025
Reporting报告4030
· Test Repor· 测试报告2018
· Size Measurement· 计算工作量106
· Postmortem & Process Improvement Plan· 事后总结, 并提出过程改进计划106
All合计390460

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

1.核心算法

知乎_SimHash算法计算相似度

1.1SimHash算法

simHash算法主要有五个过程:分词、Hash、加权、合并、降维。
在这里插入图片描述

1.分词

对给定的一段文本进行分词,产生n个特征词,并赋予每个特征词一个权重。比如一段文本为“中国科大计算机系的学生的能力怎么样”,产生的特征词就应该是“中国科大”、“计算机系”、“的”、“学生”、“能力”、“怎么样”,然后对这些词分别赋予权重,假设有1-5五个分类,分词之后以上五个词便会各有一个权重,比如中国科大(4)、计算机系(3)、的(1)、学生(4)、能力(5)、怎么样(3)。

其中,数字越大,代表特征词在句子中的重要性就越高。这样,我们就得到了一个文本的分词的词向量和每个词向量对应的权重。

2.Hash

通过hash函数对每一个词向量进行映射,产生一个n位二进制串,比如CSDN的hash值就是100101。

3.加权

前面的计算我们已经得到了每个词向量的Hash串和该词向量对应的权重,这一步我们计算权重向量W=hash*weight。

具体的计算过程如下:hash二进制串中为1的乘以该特征词的分词权重,二进制串中为0的乘以该特征词的分词权重后取负,继而得到权重向量。

举个例子,CSDN的hash二进制串是100101,CSDN的权重是3,那么生成的权重向量就是[3,-3,-3,3,-3,3]。

4.合并

对于一个文本,我们计算出了文本分词之后每一个特征词的权重向量,在合并这个阶段,我们把文本所有词向量的权重向量相累加,得到一个新的权重向量,形如[3,4,1,5,-5,1]

5.降维

对于前面合并后得到的文本的权重向量,大于0的位置1,小于等于0的位置0,就可以得到该文本的SimHash值,以上面提到的[3,4,1,5,-5,1]为例,我们得到[1,1,1,1,0,1]这个bit串,也就是论文中提及的该文本的指纹。

到此为止,我们已经计算出了一个文本的SimHash值。那么,如何判断两个文本是否相似呢?我们要用到海明距离。

1.2计算海明距离

简单的说,海明距离可以理解为,两个二进制串之间相同位置不同值的个数。举个例子,[1,1,1,0,0,0]和[1,1,1,1,1,1]的海明距离就是3。

在处理大规模数据的时候,我们一般使用64位的SimHash,正好可以被一个long型存储。这种时候,海明距离在3以内就可以认为两个文本是相似的。

2.接口的设计关系

模块功能
A_main实现类
ShortStringException处理文本过短异常
SimHash计算SimHash值
Hamming计算海明距离
TXT_IO文本的IO操作

在这里插入图片描述

2.1 A_main

读取命令行参数,将第1个args路径作为原文,第2个args路径作为抄袭文,写入第3个args路径的文本内。

2.2 ShortStringException

public ShortStringException(String message) 当文本长度小于200时异常处理

2.3SimHash

public static String getHash(String str) 获取MD5值 public static String
getSimHash(String str) 传入字符串,通过分词,Hash,加权,合并,最后降维,计算出它的simHash值,并以字符串形式输出

2.4Hamming

public static int getHammingDistance(String simHash1, StringsimHash2) 输入两个simHash值(simHash1、simHash2),计算它们的海明距离 public static
double getSimilarity(String simHash1, String simHash2) 输入两个simHash值,输出相似度

2.5 TXT_IO

public static String readTxt(String txtPath) 读取文本内容,以字符串输出 public
static void writeTxt(double txtElem,String txtPath) 写入文本

3.程序流程图

在这里插入图片描述

五.单元测试

在这里插入图片描述

1.TXT_IO_text

1.1代码

package se.test;

import org.junit.Test;
import se.main.TXT_IO;

import static se.main.TXT_IO.*;

public class TXT_IO_test {
    @Test
    public void readTxtTest() {
        // 路径存在,正常读取
        String str = readTxt("txt/orig.txt");
        String[] strings = str.split(" ");
        for (String string : strings) {
            System.out.println(string);
        }
    }

    @Test
    public void writeTxtTest() {
        // 路径存在,正常写入
        double[] elem = {0.1, 0.2, 0.3, 0.4, 0.5};
        for (int i = 0; i < elem.length; i++) {
            writeTxt(elem[i], "txt/ans.txt");
        }
    }

    @Test
    public void readTxtFailTest() {
        // 路径不存在,读取失败
        String str = readTxt("txt/none.txt");
    }

    @Test
    public void writeTxtFailTest() {
        // 路径错误,写入失败
        double[] elem = {0.1, 0.2, 0.3, 0.4, 0.5};
        for (int i = 0; i < elem.length; i++) {
            writeTxt(elem[i], "User:/test/ans.txt");
        }
    }

}

1.2测试结果

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.ShortStringException_text

2.1代码

package se.test;

import org.junit.Test;
import se.main.SimHash;


public class ShortStringException_test {

    @Test
    public void shortStringExceptionTest(){
        //测试str.length()<200的情况
        System.out.println(SimHash.getSimHash("孤独之旅"));
    }

}

2.2测试结果

在这里插入图片描述

3.SimHash_text

3.1代码

package se.test;

import org.junit.Test;
import se.main.SimHash;
import se.main.TXT_IO;

public class SimHash_test {


    @Test
    public void getHashTest(){
        String[] strings = {"我", "在", "GDUT", "2021级计算机科学与技术", "学习", "软件工程"};
        for (String string : strings) {
            String stringHash = SimHash.getHash(string);
            System.out.println(stringHash.length());
            System.out.println(stringHash);
        }
    }

    @Test
    public void getSimHashTest(){
        String str0 = TXT_IO.readTxt("txt/orig.txt");
        String str1 = TXT_IO.readTxt("txt/orig_0.8_add.txt");
        System.out.println(SimHash.getSimHash(str0));
        System.out.println(SimHash.getSimHash(str1));
    }

}

3.2测试结果

3.3代码覆盖率

4.Hamming_text

4.1代码

package se.test;

import org.junit.Test;
import se.main.Hamming;
import se.main.SimHash;
import se.main.TXT_IO;

public class Hamming_test

 {

    @Test
    public void getHammingDistanceTest() {
        String str0 = TXT_IO.readTxt("txt/orig.txt");
        String str1 = TXT_IO.readTxt("txt/orig_0.8_add.txt");
        int distance = Hamming.getHammingDistance(SimHash.getSimHash(str0), SimHash.getSimHash(str1));
        System.out.println("海明距离:" + distance);
        System.out.println("相似度: " + (100 - distance * 100 / 128) + "%");
    }

    @Test
    public void getHammingDistanceFailTest() {
        // 测试str0.length()!=str1.length()的情况
        String str0 = "1010";
        String str1 = "1111";
        System.out.println(Hamming.getHammingDistance(str0, str1));
    }

    @Test
    public void getSimilarityTest() {
        String str0 = TXT_IO.readTxt("txt/orig.txt");
        String str1 = TXT_IO.readTxt("txt/orig_0.8_add.txt");
        int distance = Hamming.getHammingDistance(SimHash.getSimHash(str0), SimHash.getSimHash(str1));
        double similarity = Hamming.getSimilarity(SimHash.getSimHash(str0), SimHash.getSimHash(str1));
        System.out.println("str0和str1的汉明距离: " + distance);
        System.out.println("str0和str1的相似度:" + similarity);
    }
}

4.2测试结果

4.3代码覆盖率

5.A_main_text

此部分对作业所给的6个测试文本进行测试,其中原文为orig.txt,抄袭文本为所有txt文本。

5.1代码

package se.test;

import org.testng.annotations.Test;
import se.main.Hamming;
import se.main.SimHash;
import se.main.TXT_IO;

public class A_main_test
{
   
    @Test
    public void origAndOrigTest(){
        String str0 = TXT_IO.readTxt("txt/orig.txt");
        String str1 = TXT_IO.readTxt("txt/orig.txt");
        String ansFileName = "txt/ansAndOrigTest.txt";
        double ans = Hamming.getSimilarity(SimHash.getSimHash(str0), SimHash.getSimHash(str1));
        System.out.println("查重率:"+ans*100+"%");
        TXT_IO.writeTxt(ans, ansFileName);
    }

    @Test
    public void origAndAddTest(){
        String str0 = TXT_IO.readTxt("txt/orig.txt");
        String str1 = TXT_IO.readTxt("txt/orig_0.8_add.txt");
        String ansFileName = "txt/ansAndAddTest.txt";
        double ans =Hamming.getSimilarity(SimHash.getSimHash(str0), SimHash.getSimHash(str1));
        System.out.println("查重率:"+ans*100+"%");
        TXT_IO.writeTxt(ans, ansFileName);
    }

    @Test
    public void origAndDelTest(){
        String str0 = TXT_IO.readTxt("txt/orig.txt");
        String str1 = TXT_IO.readTxt("txt/orig_0.8_del.txt");
        String ansFileName = "txt/ansAndDelTest.txt";
        double ans = Hamming.getSimilarity(SimHash.getSimHash(str0), SimHash.getSimHash(str1));
        System.out.println("查重率:"+ans*100+"%");
        TXT_IO.writeTxt(ans, ansFileName);
    }

    @Test
    public void origAndDis1Test(){
        String str0 = TXT_IO.readTxt("txt/orig.txt");
        String str1 = TXT_IO.readTxt("txt/orig_0.8_dis_1.txt");
        String ansFileName = "txt/ansAndDis1Test.txt";
        double ans = Hamming.getSimilarity(SimHash.getSimHash(str0), SimHash.getSimHash(str1));
        System.out.println("查重率:"+ans*100+"%");
        TXT_IO.writeTxt(ans, ansFileName);
    }

    @Test
    public void origAndDis10Test(){
        String str0 = TXT_IO.readTxt("txt/orig.txt");
        String str1 = TXT_IO.readTxt("txt/orig_0.8_dis_10.txt");
        String ansFileName = "txt/ansAndDis10Test.txt";
        double ans = Hamming.getSimilarity(SimHash.getSimHash(str0), SimHash.getSimHash(str1));
        System.out.println("查重率:"+ans*100+"%");
        TXT_IO.writeTxt(ans, ansFileName);
    }

    @Test
    public void origAndDis15Test(){
        String str0 = TXT_IO.readTxt("txt/orig.txt");
        String str1 = TXT_IO.readTxt("txt/orig_0.8_dis_15.txt");
        String ansFileName = "txt/ansAndDis15Test.txt";
        double ans = Hamming.getSimilarity(SimHash.getSimHash(str0), SimHash.getSimHash(str1));
        System.out.println("查重率:"+ans*100+"%");
        TXT_IO.writeTxt(ans,ansFileName);
    }

}


5.2测试结果

5.3代码覆盖率

六.测试结果

在终端使用jar包给出命令行参数,得到查重(Duplicate Checking Rate)结果如下
在这里插入图片描述

七.程序缺陷

适用于较长文本的去重场景,对短文本不太合适,容易冲突生成相同simhash值,存在误判情况。

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值