哪种算法在拼写检查器中提供建议?


114

实现带有单词建议的拼写检查器时通常使用哪种算法?

起初,我认为检查每个键入的新单词(如果在词典中找不到)相对于其与词典中其他每个单词的Levenshtein距离并返回最佳结果可能是有意义的。但是,这似乎效率很低,必须反复评估整个字典。

通常如何完成?

Answers:


203

彼得·诺维格(Peter Norvig)一篇很好的文章,介绍如何实现拼写校正器。从根本上讲,这是一种蛮力尝试具有给定编辑距离的候选字符串。(以下是一些技巧,您可以使用布隆过滤器更快的候选哈希来提高拼写校正器的性能。)

拼写检查器的要求较弱。您只需找出字典中没有单词。您可以使用布隆过滤器来构建拼写检查器,从而减少内存消耗。乔恩·本特利(Jon Bentley)在Programming Pearls中使用64kb作为英语词典来描述一个古老的版本。

BK-树是一种替代方法。一篇不错的文章在这里

Levenshstein距离不是拼写检查器正确的编辑距离。它只知道插入,删除和替换。缺少换位,并为1个字符的换位生成2(即1个删除和1个插入)。Damerau–Levenshtein距离是正确的编辑距离。


2
相对未知的BK-Tree引用为+1。这就是像Google这样的公司使用Real-World(TM)大量数据的方式。
NoozNooz42年

2
有BK-树一个更好的解释这里
伊恩·博伊德

17

生成我已经成功使用过但在任何地方都从未见过的建议的一种方法是使用“不良”哈希函数预先计算建议(在构建字典时)。

这样做的目的是查看人们犯的拼写错误的类型,并设计散列函数,这些散列函数会将错误的拼写分配给与其正确的拼写相同的存储桶。

例如,一个常见的错误是使用错误的元音,例如definate而不是definite。因此,您需要设计一个将所有元​​音视为相同字母的哈希函数。一种简单的方法是首先对输入的单词进行“规范化”,然后通过常规的哈希函数将规范化的结果放入。在此示例中,归一化功能可能会删除所有元音,因此definite变为dfnt。然后用典型的散列函数对“归一化”的单词进行散列。

使用此特殊的哈希函数将所有词典单词插入辅助索引(哈希表)。该表中的存储桶将具有较长的冲突列表,因为哈希函数是“错误的”,但是这些冲突列表本质上是预先计算的建议。

现在,当您找到拼写错误的单词时,您会在辅助索引中查找该拼写错误映射到的存储桶的冲突列表。塔达:您有一个建议清单!您所要做的只是在上面排列单词。

在实践中,您将需要一些带有其他哈希函数的辅助索引来处理其他类型的错误,例如转置字母,单/双字母,甚至是一个简单的类似于Soundex的错误,以捕捉拼写错误。在实践中,我发现简单的发音方式可以走很长一段路,并且实际上已经淘汰了一些旨在查找琐碎错别字的发音。

因此,现在您在每个辅助索引中查找拼写错误,并在排名之前将冲突列表连接起来。

请记住,冲突列表仅包含词典中的单词。使用尝试生成替代拼写的方法(如Peter Norvig文章中所述),您可以获得(成千上万)首先要针对字典进行筛选的数千名候选人。使用预先计算的方法,您可能会获得数百名候选人,而且您知道他们的拼写都正确,因此您可以直接跳到排名。

更新:此后,我发现了一个与此算法类似的算法描述,即FAROO分布式搜索。这仍然是一个编辑距离受限的搜索,但是它非常快,因为预计算步骤的工作原理与我的“错误的哈希函数”想法类似。FAROO仅使用了错误哈希函数的有限概念。


感谢您引用Faroos的SymSpell算法。两种算法都在预先计算可能的拼写错误并使用哈希表进行快速查找时,主要区别在于SymSpell保证可以检测到一定编辑距离内的所有可能的拼写错误(在这方面,SymSpell等效于Peter Norvig的算法,只是速度提高了3..6个数量级),而您的算法正在使用启发式方法,该方法将仅检测所有理论上可能的拼写错误的有限子集(因此,您的预计算成本可能会更低)。
Wolf Garbe

SymSpell算法显式预先计算并存储可能的错别字,而我的“错误哈希”方案则不会。对于英语而言,仅添加一个覆盖大量地面的简单化语音哈希(例如“ terradacktle”->“翼手龙”,其编辑距离为6)是微不足道的。当然,如果您需要多语言查找,则可能会困难得多。
Adrian McCarthy

绝对地,通过利用有关可能的错别字的经验知识(并限制于这些错字),您可以节省预先计算的时间和空间。但是要覆盖所有可能的拼写错误,SymSpell仅需要预先计算其中的一小部分即可。一个5个字母的单词在最大3个编辑距离内可能存在约300万个拼写错误,但是使用SymSpell,您需要预先计算并存储25个删除。这对于不存在经验知识的拼写校正之外的模糊/相似性搜索非常重要。
Wolf Garbe

7

算法

  1. 输入拼写错误的单词作为输入。
  2. 将英语单词列表及其频率存储在文本文件中。
  3. 在三进制搜索树中插入所有可用的英语单词(存储在文本文件中)及其频率(衡量单词在英语中使用的频率)。
  4. 现在沿着三元搜索树遍历-
    • 对于三元搜索树中遇到的每个单词,请从拼写错误的单词计算其Levensthein距离。
    • 如果Levensthein Distance <= 3,则将该单词存储在Priority Queue中。
    • 如果两个词具有相同的编辑距离,则频率较高的一个词会更平滑。从“优先级队列”中打印前10个项目。

优化

  1. 如果输入单词的子字符串到当前单词的编辑距离大于3,则可以消除当前节点的子树中的单词。
    您可以在github project上找到更详细的解释和源代码。

嗯,在这种情况下,从“刨丝器”到“更大”的Levenshtein距离是不够的,因为“刨丝器”也是一个字典单词。;-)
Tony Brasunas

1
@TonyBrasunas,是的,您是对的。但是,在输入“ grater”的情况下,该程序实际上将返回一个10个单词的列表,它会建议编辑距离为0的“ grater”,以及编辑距离为1的“ greater”,这可能会有帮助。;)
amarjeetAnand

如果一个候选者的距离为2,但是非常频繁,而另一个候选者的距离为1,但是非常罕见,那么您如何对两个候选者进行排名?在上述方法中,稀有物品将永远获胜,这是正确的结果吗?
快艇

@speedplane是的。难得的人会赢。我认为这是正确的结果。因为我们期望的是基于输入单词的拼写最接近的单词。如果您仍然有疑问,请以这种方式考虑---假设有一个用户正确拼写的罕见单词。现在它的距离是0,但是频率很低。现在在建议中,我们应该在顶部列出该稀有单词(距离为0)(因为赢得最小编辑距离),而在下面列出其他距离为1-2-3的单词。
amarjeetAnand

3

您无需知道词典中每个单词的确切编辑距离。您可以在达到极限值后停止算法并排除单词。这将为您节省大量的计算时间。


1

拼写检查器非常容易实现,就像在Unix拼写程序中一样。源代码是公开可用的。可能涉及更正,一种技术是进行编辑,然后再次检查该新词是否在词典中。这样的新编辑可以被分组并显示给用户。

Unix系统使用Mc IllRoy编写的程序。另一种方法是使用Trie,它在文件很大的情况下很有用。

Unix方法由于使用散列散列算法,因此对于大型词典而言,其占用的空间非常少。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.