【问题标题】:What algorithm gives suggestions in a spell checker?什么算法在拼写检查器中给出建议?
【发布时间】:2011-01-18 16:34:34
【问题描述】:

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

起初,我认为将输入的每个新单词(如果在字典中找不到)与字典中的每个其他单词中的 Levenshtein distance 进行检查并返回最高结果可能是有意义的。但是,这似乎效率很低,必须重复评估整个字典。

这通常是如何完成的?

【问题讨论】:

    标签: algorithm language-agnostic spell-checking levenshtein-distance


    【解决方案1】:

    good essay by Peter Norvig 如何实现拼写校正器。这基本上是一种蛮力方法,尝试具有给定编辑距离的候选字符串。 (Here 是一些提示,您可以使用 Bloom Filterfaster candidate hashing 提高拼写校正器的性能。)

    对拼写检查器的要求较弱。您只需要找出字典中没有的单词。您可以使用Bloom Filter 来构建消耗更少内存的拼写检查器。 Jon Bentley 在Programming Pearls 中使用 64kb 的英文词典描述了一个古老的版本。

    BK-Tree 是另一种方法。一篇不错的文章是here

    Levenshstein 距离并不完全适合拼写检查器的编辑距离。它只知道插入、删除和替换。转置丢失并为 1 个字符的转置产生 2(它是 1 个删除和 1 个插入)。 Damerau–Levenshtein distance 是正确的编辑距离。

    【讨论】:

    • +1 用于相对未知的 BK-Tree 参考。这就是像 Google 这样的公司处理真实世界 [TM] 数据量的方式。
    • 对 BK-Trees here 有更好的解释。
    【解决方案2】:

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

    我们的想法是查看人们犯的拼写错误的类型,并设计散列函数,将不正确的拼写分配到与正确拼写相同的存储桶中。

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

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

    现在,当您发现拼写错误的单词时,您会在辅助索引中查找拼写错误映射到的存储桶的冲突列表。 Ta da:你有一个建议清单!您所要做的就是对上面的单词进行排名。

    在实践中,您将需要一些带有其他哈希函数的辅助索引来处理其他类型的错误,例如转置字母、单/双字母,甚至是一个简单的类似 Soundex 的索引来捕获语音拼写错误。在实践中,我发现简单的发音方法有很长的路要走,并且基本上已经过时了一些旨在查找琐碎拼写错误的方法。

    所以现在您在每个辅助索引中查找拼写错误,并在排名前连接冲突列表。

    请记住,冲突列表仅包含字典中的单词。使用尝试生成替代拼写的方法(如 Peter Norvig 文章中所述),您可以获得(数万)个您首先必须根据字典过滤的候选者。使用预先计算的方法,您可能会得到几百个候选人,并且您知道他们都拼写正确,因此您可以直接跳到排名。

    更新:我后来发现了一个与此类似的算法描述,FAROO Distributed Search。这仍然是一个编辑距离有限的搜索,但它非常快,因为预计算步骤就像我的“坏哈希函数”想法一样。 FAROO 只是使用了一个有限的散列函数概念。

    【讨论】:

    • 感谢您参考 Faroos 的 SymSpell 算法。虽然这两种算法都预先计算可能的拼写错误并使用哈希表进行快速查找,但主要区别在于 SymSpell 保证在一定的编辑距离内检测所有可能的拼写错误(在这方面 SymSpell 相当于 Peter Norvig 的算法,只是3..6 个数量级),而您的算法使用启发式方法,该方法仅检测所有理论上可能的拼写错误的有限子集(因此您的预计算成本可能会更低)。
    • SymSpell 算法明确地预先计算并存储可能的错别字,我的“坏哈希”方案没有。对于英语,只需添加一个涵盖很多领域的简单语音散列(例如,“terradacktle”->“pterodactyl”,其编辑距离为 6)是微不足道的。当然,如果您需要多语言查找,那可能会更难。
    • 当然,通过利用关于可能的错别字(并限制在这些错别字)的经验知识,您可以节省预计算的时间和空间。但要涵盖所有可能的拼写错误,SymSpell 只需要预先计算其中的一小部分。一个 5 个字母的单词在最大编辑距离 3 内有大约 300 万个可能的拼写错误,但使用 SymSpell,您只需预先计算和存储 25 个删除。这对于不存在经验知识的拼写校正之外的模糊/相似性搜索很重要。
    【解决方案3】:

    算法

    1. 将拼写错误的单词作为输入。
    2. 将英语单词列表及其频率存储在一个文本文件中。
    3. 在三元搜索树中插入所有可用的英语单词(存储在文本文件中)及其频率(衡量一个单词在英语中的使用频率)。
    4. 现在沿着三元搜索树遍历 -
      • 对于在三元搜索树中遇到的每个单词,计算其与拼写错误的单词之间的 Levensthein 距离。
      • 如果 Levensthein 距离
      • 如果两个词的编辑距离相同,频率越高的词越重。 打印优先队列中的前 10 项。

    优化

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

    【讨论】:

    • 嗯,在这种情况下,从 'grater' 到 'greater' 的 Levenshtein 距离是不够的,因为 'grater' 也是一个字典词。 ;-)
    • @TonyBrasunas ,是的,你是对的。但是程序实际上会返回一个包含 10 个单词的列表,如果输入为“grater”,它会建议编辑距离为 0 的“grater”和编辑距离为 1 的“greater”。这可能会有所帮助。 ;)
    • 如果一个候选者的距离为2但非常频繁,而另一个候选者的距离为1但非常罕见,你如何对两者进行排名?在上述方法中,稀有物品总是会获胜,这是正确的结果吗?
    • @speedplane 是的。稀有的会赢。我认为这是正确的结果。因为我们期望的是最接近的词,基于输入词的拼写。如果您仍有疑问,请这样想——假设有一个用户拼写正确的稀有词。现在它的距离为 0,但频率非常低。现在在建议中,我们应该在顶部列出这个稀有词(距离为 0)(因为最小编辑距离获胜)和距离为 1-2-3 的其他词,在下面。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

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

      unix 方法需要非常少的空间来存储一个庞大的字典,因为它使用散列散列算法。

      【讨论】:

        猜你喜欢
        • 2011-03-16
        • 2011-05-11
        • 1970-01-01
        • 1970-01-01
        • 2013-04-15
        • 2019-04-16
        • 2014-12-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多