zoukankan      html  css  js  c++  java
  • 自然语言处理之Levenshtien Distance算法研究

    自然语言处理中,一个很重要的应用就是问答系统,这里面,涉及到问题和知识库里面的问题的匹配度,从而检索出问题的答案,这个是一个比较常见的应用算法。

    编辑距离(Edit Distance),又称Levenshtein距离(即莱文斯坦距离,LD算法),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。

    许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。一般来说,编辑距离越小,两个串的相似度越大

    该算法由俄罗斯科学家Vladimir Levenshtein于1965年提出。

    算法应用范围很广泛,除了论文查重(抄袭率),基因序列匹配,当前一个很重要的应用就是自然语言处理中的语句的近似度。今天,我们重点讨论的是用LD算法计算两个语句串的相似度。

    例如将kitten转成sitting(变化过程中没有删除动作,只有修改和插入):

    kitten->sitten (将字母k→s)
    sitten->sittin (将字母e→i)
    sittin->sitting (插入g)

    算法逻辑步骤:

    1. 计算出比较的字符串S,T的长度n和m。

    2. 初始化一个(n+1)*(m+1)的二维数组edit(i,j)。

    3. 抽象出动态规划计算编辑距离的方程edit(i,j)=min{edit(i-1,j)+1,edit(i,j-1)+1,edit(i-1,j-1)+cost}

        其中cost=[当S串的i字符与T串的j字符不等时为1,否则为0]

    4. 遍历S,T中的每个字符的对比,最后的edit(n,m)为编辑距离。

    比如要计算cafe和coffee的编辑距离。cafe→caffe→coffe→coffee,定义S=cafe,T=coffee,编辑距离是3.

    算法的java的实现过程源码:

    import java.io.File;
    import java.io.FileNotFoundException;
    import java.util.Scanner;
    
    /**
     * @author shihuc
     * @date  2017年9月28日 下午3:24:43
     */
    public class EditDistance {
    
        /**
         * @author shihuc
         * @param args
         * @throws FileNotFoundException 
         */
        public static void main(String[] args) throws FileNotFoundException {
            File file = new File("./src/com/shihuc/nlp/leventhienDistance/sample.txt");
            Scanner sc = new Scanner(file);
            int N = sc.nextInt();
            sc.nextLine();
            for(int i=0; i<N; i++){            
                String T = sc.nextLine();
                String S = sc.nextLine();
                int dist = editDist(S,T);
                System.out.println(S + " vs " + T + " distance: " + dist);
            }
            sc.close();
        }
        
        private static int editDist(String S, String T){
            /*
             * 步骤1.
             */
            int n = S.length();
            int m = T.length();
            int [][] edit = new int[n+1][m+1];
            
            /*
             * 步骤2.
             * 初始化动态规划数据容器edit[][]
             */
            for(int i=0;i<=n;i++) edit[i][0] = i;
            for(int j=0;j<=m;j++) edit[0][j] = j;
            
            /*
             * 步骤4.
             * 遍历S,T
             */
            for(int i=1; i<=n; i++){    
                char s = S.charAt(i-1);
                for(int j=1; j<=m; j++){
                    /*
                     * 步骤3.
                     * 动态规划,迭代计算edit[i][j]的距离
                     */
                    int cost = calcCost(s, T.charAt(j-1));
                    edit[i][j] = min(edit[i-1][j]+1,edit[i][j-1]+1,edit[i-1][j-1]+cost);
                }
            }    
            printEdit(S,T,edit);
            return edit[n][m];
        }
        
        private static int calcCost(int a, int b){
            if(a == b) {
                return 0;
            }else {
                return 1;
            }
        }
        
        private static int min(int a, int b, int c){
            int m = 0;
            if(a < b){
                m = a;
            }else{
                m = b;
            }
            if(m < c){
                return m;
            }else{
                return c;
            }
        }
        
        private static void printEdit(String S, String T, int es[][]){
            System.out.print("      ");
            for(int x=0; x<es[0].length - 1; x++){
                System.out.print(T.charAt(x)+"  ");
            }
            System.out.println("");
            for(int i=0;i<es.length;i++){            
                if(i > 0){
                    System.out.print(S.charAt(i - 1) + "  ");
                }else{
                    System.out.print("   " );
                }
                for(int j=0;j<es[0].length;j++){
                    System.out.print(es[i][j] + "  ");
                }
                System.out.println("");
            }        
        }
    }

    这里,附上测试案例数据:

    4             #表示有4组测试数据,每组含有S和T。每组的第一行是T,表示目标数据,第二行S表示源数据
    coffee
    cafe
    failing
    sailn
    kitten
    sitting
    girl
    girlfriend

    运行后的结果如下:

          c  o  f  f  e  e  
       0  1  2  3  4  5  6  
    c  1  0  1  2  3  4  5  
    a  2  1  1  2  3  4  5  
    f  3  2  2  1  2  3  4  
    e  4  3  3  2  2  2  3  
    cafe vs coffee distance: 3
          f  a  i  l  i  n  g  
       0  1  2  3  4  5  6  7  
    s  1  1  2  3  4  5  6  7  
    a  2  2  1  2  3  4  5  6  
    i  3  3  2  1  2  3  4  5  
    l  4  4  3  2  1  2  3  4  
    n  5  5  4  3  2  2  2  3  
    sailn vs failing distance: 3
          k  i  t  t  e  n  
       0  1  2  3  4  5  6  
    s  1  1  2  3  4  5  6  
    i  2  2  1  2  3  4  5  
    t  3  3  2  1  2  3  4  
    t  4  4  3  2  1  2  3  
    i  5  5  4  3  2  2  3  
    n  6  6  5  4  3  3  2  
    g  7  7  6  5  4  4  3  
    sitting vs kitten distance: 3
          g  i  r  l  f  r  i  e  n  d  
       0  1  2  3  4  5  6  7  8  9  10  
    g  1  0  1  2  3  4  5  6  7  8  9  
    i  2  1  0  1  2  3  4  5  6  7  8  
    r  3  2  1  0  1  2  3  4  5  6  7  
    l  4  3  2  1  0  1  2  3  4  5  6  
    girl vs girlfriend distance: 6

    是不是比较有意思,还是比较有价值的。

    PS。最近有很长一段时间没有跟算法了,因为项目太紧,自然语言处理是个深远的领域,路很长,慢慢走!

  • 相关阅读:
    linux系统中fdisk命令进行磁盘分区
    linux系统中挂载mount命令、umount命令
    linux系统中du命令
    linux系统中独立冗余磁盘阵列RAID
    linux vmware虚拟机添加硬盘
    linux系统中部署raid10磁盘阵列
    linux系统中配置磁盘容量配额服务(quota)
    linux系统中添加swap交换分区、删除swap交换分区
    linux系统中软链接和硬链接
    调试流程(暂定)
  • 原文地址:https://www.cnblogs.com/shihuc/p/7609946.html
Copyright © 2011-2022 走看看