第一次个人编程作业

这个作业属于哪个课程 计科22级34班
这个作业要求在哪里 作业要求
这个作业的目标 创建项目,实现论文查重功能
  • PSP表格
PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
Estimate 估计这个任务需要多少时间 330 460
Development 开发 20 30
Analysis 需求分析 (包括学习新技术) 100 50
Design Spec 生成设计文档 30 45
Design Review 设计复审 10 15
Coding Standard 代码规范 (为目前的开发制定合适的规范) 5 10
Design 具体设计 15 20
Coding 具体编码 45 40
Code Review 代码复审 10 15
Test 测试(自我测试,修改代码,提交修改) 15 20
Reporting 报告 30 25
Test Repor 测试报告 15 20
Size Measurement 计算工作量 20 20
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 15 30
合计 330 340
  • 需求分析
    描述如下:

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

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

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

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

  • 算法实现基本思路

  • simHash算法原理
    分词:现在有很多可供使用的包来进行文本的分词,本篇所使用的分词器是IKAnalysis,需要安装 IKAnalyzer2012_u6.jar包
    hash:通过hash函数计算各个特征向量(这里为划分好的词)的hash值,hash值为二进制数01组成的n-bit签名。
    加权:权重:就是词频;把第2步生成的hash值从左至右与权重进行运算;如果该bit的数值为1,则将权重赋给该位;如果该bit的数值为0,则将权重的负值赋给该位。example:“我”,hash = 101011,weight(词频) = 5;则加权后的结果为:5 -5 5 -5 5 5;
    合并:经过上述的三个步骤,我们可以得到全部词(word)的加权hash值,此时需要将全部的加权后的hash值进行累加;
    降维:将第四步计算出来的序列串变为01串;具体规则:如果该位的数值>0,则置为1;反之则置为0.
    海明距离(Hamming Distance):在信息编码中,两个合法代码对应位上编码不同的位数称为码距,又称海明距离;
  • 余弦定理查找相似度
    (1)找出两篇文章的关键词;
    (2)每篇文章各取出若干个关键词,合并成一个集合,计算每篇文章对于这个集合中的词的词频
    (3)生成两篇文章各自的词频向量;
    (4)计算两个向量的余弦相似度,值越大就表示越相似。
  • 模块接口部分

  • 执行结果
  • 代码测试
    package utils; import CommonText.utils.TxtIOUtils; import org.junit.Test; public class TxtIOUtilsTest { @Test public void readNotTxtTest(){ String str = TxtIOUtils.readTxt("D:\\新建文件夹\\git\\pa\\src\\main\\java\\com\\will\\exception\\TooShortException.java"); System.out.println(str); } //文件类型不是txt类型 @Test public void readTxtTest() { String str = TxtIOUtils.readTxt("F:\\DeskTop\\查重测试文件\\orig.txt"); String[] strings = str.split(" "); for (String string : strings) { System.out.println(string); } } //路径存在,正常读取 @Test public void writeTxtTest() { TxtIOUtils.writeTxt("今天是星期天,天气晴,今天晚上我要去看电影, "F:\\DeskTop\\查重测试文件\\textWrite.txt"); } //路径存在,正常写入 @Test public void readTxtFailTest() { String str = TxtIOUtils.readTxt("D:/test/none.txt"); } //路径不存在,读取失败 @Test public void writeTxtFailTest() { TxtIOUtils.writeTxt("今天是星期天,天气晴,今天晚上我要去看电影", "D:/test/none.txt"); } } //路径错误,写入失败
  • HammingUtilsTest测试
    `package utils;

import CommonText.utils.HammingUtils;
import CommonText.utils.SimHashUtils;
import CommonText.utils.TxtIOUtils;
import org.junit.Test;

public class HammingUtilsTest {

@Test
public void getHammingDistanceTest() {
    String str0 = TxtIOUtils.readTxt("F:\\DeskTop\\查重测试文件\\orig.txt");
    String str1 = TxtIOUtils.readTxt("F:\\DeskTop\\查重测试文件\\orig_0.8_add.txt");
    int distance = HammingUtils.getHammingDistance(SimHashUtils.getSimHash(str0), SimHashUtils.getSimHash(str1));
    System.out.println("海明距离:" + distance);
    System.out.println("相似度: " + (100 - distance * 100 / 128) + "%");
}    //获取海明距离


@Test
public void getHammingDistanceFailTest() {
    // 测试str0.length()!=str1.length()的情况
    String str0 = "10101010";
    String str1 = "1010101";
    System.out.println(HammingUtils.getHammingDistance(str0, str1));
}    //获取海明距离失败测试


@Test
public void getSimilarityTest() {
    String str0 = TxtIOUtils.readTxt("F:\\DeskTop\\查重测试文件\\orig.txt");
    String str1 = TxtIOUtils.readTxt("F:\\DeskTop\\查重测试文件\\orig_0.8_add.txt");
    int distance = HammingUtils.getHammingDistance(SimHashUtils.getSimHash(str0), SimHashUtils.getSimHash(str1));
    double similarity = HammingUtils.getSimilarity(SimHashUtils.getSimHash(str0), SimHashUtils.getSimHash(str1));
    System.out.println("str0和str1的汉明距离: " + distance);
    System.out.println("str0和str1的相似度:" + similarity);
}

} //获取相似度测试

`

测试代码主要分成2部分:
1、计算海明码距离
2、根据海明码距离获取两者相似度

  • MainTest测试

`package MainText;
import CommonText.utils.HammingUtils;
import CommonText.utils.SimHashUtils;
import CommonText.utils.TxtIOUtils;
import org.junit.Test;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class MainTest {
@Test
public void origAndAllTest() {
String[] str = new String[6];
str[0] = TxtIOUtils.readTxt("F:\DeskTop\查重测试文件\orig.txt");
str[1] = TxtIOUtils.readTxt("F:\DeskTop\查重测试文件\orig_0.8_add.txt");
str[2] = TxtIOUtils.readTxt("F:\DeskTop\查重测试文件\orig_0.8_del.txt");
str[3] = TxtIOUtils.readTxt("F:\DeskTop\查重测试文件\orig_0.8_dis_1.txt");
str[4] = TxtIOUtils.readTxt("F:\DeskTop\查重测试文件\orig_0.8_dis_10.txt");
str[5] = TxtIOUtils.readTxt("F:\DeskTop\查重测试文件\orig_0.8_dis_15.txt");
String ansFileName = "F:\DeskTop\查重测试文件\text.txt";
for (int i = 0; i <= 5; i++) {
Double similarity = HammingUtils.getSimilarity(SimHashUtils.getSimHash(str[0]), SimHashUtils.getSimHash(str[i]));
String resultSimilarity = String.format("%.2f", similarity);
String result = "时间:" + DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss").format(LocalDateTime.now()) + "\n" + "原文件与文件" + i + "的相似度为:" + resultSimilarity + "\r\n";
TxtIOUtils.writeTxt(result, ansFileName);
System.out.println(result);
}
}

}`

异常分析
TooExceptionTest异常分析
设计目标:为防止文本长度不满足要求而设置的规范长度异常
对应场景:当读取的文本内容少于300字符时将抛出。

package CommonText.exception; import CommonText.utils.SimHashUtils; import org.junit.Test; public class TooExceptionTest { @Test public void shortStringExceptionTest() { //测试str.length()<300的情况 System.out.println(SimHashUtils.getSimHash("123123123")); } @Test public void longStringExceptionTest() { //测试str.length()>300的情况 String str = "今天是星期天,天气晴,今天晚上我要去看电影; System.out.println(SimHashUtils.getSimHash(str)); System.out.println(str.length()); } }

性能分析
性能分析图

方法调用情况

程序消耗最大函数

`public static String getSimHash(String str) {
//文本长度太短时HanLp无法取得关键字
try {
if (str.length() < 300) {
TooException.cast();
}
} catch (TooException myException) {
myException.printStackTrace();
return null;
}
//用数组表示特征向量,取128位,从 0 1 2 位开始表示从高位到低位
int[] v = new int[128];
//1. 分词(使用了外部依赖hankcs包提供的接口)
List keywordList = HanLP.extractKeyword(str, str.length());
int size = keywordList.size();
int i = 0;
for (String keyword : keywordList) {
//2. 获取hash值
String keywordHash = getHash(keyword);
if (keywordHash.length() < 128) {

            int dif = 128 - keywordHash.length();
            for (int j = 0; j < dif; j++) {
                keywordHash += "0";
            }
        }
    //3. 加权、合并
        for (int j = 0; j < v.length; j++) {
        
            if (keywordHash.charAt(j) == '1') {
                v[j] += (10 - (i / (size / 10)));
            } else {
                v[j] -= (10 - (i / (size / 10)));
            }
        }
        i++;
    }
    //4. 降维
    //储存返回的simHash值
    String simHash = "";
    for (int j = 0; j < v.length; j++) {
        if (v[j] <= 0) {
            simHash += "0";
        } else {
            simHash += "1";
        }
    }
    return simHash;
}

`

posted @   唐学鹏  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 我干了两个月的大项目,开源了!
· 推荐一款非常好用的在线 SSH 管理工具
· 聊一聊 操作系统蓝屏 c0000102 的故障分析
· 千万级的大表,如何做性能调优?
· .NET周刊【1月第1期 2025-01-05】
点击右上角即可分享
微信分享提示