创建一个“拼写检查”,用合理的运行时检查数据库

时间:2011-01-28 22:42:02

标签: c# database algorithm runtime spell-checking

我不是要求实现拼写检查算法本身。我有一个包含数十万条记录的数据库。我要做的是针对所有这些记录检查表格中某个列的用户输入,并返回具有某个汉明距离的任何匹配(同样,这个问题不是关于确定汉明距离等)。当然,目的是创建一个“你是说”的功能,用户搜索名称,如果在数据库中找不到直接匹配,则返回可能匹配的列表。

我正试图想出一种方法,在最合理的运行时间内完成所有这些检查。如何以最有效的方式检查用户对所有这些记录的输入?

该功能目前已实现,但运行时速度非常慢。它现在的工作方式是将所有记录从用户指定的表(或多个表)加载到内存中,然后执行检查。

对于它的价值,我正在使用NHibernate进行数据访问。

如果我能做到这一点或我的选择是什么,我将不胜感激。

6 个答案:

答案 0 :(得分:7)

计算Levenshtein距离并不像您想象的那样昂贵。 Norvig article中的代码可以被认为是伪代码,以帮助读者理解算法。更有效的实现(在我的情况下,在20,000个术语数据集上快约300倍)是走trie。性能差异主要归因于无需分配数百万个字符串以进行字典查找,在GC中花费的时间少得多,并且您还可以获得更好的引用局部性,从而减少CPU缓存未命中率。通过这种方法,我可以在我的Web服务器上大约2ms进行查找。另一个好处是能够轻松返回以提供的字符串开头的所有结果。

缺点是创建trie很慢(可能需要一秒左右),因此如果源数据定期更改,那么您需要决定是重建整个事物还是应用增量。无论如何,您希望在构建后尽可能多地重用该结构。

答案 1 :(得分:3)

正如达卡拉所说,BK树是一个很好的第一次采取。它们很容易实现。通过Google可以轻松找到几种免费实现,但可以在此处找到更好的算法介绍:http://blog.notdot.net/2007/4/Damn-Cool-Algorithms-Part-1-BK-Trees

不幸的是,计算Levenshtein距离非常昂贵,如果您使用带有大字典的BK树,您将会做很多事情。为了获得更好的性能,您可以考虑使用Levenshtein Automata。实施起来有点困难,但也更有效,它们可以用来解决您的问题。同样棒的博主也有详细信息:http://blog.notdot.net/2010/07/Damn-Cool-Algorithms-Levenshtein-Automata。本文可能也很有趣:http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.16.652

答案 2 :(得分:2)

我认为Levenshtein distanceHamming distance更有用。

让我们举一个例子:我们接受example这个词并将自己限制在Levenshtein距离1.然后我们可以列举所有可能存在的拼写错误:

  • 1次插入(208)
    • aexample
    • bexample
    • cexample
    • ...
    • examplex
    • exampley
    • examplez
  • 1删除(7)
    • xample
    • eample
    • exmple
    • ...
    • 为例
  • 1替换(182)
    • axample
    • bxample
    • cxample
    • ...
    • examplz

您可以将每个拼写错误存储在数据库中,并将其链接到正确的拼写example。这种方法很有效,但会创建一个庞大的数据库。

注意大多数拼写错误是通过使用不同的字符执行相同操作而发生的:

  • 1次插入(8)
    • ?实例
    • E'xample
    • 前?充足
    • EXA?mple
    • 考?PLE
    • examp?文件
    • 为例的ΔE
    • 例如
  • 1删除(7)
    • xample
    • eample
    • exmple
    • 的exaple
    • examle
    • exampe
    • 为例
  • 1替换(7)
    • ?xample
    • E'充足
    • 前?mple
    • EXA?PLE
    • 考?文件
    • examp的ΔE
    • 为例?

看起来很容易管理。您可以为每个单词生成所有这些“提示”并将它们存储在数据库中。当用户输入单词时,从中生成所有“提示”并查询数据库。

示例:用户输入exaple(注意缺少m)。

SELECT DISTINCT word
           FROM dictionary
          WHERE hint = '?exaple'
             OR hint = 'e?xaple'
             OR hint = 'ex?aple'
             OR hint = 'exa?ple'
             OR hint = 'exap?le'
             OR hint = 'exapl?e'
             OR hint = 'exaple?'
             OR hint = 'xaple'
             OR hint = 'eaple'
             OR hint = 'exple'
             OR hint = 'exale'
             OR hint = 'exape'
             OR hint = 'exapl'
             OR hint = '?xaple'
             OR hint = 'e?aple'
             OR hint = 'ex?ple'
             OR hint = 'exa?le'
             OR hint = 'exap?e'
             OR hint = 'exapl?'
带有1次插入的

exaple == exa?ple == example 1次替换

另请参阅:How does the Google “Did you mean?” Algorithm work?

答案 3 :(得分:1)

  

它将用户指定的一个或多个表中的所有记录加载到内存中,然后执行检查

不要那样做

要么

  • 在后端进行匹配匹配 并且只返回你需要的结果。

  • 尽早将记录缓存到内存中 在工作集击中并做 你需要时检查。

答案 4 :(得分:1)

您需要以不同于数据库的方式构建数据。在客户端上构建自定义搜索树,包含所需的所有字典数据。虽然如果字典非常大,内存可能会成为问题,但搜索本身会非常快。 O(nlogn),如果我没记错的话。

查看BK-Trees

此外,请考虑Levenshtein distance

,而不是使用汉明距离

答案 5 :(得分:1)

您标记为正确的答案..

Note: when i say dictionary.. in this post, i mean hash map .. map.. 
 basically i mean a python dictionary

另一种方法是通过创建单词的倒排索引来提高其性能。

因此,不是根据整个数据库计算编辑距离,而是创建26个字典..每个字母都有一个键。所以英语有26个字母..所以键是“a”,“b”..“z”

所以假设你的数据库中有“苹果”

所以在“a”词典中:你添加“apple”这个词

在“p”词典中:添加“apple”一词

在“l”字典中:你添加“apple”一词

字典中的

:添加“apple”一词

所以,对字典中的所有单词都这样做..

现在输入拼写错误的单词时..

让我们说aplse

你从“a”开始,然后检索“a”中的所有单词

然后从“p”开始,找到“a”和“p”

之间的单词的交集

然后你从“l”开始,找到“a”,“p”和“l”之间的单词的交集

你为所有的字母表做了这个。

最后你会得到一堆由字母“a”,“p”,“l”,“s”,“e”组成的单词

在下一步中,您将计算输入字与上述步骤返回的一串字之间的编辑距离。从而大大缩短您的运行时间..

现在可能会出现无法返回任何内容的情况。

所以像“aklse”这样的东西......很有可能没有单词由这些字母组成。 在这种情况下,您必须开始将上述步骤反转到剩下有限数量的单词的阶段。

所以有点喜欢以* klse开头(单词k,l,s,e之间的交集)num(wordsreturned)= k1

然后a * lse(单词a,l,s,e之间的交叉点)...... numwords = k2

等等.. 选择返回的单词数量较多的那个..在这种情况下,实际上没有一个答案..因为很多单词可能具有相同的编辑距离...你可以说如果editdistance大于“k”那么没有好的比赛......

在此基础上构建了许多复杂的算法。

像这些很多步骤之后,使用统计推断(当输入是“aplse”时,这个词是“苹果”的概率等等)然后你去机器学习方式:)