zoukankan      html  css  js  c++  java
  • 字符串相似度算法——Levenshtein Distance算法

    Levenshtein Distance 算法,又叫 Edit Distance 算法,是指两个字符串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。一般来说,编辑距离越小,两个串的相似度越大。

    算法实现原理图解:

    a.首先是有两个字符串,这里写一个简单的 abc 和 abe

    b.将字符串想象成下面的结构。

    A 处 是一个标记,为了方便讲解,不是这个表的内容。

      abc a b c
    abe 0 1 2 3
    a 1 A处    
    b 2      
    e 3      

    c.来计算 A 处 出得值

    它的值取决于:左边的 1、上边的 1、左上角的 0。

    按照 Levenshtein distance 的意思:

    上面的值加 1 ,得到 1+1=2 ,

    左面的值加 1 ,得到 1+1=2 ,

    左上角的值根据字符是否相同,相同加 0 ,不同加 1 。A 处由于是两个 a 相同,左上角的值加 0 ,得到 0+0=0 。

    然后从我们上面计算出来的 2,2,0 三个值中选取最小值,所以 A 处的值为 0 。

    d.于是表成为下面的样子

      abc a b c
    abe 0 1 2 3
    a 1 0    
    b 2 B处    
    e 3      

    在 B 处 会同样得到三个值,左边计算后为 3 ,上边计算后为 1 ,在 B 处 由于对应的字符为 a、b ,不相等,所以左上角应该在当前值的基础上加 1 ,这样得到 1+1=2 ,在(3,1,2)中选出最小的为 B 处的值。

    e.于是表就更新了

      abc a b c
    abe 0 1 2 3
    a 1 0    
    b 2 1    
    e 3 C处    

    C 处 计算后:上面的值为 2 ,左边的值为 4 ,左上角的:a 和 e 不相同,所以加 1 ,即 2+1 ,左上角的为 3 。

    在(2,4,3)中取最小的为 C 处的值。

    f.于是依次推得到

        a b c
      0 1 2 3
    a 1 A处 0 D处 1 G处 2
    b 2 B处 1 E处 0 H处 1
    e 3 C处 2 F处 1 I处 1

    I 处: 表示 abc 和 abe 有1个需要编辑的操作( c 替换成 e )。这个是需要计算出来的。

    同时,也获得一些额外的信息:

    A处: 表示a      和a       需要有0个操作。字符串一样

    B处: 表示ab    和a       需要有1个操作。

    C处: 表示abe  和a       需要有2个操作。

    D处: 表示a      和ab     需要有1个操作。

    E处: 表示ab    和ab     需要有0个操作。字符串一样

    F处: 表示abe  和ab     需要有1个操作。

    G处: 表示a      和abc   需要有2个操作。

    H处: 表示ab    和abc   需要有1个操作。

    I处: 表示abe   和abc    需要有1个操作。

    g.计算相似度

    先取两个字符串长度的最大值 maxLen,用 1-(需要操作数 除 maxLen),得到相似度。

    例如 abc 和  abe  一个操作,长度为 3 ,所以相似度为 1-1/3=0.666 。

    以上就是整个算法的推导过程,但是至于为什么能算出相似度,还是不太懂。而且我发现这个算法有个很坑的地方,就是有时候根据算法推算出的结果,会和我们想象中的不一样:

    例如:字符串 abcd 和字符串 def ,根据算法算出来的相似度为 0,可是明明是由一个相同字符 d 的,至少应该是有一定相似度,即使很相似度很低,但是也不应该为0才对。但是字符串 abcd 和字符串 aert ,同样是只有一个相同字符 a ,但是根据算法算出来的相似度却为 0.25 。

    根据最开始对算法的介绍,使用替换、删除、插入这三种操作方式,字符串 abcd 和字符串 aert 最少需要 3 步就能完成转变,而字符串 abcd 和字符串 def 却最少需要4步才能完成转变,两个字符串的最大长度才为 4 ,而完成转换却至少需要 4 步,相似度为 0 好像也能说的通,但是从表面来看,明明有一个相同的字符d,相似度为0,让我觉得很怪异,所以我认为再使用的时候需要慎重。

    下面是我的 C# 代码实现:

     1 using UnityEngine;
     2 using System.Collections;
     3 using System;
     4 
     5 public class EditorDistance
     6 {
     7     /// <summary>
     8     /// 比较两个字符串的相似度,并返回相似率。
     9     /// </summary>
    10     /// <param name="str1"></param>
    11     /// <param name="str2"></param>
    12     /// <returns></returns>
    13     public static float Levenshtein(string str1, string str2)
    14     {
    15         char[] char1 = str1.ToCharArray();
    16         char[] char2 = str2.ToCharArray();
    17         //计算两个字符串的长度。  
    18         int len1 = char1.Length;
    19         int len2 = char2.Length;
    20         //建二维数组,比字符长度大一个空间  
    21         int[,] dif = new int[len1 + 1, len2 + 1];
    22         //赋初值  
    23         for (int a = 0; a <= len1; a++)
    24         {
    25             dif[a, 0] = a;
    26         }
    27         for (int a = 0; a <= len2; a++)
    28         {
    29             dif[0, a] = a;
    30         }
    31         //计算两个字符是否一样,计算左上的值  
    32         int temp;
    33         for (int i = 1; i <= len1; i++)
    34         {
    35             for (int j = 1; j <= len2; j++)
    36             {
    37                 if (char1[i - 1] == char2[j - 1])
    38                 {
    39                     temp = 0;
    40                 }
    41                 else
    42                 {
    43                     temp = 1;
    44                 }
    45                 //取三个值中最小的  
    46                 dif[i, j] = Min(dif[i - 1, j - 1] + temp, dif[i, j - 1] + 1, dif[i - 1, j] + 1);
    47             }
    48         }
    49         //计算相似度  
    50         float similarity = 1 - (float)dif[len1, len2] / Math.Max(len1, len2);
    51         return similarity;
    52     }
    53 
    54     /// <summary>
    55     /// 求最小值
    56     /// </summary>
    57     /// <param name="nums"></param>
    58     /// <returns></returns>
    59     private static int Min(params int[] nums)
    60     {
    61         int min = int.MaxValue;
    62         foreach (int item in nums)
    63         {
    64             if (min > item)
    65             {
    66                 min = item;
    67             }
    68         }
    69         return min;
    70     }
    71 }
  • 相关阅读:
    Python学习笔记:List类型所有方法汇总
    Python学习笔记:String类型所有方法汇总
    制作“铜墙铁壁”一样的比特币冷钱包的完整流程详解!!
    jpa教程+ 常见的jpa报错以及解决方式
    Hibernate之mappedBy与@JoinColumn
    什么是JPA?Java Persistence API简介
    一文搞懂并发和并行
    清华大学操作系统【mark下】
    一文足以了解什么是 Java 中的锁.
    JDK8日常开发系列:Consumer详解
  • 原文地址:https://www.cnblogs.com/xiaoyulong/p/8846745.html
Copyright © 2011-2022 走看看