词汇/表达差异-1-编辑距离-莱文斯坦距离-Levenshtein

1.基本原理

其核心思想是:将一个字符串转换为另一个字符串所需的最少单字符编辑操作次数。

**编辑距离(Edit Distance)**衡量的是:

把字符串 A 转换成字符串 B 所需的最小编辑操作代价

最经典的是 Levenshtein Distance,允许三种操作:

操作 含义 代价
Insert 插入字符 1
Delete 删除字符 1
Substitute 替换字符 1(相同为 0)

核心逻辑

  • 编辑距离越大,字符串差异越显著;距离为 0 时,字符串完全一致。
  • 与汉明距离的区别:汉明距离仅适用于等长字符串且仅支持替换操作,编辑距离无长度限制、支持三种操作,适用范围更广。
  • 底层实现依赖动态规划,通过构建 DP 表避免暴力枚举的高复杂度(时间复杂度 O ( m × n ) , m / n O(m×n),m/n O(m×n),m/n 为两字符串长度)。

2.算法步骤

2.1 算法步骤讲解

🧠 动态规划的核心思想:"从小问题推大问题"

想象你有一个表格(像 Excel 表格),横着写 B 的每个字母,竖着写 A 的每个字母。

表格里的每一个格子 dp[i][j] 的意思是:

"把 A 的前 i 个字母 → 变成 B 的前 j 个字母,最少要几步?"

我们的任务,就是把这个表格一步步填满,最后右下角那个格子就是答案!

🔑 状态转移方程的通俗解释

现在,我们站在某个格子 dp[i][j] 上,想知道它的值是多少。

这时候,我们看 A 的第 i 个字母 和 B 的第 j 个字母(注意:编程里从 0 开始,所以实际是 A[i-1]B[j-1])。

情况 1️⃣:两个字母一样 (比如都是 't'
  • 那太好了!不需要任何操作。
  • 所以当前的最小步数 = 左上角格子的值(因为前面那部分已经搞定了)。

公式dp[i][j] = dp[i-1][j-1]

情况 2️⃣:两个字母不一样 (比如 'k' vs 's'
  • 这时候我们必须做一次操作 ,但有三种选择,我们要选最省事的那条路
操作 对应哪个格子? 含义
删除 A 的当前字母 dp[i-1][j] 上方 先把 A 的前 i-1 个变成 B 的前 j 个,再把 A 多出的这个字母删掉
插入 B 的当前字母 dp[i][j-1] 左方 先把 A 的前 i 个变成 B 的前 j-1 个,再在末尾加上 B 的这个字母
把 A 的当前字母改成 B 的 dp[i-1][j-1] 左上 先把前面都对齐了,再改这一个字母

👉 这三种方式都要 +1 步 (因为做了一次操作),我们取其中最小的那个

公式dp[i][j] = 1 + min( 上方, 左方, 左上方 )

🖼️ 举个小例子:A="ab", B="abc"

我们填表:

这一列表示A要变空白,需要删除的操作数
"" a b c
只一行表示A要从空变成B,需要插入的操作数 "" 0 1 2 3
a 1 0 1 2
b 2 1 0 1 ← 答案!

看右下角 dp[2][3](即 "ab" → "abc"):

  • A 最后是 'b',B 最后是 'c' → 不一样!
  • 看三个邻居:
    • 上方 dp[1][3] = 2(删 'b',再变 "a"→"abc"?不划算)
    • 左方 dp[2][2] = 0"ab"→"ab" 已完成,再插 'c' → 总共 1 步 ✅)
    • 左上 dp[1][2] = 1"a"→"ab" 花 1 步,再改 'b'→'c' → 共 2 步)

→ 最小的是 左方 + 1 = 0 + 1 = 1 ,所以答案是 1(只需插入 'c')。

✅ 总结一句话:

当前格子的值,取决于"左边、上边、左上角"哪个路径最短,再根据当前两个字母是否相同,决定要不要多花一步。

这就像是在玩一个"文字变形迷宫",每一步都选择代价最小的走法,最终走到终点时,总步数就是编辑距离。

2.2 算法实现伪代码

