动态规划计算文本相似度项目—第一次个人编程作业(软件工程)
| 软件工程 | https://edu.cnblogs.com/campus/gdgy/informationsecurity1812 | 
|---|---|
| 作业要求 | https://edu.cnblogs.com/campus/gdgy/informationsecurity1812/homework/11155 | 
| 作业目标 | 论文查重算法设计+单元测试+JProfiler+PSP表格+Git管理 | 
整体流程
- 程序入口MainApplication里面传入:需要检验重复的文章,要对比的文章,以及输出的文章。
- IOUtil进行io的读写操作,获得文本
- TextUtil过滤去html标签,同时分句,获得一个分句的list
- 两边要检测的文本进行m*n级别的比照
- 每句的比照重复使用EasyCompared中的compaired方法,利用动态规划getDistance获得两个句子之间最少的移动距离。最后根据移动的距离以及两个句子的长度,来计算这两个句子是否相似
- 最后根据相似的句子的数目,和总句子的数目来计算相似度。
- 最后输出将相似度写入文件

项目结构

主要的算法
//动态转移方程
d[i][j] = getMin(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1]+ temp);

我们这个表是对比"我喜欢吃牛扒"和"你喜欢打牛牛"
可以看到(欢,欢)这个坐标是1,这个1表示的是,"我喜欢"和"你喜欢"之间的距离间隔着1,这两个词之间最少经过1次字符的变换就能得到另一个词。
那这个1怎么来的呢,我们可以试想一下,"你喜欢"和"我喜欢"的上一步,到底是怎么经过最少次变换得来的,可以找
"我喜"->"你喜欢"
"我喜欢"->"你喜"
"我喜"->"你喜"
分别对应的坐标是d[i - 1][j],d[i][j - 1],d[i - 1][j - 1]
只要我们知道,这三者的中最小的(上一次+本次变换步数)就可以得到到底变换了多少步骤。
最后就可以得出这条动态转移方程。
d[i][j] = getMin(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1]+ temp);
距离得到了,那怎么判断这两个句子是否相似呢,这里我就设计了一个值,k

|s1-s2|是s1和s2之间的长度差
d是s1和s2的变换距离差。
根据算出来的k值再判断两个k是否相似。
k越小越相似!
一开始想设计d/s1,但是这样一想好像有点不对劲,因为"我"和"我喜欢吃蒜香鸡"这两个句子算的话d/s1就会是一个巨大的数据
于是假如|s1-s2|消除一下句子过长的影响。
这条式子其实可以再优化一下,不过概率论没学好,以后继续加油。
最后我们就可以得到,原文中存在重复句子的个数,
用原文/疑似原文就可以得到重复度。
时间复杂度分析
时间复杂大概是O(n2),首先进行m*n级别的句子匹配,每对句子之间要进行a2级别的匹配。但是总体来说,基本上每个字都要经历一次句子长度*疑似文本长度的运算。
代码测试的覆盖度
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class MainApplicationTest {
    @BeforeClass
    public static void beforeTest(){
        System.out.println("junit单元测试开始");
    }
    @AfterClass
    public static void afterTest(){
        System.out.println("junit单元测试结束");
    }
    /**
     * 测试 正常的测试
     */
    @Test
    public void testForAdd(){
        MainApplication.run("C:\\homework\\orig.txt","C:\\homework\\orig_0.8_add.txt","C:\\homework\\happy.txt");
    }
    /**
     * 测试 删除orig_0.8_del.txt
     */
    @Test
    public void testForDel(){
        MainApplication.run("C:\\homework\\orig.txt","C:\\homework\\orig_0.8_del.txt","C:\\homework\\happy.txt");
    }
    /**
     * 测试 分割orig_0.8_dis_1.txt
     */
    @Test
    public void testForDis1(){
        MainApplication.run("C:\\homework\\orig.txt","C:\\homework\\orig_0.8_dis_1.txt","C:\\homework\\happy.txt");
    }
    /**
     * 测试 分割orig_0.8_dis_10.txt
     */
    @Test
    public void testForDis10(){
        MainApplication.run("C:\\homework\\orig.txt","C:\\homework\\orig_0.8_dis_10.txt","C:\\homework\\happy.txt");
    }
    /**
     * 测试 分割orig_0.8_dis_15.txt
     */
    @Test
    public void testForDis15(){
        MainApplication.run("C:\\homework\\orig.txt","C:\\homework\\orig_0.8_dis_15.txt","C:\\homework\\happy.txt");
    }
    /**
     * 测试 传入的文件路径错误
     */
    @Test
    public void testFororigpath(){
        MainApplication.run("don't run","C:\\homework\\orig_0.8_dis_15.txt","C:\\homework\\happy.txt");
    }
}
异常的处理

文件名不对的时候的报错


JProfile进行性能测试

内存的使用情况

cpu的运行时间

耗时最长的地方

是getdistance方法,由于必须进行n^2级别的运算才能算完,而且已经是优化过后的动态规划了,所以改不了-3-。
PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) | 
|---|---|---|---|
| Planning | 计划 | 10 | 20 | 
| · Estimate | · 估计这个任务需要多少时间 | 180 | 250 | 
| Development | 开发 | 30 | 35 | 
| · Analysis | · 需求分析 (包括学习新技术) | 15 | 30 | 
| · Design Spec | · 生成设计文档 | 25 | 25 | 
| · Design Review | · 设计复审 | 15 | 15 | 
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 30 | 
| · Design | · 具体设计 | 25 | 25 | 
| · Coding | · 具体编码 | 20 | 20 | 
| · Code Review | · 代码复审 | 20 | 20 | 
| · Test | · 测试(自我测试,修改代码,提交修改) | 30 | 35 | 
| Reporting | 报告 | 30 | 40 | 
| · Test Repor | · 测试报告 | 30 | 40 | 
| · Size Measurement | · 计算工作量 | 15 | 20 | 
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 20 | 
| · 合计 | 485 | 625 | 
总结
1.这次个人项目收获了挺多东西,尤其是第一次借助到测试这个工作,以及这些工具jProfile,还有使用junit进行单元测试
2.学到了不少算法,虽然写上去的还是自己想的,这个算法有优点也有缺点,优点就是,思路比较简单,不用依赖外部的库,而且是靠句子来分割的,判断重复的力度大一点,缺点也是很明显的,没有句意分析,同义词,以及时间复杂度过高的问题,看了不少大佬写的太优秀了。
3.最后发现自己还有很多不足的地方,以后要加把劲吼。
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号