假设我有一大堆单词。 (大约4-5千和增加)。有人搜索了一个字符串。不幸的是,在wordlist上找不到字符串。现在找到与输入字符串类似的单词的最佳和优化方法是什么?我想到的第一件事是计算词汇表的每个条目和输入字符串之间的Levenshtein距离。但这是优化的方式吗?
(请注意,这不是语言特定的问题)
答案 0 :(得分:1)
我认为存在比这更好的东西,但BK树至少是蛮力的优化。
它使用Levenshtein距离作为度量空间的属性,因此如果你的d
和任意字符串query
之间的Levenshtein距离为s
,那么您的所有结果必须与(d+n) to (d-n)
到s
保持一定距离。这里n是要输出的查询的最大Levenshtein距离。
这里详细解释:http://blog.notdot.net/2007/4/Damn-Cool-Algorithms-Part-1-BK-Trees
答案 1 :(得分:1)
如果您对代码本身感兴趣,我实现了一种算法,用于查找两个字符串之间的最佳对齐方式。它基本上展示了如何使用k
操作将一个字符串转换为另一个字符串(其中k
是字符串的Levenstein/Edit Distance
)。它可以根据您的需求进行简化(因为您只需要距离本身)。顺便说一句,它适用于O(mn)
,其中m
和n
是字符串的长度。我的实施基于:this和this。
#optimalization: using int instead of strings:
#1 ~ "left", "insertion"
#7 ~ "up", "deletion"
#17 ~ "up-left", "match/mismatch"
def GetLevenshteinDistanceWithBacktracking(sequence1,sequence2):
distances = [[0 for y in range(len(sequence2)+1)] for x in range(len(sequence1)+1)]
backtracking = [[1 for y in range(len(sequence2)+1)] for x in range(len(sequence1)+1)]
for i in range(1, len(sequence1)+1):
distances[i][0]=i
for i in range(1, len(sequence2)+1):
distances[0][i]=i
for j in range(1, len(sequence2)+1):
for i in range(1, len(sequence1)+1):
if sequence1[i-1] == sequence2[j-1]:
distances[i][j]=distances[i-1][j-1]
backtracking[i][j] = 17
else:
deletion = distances[i-1][j]+1
substitution = distances[i-1][j-1]+1
insertion = distances[i][j-1] + 1
distances[i][j]=min( deletion, substitution, insertion)
if distances[i][j] == deletion:
backtracking[i][j] = 7
elif distances[i][j] == insertion:
backtracking[i][j] = 1
else:
backtracking[i][j] = 17
return (distances[len(sequence1)][len(sequence2)], backtracking)
def Alignment(sequence1, sequence2):
cost, backtracking = GetLevenshteinDistanceWithBacktracking(sequence1, sequence2)
alignment1 = alignment2 = ""
i = len(sequence1)
j = len(sequence2)
#from backtracking-matrix we get optimal-alignment
while(i > 0 or j > 0):
if i > 0 and j > 0 and backtracking[i][j] == 17:
alignment1 = sequence1[i-1] + alignment1
alignment2 = sequence2[j-1] + alignment2
i -= 1
j -= 1
elif i > 0 and backtracking[i][j] == 7:
alignment1 = sequence1[i-1] + alignment1
alignment2 = "-" + alignment2
i -= 1
elif j > 0 and backtracking[i][j]==1:
alignment2 = sequence2[j-1] + alignment2
alignment1 = "-" + alignment1
j -= 1
elif i > 0:
alignment1 = sequence1[i-1] + alignment1
alignment2 = "-" + alignment2
i -= 1
return (cost, (alignment1, alignment2))
这取决于更广泛的背景以及您希望的准确程度。但我会(可能)开始:
max. distance
时停止算法应该不难。machine learning
方法。答案 2 :(得分:1)
编辑:新解决方案
是的,计算输入和单词列表之间的Levenshtein距离可能是一种合理的方法,但需要批次时间。 BK树可以改善这一点,但随着Levenshtein距离变大,它们会迅速变慢。似乎我们可以使用 trie 来加速Levenshtein距离计算,正如这篇优秀博客文章所述:
Fast and Easy Levenshtein distance using a Trie
它依赖于以下事实:Levenshtein距离的动态编程查找表在不同的调用中具有公共行,即levenshtein(kate,cat)
和levenshtein(kate,cats)
。
使用TWL06字典运行该页面上给出的Python程序,提供:
> python dict_lev.py HACKING 1
Read 178691 words into 395185 nodes
('BACKING', 1)
('HACKING', 0)
('HACKLING', 1)
('HANKING', 1)
('HARKING', 1)
('HAWKING', 1)
('HOCKING', 1)
('JACKING', 1)
('LACKING', 1)
('PACKING', 1)
('SACKING', 1)
('SHACKING', 1)
('RACKING', 1)
('TACKING', 1)
('THACKING', 1)
('WHACKING', 1)
('YACKING', 1)
Search took 0.0189998 s
这真的很快,在其他语言中会更快。大部分时间花在构建trie上,这是无关紧要的,因为它只需要完成一次并存储在内存中。
唯一的一个小缺点就是尝试占用大量内存(可以通过DAWG减少,但代价是速度)。
另一种方法:Peter Norvig在拼写纠正方面有一篇很棒的文章(包含完整的源代码)。
http://norvig.com/spell-correct.html
这个想法是建立可能的单词编辑,然后选择最可能的单词拼写纠正。