zoukankan      html  css  js  c++  java
  • 动态规划计算文本相似度项目—第一次个人编程作业(软件工程)

    github链接

    软件工程 https://edu.cnblogs.com/campus/gdgy/informationsecurity1812
    作业要求 https://edu.cnblogs.com/campus/gdgy/informationsecurity1812/homework/11155
    作业目标 论文查重算法设计+单元测试+JProfiler+PSP表格+Git管理

    整体流程

    1. 程序入口MainApplication里面传入:需要检验重复的文章,要对比的文章,以及输出的文章。
    2. IOUtil进行io的读写操作,获得文本
    3. TextUtil过滤去html标签,同时分句,获得一个分句的list
    4. 两边要检测的文本进行m*n级别的比照
    5. 每句的比照重复使用EasyCompared中的compaired方法,利用动态规划getDistance获得两个句子之间最少的移动距离。最后根据移动的距离以及两个句子的长度,来计算这两个句子是否相似
    6. 最后根据相似的句子的数目,和总句子的数目来计算相似度。
    7. 最后输出将相似度写入文件

    项目结构

    主要的算法

    //动态转移方程
    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.最后发现自己还有很多不足的地方,以后要加把劲吼。

  • 相关阅读:
    C语言的存储类别和动态内存分配
    C语言中复杂的声明
    C语言中typedef的解释_2
    C语言中类型限定符
    C语言文件I/O和标准I/O函数
    C语言中存储类别、链接与内存管理
    C++中static与const成员
    C++多态、虚函数、纯虚函数、抽象类
    sizeof结构体
    杂类
  • 原文地址:https://www.cnblogs.com/chiguoguo/p/13722071.html
Copyright © 2011-2022 走看看