设字符串 A A A 长度为 m m m, B B B 长度为 n n n。

构建一个 ( m + 1 ) × ( n + 1 ) (m+1)×(n+1) (m+1)×(n+1) 的二维数组 d p dp dp,其中 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示 A [ 0 : i ] A[0:i] A[0:i] 与 B [ 0 : j ] B[0:j] B[0:j] 的编辑距离。

  1. 初始化
    d p [ 0 ] [ j ] = j dp[0][j] = j dp[0][j]=j(空串变 B [ 0 : j ] B[0:j] B[0:j] 需 j j j 次插入)
    d p [ i ] [ 0 ] = i dp[i][0] = i dp[i][0]=i( A [ 0 : i ] A[0:i] A[0:i] 变空串需 i i i 次删除)

  2. 状态转移方程
    对每个 i ∈ [ 1 , m ] , j ∈ [ 1 , n ] i∈[1,m], j∈[1,n] i∈[1,m],j∈[1,n]:

    if A[i-1] == B[j-1]:
    dp[i][j] = dp[i-1][j-1] # 字符相同,无需操作
    else:
    dp[i][j] = 1 + min(
    dp[i-1][j], # 删除 A[i-1]
    dp[i][j-1], # 插入 B[j-1]
    dp[i-1][j-1] # 替换 A[i-1] → B[j-1]
    )

  3. 结果
    d p [ m ] [ n ] dp[m][n] dp[m][n] 即为最终编辑距离。

3.优缺点适用场景

特点 说明
✅ 核心优点 直观易懂 :编辑操作(增、删、改)符合人类直觉。 应用广泛 :是许多文本处理任务的基石算法。 可灵活扩展:可通过为不同操作设置不同代价来适应特定场景。
❌ 主要缺点 计算成本高 :基础动态规划时间复杂度为 O(m*n) ,长文本计算慢。 语义盲区 :只比较字符,不理解单词意义(如无法知道"电脑"和"计算机"是近义词)。 长度敏感:长字符串间的微小差异也会导致距离值较大。
🛠️ 典型使用场景 1. 拼写检查与纠错 :为输入词在词典中快速找到最相似的候选词。 2. 数据清洗 :模糊匹配数据库中的名称(如"Jon Smith"与"John Smith")。 3. 语音识别评估 :计算词错误率(WER),即识别结果与正确文本的编辑距离。 4. 生物信息学 :比较DNA/RNA序列的相似性。 5. NER中 :实体规范化、拼写纠错、边界微调。一般用于 后处理阶段 6. 实体对齐 :不单独使用结合语义网络使用 7. 本体匹配 : 概念名 / 属性名初筛,Tree / Graph Edit Distance(进阶) 8. 搜索 / OCR / 输入纠错:模糊搜索、自动补全、OCR 错误修正

4.主流库推荐

