第一次个人编程作业
Github地址:
PSP表格:
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 100 | 150 |
Estimate | 估计这个任务需要多少时间 | 50 | 80 |
Development | 开发 | 60 | 100 |
Analysis | 需求分析 (包括学习新技术) | 150 | 200 |
Design Spec | 生成设计文档 | 20 | 30 |
Design Review | 设计复审 | 20 | 10 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 20 |
Design | 具体设计 | 20 | 30 |
Coding | 具体编码 | 10 | 20 |
Code Review | 代码复审 | 20 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 40 | 60 |
Reporting | 报告 | 30 | 20 |
Test Report | 测试报告 | 30 | 40 |
Size Measurement | 计算工作量 | 10 | 15 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 50 | 30 |
计算模块接口的设计与实现过程
(1)解题思路:
第一步:将文本通过命令行参数读入;
第二步:将文本内容转化为字符串存下(可以在这里把标点,空格去掉);
第三步:关键的来了,百度求 相似度算法;通过合适的来求解;我刚刚开始采用余弦定理,计算两个句子向量 如:
句子A:我喜欢晚上吃面条,喜欢中午吃米饭;
句子B:我不喜欢晚上吃面条,我们不合适;
我们可以提取每个字的出现次数(可以用map来存下)句子A:(1,2,2,1,1,2,1,1,1,1,1,1);句子B:(2,2,1,1,1,1,1,1,1,1,1,1)通过它们的向量余弦值来确定两个句子的相似度,但文本长的时候准确率不够(后面我才知道可以用词频来优化)。
最终我选择最小编辑距离算法,点这里;是指利用字符操作,把字符串A转换成字符串B所需要的最少操作数。其中,字符操作包括:删除一个字符,插入一个字符,修改一个字符。我们只要求出最小编辑距离,最后得相似度就是1-最小编辑距离/两句中更长的长度。
第四步:将答案输出到文本。
(2)流程图:
(3)核心代码实现:
可以在短文本时使用余弦定理
package com.company;
import java.io.*;
/*import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;*/
public class Main {
public static void main(String[] args) throws IOException,NullTextException {//io异常抛出
// write your code here
// ArrayList list = new ArrayList();
// args = new String[3];
String filepath = args[0];
File file = new File(filepath);
File file2 = new File(args[1]);
File file3 = new File(args[2]);
System.out.println("文件的绝对路径:" + file.getAbsolutePath());
System.out.println("文件的绝对路径:" + file2.getAbsolutePath());
System.out.println("文件的绝对路径:" + file3.getAbsolutePath());
/* try { 文本是否读入
Reader fr = new FileReader(filepath);
char[] data = new char[1000];
int length = 0;
while ((length = fr.read(data)) > 0) {
String str = new String(data, 0, length);
System.out.println(str);
}
fr.close();
} catch (Exception e) {
e.printStackTrace();
}*/
/*File test = new File("src/sim_0.8/tese.txt");测试异常
if(test.length() == 0)
try {
throw new NullTextException("null text");
}catch (NullTextException e){
e.printStackTrace();
}*/
String Str1 =io.read(file);
String Str2 =io.read(file2);
//编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符
int ed = ED.getEd(Str1, Str2, Str1.length(), Str2.length());//获得最小编辑距离(动态规划)
double ans = 1 - 1.0 * ed / Math.max(Str1.length(), Str2.length());//求得相似度
// double bb = getSimilarity(Str_1,Str_2);余弦定理方法
io.write(ans,file3);
MainTest.testEd();
}
/*public static int getEd2(String str1,String str2,int l1, int l2){
//StackOverflowError堆栈溢出异常,递归不能用
int op1 = getEd2(str1,str2,l1-1,l2)+1;
int op2 = getEd2(str1,str2,l1,l2-1)+1;
int op3 = getEd2(str1,str2,l1-1,l2-1);
if(str1.charAt(l1-1)!=str2.charAt(l2-1)){
op3 += 1;
}
return Math.min(Math.min(op1,op2),op3);
}*/
/*
//利用余弦相似度算法
public static doguble getSimilarity(String s1, String s2) {
Map<Character, int[]> Mymap = new HashMap<>();//将两个字符串中的中文字符以及出现的总数封装到map中;
for (int i = 1; i < s1.length()-2; i++) //将文本转化String时多出一些空格,减去
{
char d1 = s1.charAt(i); // d1=第i个汉字,每一个汉字,标点不比较
if(isHanZi(d1)) {
int[] num = Mymap.get(d1);
if (num != null && num.length == 2) {
num[0]++; //已有该字符,加1,原文的字符数量提取
} else {
num = new int[2];
num[0] = 1;//原文
num[1] = 0;//比较文
Mymap.put(d1, num);//将字符加入mymap
}
System.out.println(d1 + Arrays.toString(num));
}
}
System.out.println(" ");
for (int i = 1; i < s2.length()-2; i++) {//将文本转化String时多出一些空格,减去
char d2 = s2.charAt(i);
if(isHanZi(d2)) {
int[] num = Mymap.get(d2);
if (num != null && num.length == 2) {
num[1]++;
} else {
num = new int[2];
num[0] = 0;
num[1] = 1;
Mymap.put(d2, num);
}
System.out.println(d2 + Arrays.toString(num));
}
}
double sq1 = 0;
double sq2 = 0;
double denuminator = 0;
for (Map.Entry entry : Mymap.entrySet()) //用map的entryset遍历所有key-value;
{
int[] c = (int[]) entry.getValue();//章节1中出现的字为:Z1c1,Z1c2,Z1c3,Z1c4……Z1cn;它们在章节中的个数为:Z1n1,Z1n2,Z1n3……Z1nm;
//章节2中出现的字为:Z2c1,Z2c2,Z2c3,Z2c4……Z2cn;它们在章节中的个数为:Z2n1,Z2n2,Z2n3……Z2nm;
denuminator += c[0] * c[1]; //z1n1*z2n1+z1n2*z2n2...
sq1 += c[0] * c[0]; //z1n1^2+z1n2^2+..
sq2 += c[1] * c[1]; //Z2n1^2+z2n2^2+..
}
return denuminator / (Math.sqrt(sq1) * Math.sqrt(sq2));//余弦定理
}}*/
public static boolean isHanZi(char ch) {
// 判断是否汉字
return (ch >= 0x4E00 && ch <= 0x9FA5);
}
}
长文本时,我是先用递归实现最小编辑距离,堆栈溢出了,后面改用动态规划的方法。
public static int getEd(String str1, String str2, int l1, int l2) {//动态规划求最小编辑距离
int Distance = 0;
int ed = 0;
if (l1 != 0 && l2 != 0) {
int[][] Distance_shuzu = new int[l1 + 1][l2 + 1];
//编号
int Bianhao = 0;
for (int i = 0; i <= l1; i++) {//初始化,给每个字符编号
Distance_shuzu[i][0] = Bianhao;
Bianhao++;
}
Bianhao = 0;
for (int i = 0; i <= l2; i++) {
Distance_shuzu[0][i] = Bianhao;
Bianhao++;
}
char[] Str1_CharArray = str1.toCharArray();
char[] Str2_CharArray = str2.toCharArray();
for (int i = 1; i <= l1; i++) {
for (int j = 1; j <= l2; j++) {
if (Str1_CharArray[i - 1] == Str2_CharArray[j - 1]) {//相同不变
Distance = 0;
} else {
Distance = 1;
}
int Temp1 = Distance_shuzu[i - 1][j] + 1;//增
int Temp2 = Distance_shuzu[i][j - 1] + 1;//减
int Temp3 = Distance_shuzu[i - 1][j - 1] + Distance;//改
Distance_shuzu[i][j] = Math.min(Temp1, Temp2);//找最小的一步
Distance_shuzu[i][j] = Math.min(Temp3, Distance_shuzu[i][j]);
}
}
ed = Distance_shuzu[l1][l2];
}
return ed;
}
性能改进
内存消耗
int数组消耗了大量的空间
overview
计算模块部分单元测试展示
测试代码
@org.junit.Test
public static void testEd(){
String [] testNames = new String[]{
"src/sim_0.8/orig_0.8_add.txt",
"src/sim_0.8/orig_0.8_del.txt",
"src/sim_0.8/orig_0.8_dis_1.txt",
"src/sim_0.8/orig_0.8_dis_3.txt",
"src/sim_0.8/orig_0.8_dis_7.txt",
"src/sim_0.8/orig_0.8_dis_10.txt",
"src/sim_0.8/orig_0.8_dis_15.txt",
"src/sim_0.8/orig_0.8_mix.txt",
"src/sim_0.8/orig_0.8_rep.txt"
};
for (int i=0;i<testNames.length;i++) {
System.out.println("测试"+i+':');
double ans = Test.test(testNames[i]);
Assert.assertEquals(0.8,ans,0.2);
}
}
测试结果
覆盖率
计算模块部分异常处理说明
设计了一个空文本异常类
public class NullTextException extends IOException {
public NullTextException(String message){
super(message);
}
public NullTextException(String message,Throwable cause){
super(message,cause);
}
public NullTextException(Throwable cause){
super(cause);
}
}
测试一下
File test = new File("src/sim_0.8/tese.txt");测试异常
if(test.length() == 0)
try {
throw new NullTextException("null text");
}catch (NullTextException e){
e.printStackTrace();
}