详细剖解Levenshtein距离算法(附python实现)

本文深入探讨了Levenshtein距离,一种衡量两字符串相似度的编辑距离算法。介绍了其基本概念、算法实现(包括动态规划方法)及Python库函数调用,适合NLP初学者理解与实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在实习期间,看到同事在做文字识别的相关项目,用Levenshtein距离作为评价模型好坏的标准之一。由于是行外人,当时对这个算法并没有任何了解,只听他介绍是用来判断两个字符是否相似的一种指标吧,直到后来自己也做NLP相关的项目,才好好钻研了一番,特此记录。

一、Levenshtein距离

一般的,我们在NLP中评价模型的时候,经常会使用计算得到的Levenshtein距离作为模型的评分(正确率或错误率)。

Levenshtein距离又称作编辑距离(Edit Distance),是指两个字符之间,有一个转变成另一个所需的最少编辑操作次数。被允许的操作有以下几种:
a. Replace替换,将一个字符替换成另一个字符
b. Insert插入,插入一个字符
c. Delete删除,删除一个字符

一般来说,编辑的距离越小,两个字符的相似度越大。不难分析出,两个字符串的编辑距离肯定不超过它们的最大长度(可以通过先把短串的每一位都修改成长串对应位置的字符,然后插入长串中的剩下字符)。

二、算法实现

通过编辑距离来判断两个字符串是否相等,属于动态规划算法的一种形式。

Levenshtein距离的数学定义:
在这里插入图片描述
其中当ai=bja_{i}=b_{j}ai=bj时,1(ai≠bj)1_{(a_{i}\neq b_{j})}1(ai=bj)为0,否则为1。leva,b(i,j)lev_{a,b}(i,j)leva,b(i,j)就是a的前i个字符与b的前j个字符的编辑距离。

a、b的相似度Sima,bSim_{a,b}Sima,bSima,b=1−(lev⁡a,b(∣a∣,∣b∣)/max(∣a∣,∣b∣))Sim_{a,b}=1-(\operatorname{lev}_{a,b}(\left | a \right |,\left | b \right |)/max(\left | a \right |,\left | b \right |))Sima,b=1(leva,b(a,b)/max(a,b))

1、简单举例

通过一个例子整理思路,如计算kitten和sitting之间的编辑距离:

kitten → sitten (替换 “k” -> “s”)
sitten → sittin (替换 “e” -> “i”)
sittin → sitting (插入"g")

上面的变化过程所需要的步数就是最小的步数,所以他们之间的编辑距离就是"3"

2、极端假设

给定字符串A和B,我们首先确定他们距离的上下界限:

  • 距离最小是两个字符串之间的长度的差值
  • 距离最大是两个字符串中较长字符串的长度
  • 当且仅当字符串相同时长度为0
  • 当字符串的长度相同时,距离的最大长度是 Hamming distance
  • 两个字符串之间的距离小于等于与另外一个字符串距离之和

根据以上规则,遍历每个字符利用递归可实现

//实现方法
private static int distance(String a, int len_a, String b, int len_b) {
    //递归回归点
    if (len_a == 0)
        return len_b;
    if (len_b == 0)
        return len_a;
    
    int cos;
    if (a.charAt(len_a-1) == b.charAt(len_b-1))
        cos = 0;
    else
        cos = 1;

    int re1 = distance(a, len_a - 1, b, len_b) + 1;
    int re2 = distance(a, len_a, b, len_b - 1) + 1;
    int re3 = distance(a, len_a - 1, b, len_b - 1) + cos;
    //返回在a中删除一个字符、在b中删除一个字符、ab中均删除一个字符获得结果中取最小值
    return re1 < re2 ? (re1 < re3 ? re1 : re3) : (re2 < re3 ? re2 : re3);
}
//测试
public static void main(String[] args) {
    String a = "kitten";
    String b = "sitting";
    int re = distnace(a, a.length(), b, b.length());
    System.out.println(re);
    //输出:3
}
3、动态规划

参考文章:

https://blog.youkuaiyun.com/jave_f/article/details/79859805
https://blog.youkuaiyun.com/asty9000/article/details/81384650
https://blog.youkuaiyun.com/优快云___LYY/article/details/85009190

def normal_leven(str1, str2):
  len_str1 = len(str1) + 1
  len_str2 = len(str2) + 1
  # 创建矩阵
  matrix = [0 for n in range(len_str1 * len_str2)]
  #矩阵的第一行
  for i in range(len_str1):
    matrix[i] = i
  # 矩阵的第一列
  for j in range(0, len(matrix), len_str1):
    if j % len_str1 == 0:
      matrix[j] = j // len_str1
  # 根据状态转移方程逐步得到编辑距离
  for i in range(1, len_str1):
    for j in range(1, len_str2):
      if str1[i-1] == str2[j-1]:
        cost = 0
      else:
        cost = 1
      matrix[j*len_str1+i] = min(matrix[(j-1)*len_str1+i]+1,
                    matrix[j*len_str1+(i-1)]+1,
                    matrix[(j-1)*len_str1+(i-1)] + cost)
 
  return matrix[-1]  # 返回矩阵的最后一个值,也就是编辑距离

三、库函数调用

应用的时候我们直接安装python-Levenshtein包,然后调用即可。

#pip install python-Levenshtein

import Levenshtein


In [56]: Levenshtein.distance('kitten', 'sitting')
Out[56]: 3

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jeremy_lf

你的鼓励是我的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值