第一次个人编程作业
- 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
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;
}
`
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 我干了两个月的大项目,开源了!
· 推荐一款非常好用的在线 SSH 管理工具
· 聊一聊 操作系统蓝屏 c0000102 的故障分析
· 千万级的大表,如何做性能调优?
· .NET周刊【1月第1期 2025-01-05】