特性 RapidFuzz python-Levenshtein textdistance difflib
速度 ⚡⚡⚡ 最快 ⚡⚡ 快 ⚡ 中等 🐢 慢
编辑距离支持 ✅(需导入子模块) ✅(distance ❌(只有 ratio)
高级模糊匹配 ✅(partial/token/WRatio) 部分 有限
多线程/批量 ✅(process.extract
跨平台安装 ✅(预编译 wheel) ⚠️(偶有编译问题) ✅(标准库)
维护状态 ✅ 活跃 ⚠️ 更新缓慢 ✅ 活跃 ✅(但功能固定)

💡 结论RapidFuzz 是当前 Python 生态中最推荐的模糊匹配库,尤其适合工业级应用。

5.RapidFuzz 使用

1. 极致性能(C++ + SIMD 加速)

  • 底层用 C++ 实现 ,并利用 SIMD 指令集(如 SSE2、AVX2)进行向量化计算;
  • 在大多数 Levenshtein 距离和相似度计算任务中,python-Levenshtein 快 2~10 倍
  • 支持多线程(通过 processes 参数),适合批量处理。

📊 实测参考(10k 字符串对):

  • RapidFuzz: ~0.8 秒
  • python-Levenshtein: ~2.5 秒
  • 纯 Python DP: >30 秒

2. 统一且现代化的 API

  • 所有函数命名清晰,参数一致,支持:

    • 单对字符串比较
    • 一对多(fuzz.ratio, fuzz.partial_ratio
    • 多对多(process.extract, process.cdist
  • 示例:

    python 复制代码
    from rapidfuzz import fuzz, process
    
    # 基础相似度(归一化 Levenshtein)
    score = fuzz.ratio("kitten", "sitting")  # → 57.14...
    
    # 从候选列表中找最匹配项
    choices = ["apple", "appel", "appl", "banana"]
    best = process.extractOne("apple", choices)
    # → ('appel', 88.88..., 1)

3. 丰富的相似度算法

不仅支持 Levenshtein,还内置多种实用变体:

函数 说明
fuzz.ratio 标准归一化编辑距离相似度
fuzz.partial_ratio 子串匹配(适合长短不一的文本)
fuzz.token_sort_ratio 忽略词序(先排序再比)
fzz.token_set_ratio 忽略重复词和顺序(取交集+差集)
fuzz.WRatio 自动选择最优策略(加权混合)

这些对实体对齐、地址/姓名模糊匹配极其有用。

4. 零依赖 & 跨平台友好

  • 纯 C++ 扩展,无需系统级依赖 (不像 python-Levenshtein 有时需 gcc 编译);
  • 提供预编译 wheel,完美支持 Windows、macOS(含 Apple Silicon)、Linux
  • 兼容 Python 3.7+。

5. 活跃维护 & 开源友好


⚠️ 二、潜在缺点或限制

问题 说明
不直接返回编辑距离整数 默认返回归一化相似度 (0~100)。若需原始编辑距离,可用: from rapidfuzz.distance.Levenshtein import distance
内存占用略高(批量时) 多线程/向量化会暂用更多内存,但通常可接受
生态整合稍弱 不如 scikit-learnpandas 原生集成(但可通过 apply 轻松使用)

🔧 获取原始编辑距离示例:

python 复制代码
from rapidfuzz.distance.Levenshtein import distance
d = distance("kitten", "sitting")  # → 3

📦 安装与快速开始

bash 复制代码
pip install rapidfuzz
python 复制代码
from rapidfuzz import fuzz, process
from rapidfuzz.distance.Levenshtein import distance

# 1. 相似度(0-100)
print(fuzz.ratio("hello", "hallo"))  # 80.0

# 2. 原始编辑距离
print(distance("hello", "hallo"))    # 1

# 3. 从列表中找最佳匹配
choices = ["Atlanta Falcons", "New York Jets", "Dallas Cowboys"]
result = process.extractOne("atlanta falcons", choices, scorer=fuzz.WRatio)
print(result)  # ('Atlanta Falcons', 100.0, 0)
相关推荐
baby_hua1 小时前
20251011_Pytorch深度学习(快速预览)
人工智能·pytorch·深度学习
会飞的小新1 小时前
大语言模型训练全流程(技术深度拆解版)---以DeepSeek为例
人工智能·语言模型·自然语言处理
jrlong2 小时前
三、Agent原理与最简实践学习笔记
人工智能·自然语言处理
工藤学编程2 小时前
零基础学AI大模型之RunnableLambda
人工智能
serve the people2 小时前
tensorflow 深度解析 Sequential 模型的输入形状指定
人工智能·python·tensorflow
陈橘又青2 小时前
开创性的初创企业利用 Amazon SageMaker孵化器释放企业价值
人工智能·网络协议·学习·ai·编辑器
Fabarta技术团队2 小时前
枫清科技受邀参加CMIS 2025第六届中国医药华北数智峰会
大数据·人工智能·科技
adaAS14143152 小时前
【矿物识别】基于改进YOLO13-C3k2-ContextGuided的铝土矿智能检测与分类系统
人工智能·分类·数据挖掘
小白狮ww2 小时前
abaqus 算例教程:考虑动水压力的 koyna 地震非线性动力响应分析
人工智能·深度学习·机器学习·abaqus·材料科学·工程模拟·混凝土抗震分析