【问题标题】:Why is collections.deque slower than collections.defaultdict?为什么 collections.deque 比 collections.defaultdict 慢?
【发布时间】:2011-10-19 18:07:42
【问题描述】:

请原谅我以如此笼统的方式提问,因为我确信它们的性能取决于人们如何使用它们,但在我的情况下,collections.dequecollections.defaultdict 慢得多,因为我想验证是否存在一个值。

我使用spelling correction from Peter Norvig 来根据一小组单词验证用户的输入。由于我没有使用带有词频的字典,所以我最初使用简单的 list 而不是 defaultdict,但当我注意到单个单词查找大约需要 25 秒时,将其替换为 deque

令人惊讶的是,这并不比使用 list 快,所以我重新使用 defaultdict,它几乎立即返回结果。

有人可以向我解释一下这种性能差异吗?

提前致谢


PS:如果你们中的任何一个想要重现我所说的内容,请更改 Norvig 脚本中的以下行。

-NWORDS = train(words(file('big.txt').read()))
+NWORDS = collections.deque(words(file('big.txt').read()))

-return max(candidates, key=NWORDS.get)
+return candidates

【问题讨论】:

    标签: python performance spell-checking deque defaultdict


    【解决方案1】:

    这三种数据结构不可互换,它们的用途非常不同,并且具有非常不同的特性:

    • 列表是动态数组,您可以使用它们按顺序存储项目以实现快速随机访问,用作堆栈(在末尾添加和删除)或仅存储某些内容,然后以相同的顺序对其进行迭代。
    • 双端队列也是序列,仅用于在两端添加和删除元素,而不是随机访问或类似堆栈的增长。
    • 字典(提供默认值只是相对简单方便,但 - 对于这个问题 - 无关扩展)是哈希表,它们将功能齐全的键(而不是索引)与值相关联,并提供对值的非常快速的访问通过密钥和(必然)非常快速地检查密钥是否存在。他们不维护秩序,并且要求密钥是可散列的,但是,你不能在不打破鸡蛋的情况下制作煎蛋。

    所有这些属性都很重要,当您选择其中一个时,请牢记它们。在这种特殊情况下,让您感到头疼的是字典的最后一个属性和必须检查的可能更正的数量的组合。一些简单的组合应该得出此代码为给定单词生成的编辑次数的具体公式,但每个经常错误预测此类事情的人都会知道,即使对于普通单词,它也会是惊人的大数字。

    对于这些编辑中的每个,有一个检查edit in NWORDS 以清除导致未知单词的编辑。 Norvig 的程序没有一点问题,因为in 检查(密钥存在检查)如前所述,非常快。但是你用一个序列(一个双端队列)交换了字典!对于序列,in 必须遍历整个序列并将每个项目与搜索的值进行比较(它可以在找到匹配项时停止,但由于最少的编辑是位于双端队列开头的已知单词,它通常仍然搜索所有或大部分双端队列)。由于有相当多的单词并且对生成的每个编辑都进行了测试,因此您最终会花费 99% 的时间在一个序列中进行线性搜索,您可以对一个字符串进行散列并比较一次(或最多 - 在碰撞的情况 - 几次)。

    如果您不需要权重,您可以在概念上使用您从未查看过的虚假值,并且仍然可以获得 O(1) in 检查的性能提升。实际上,您应该只使用set,它使用与字典几乎相同的算法,只是去掉它存储值的部分(它实际上最初是这样实现的,我不知道两者相差多远因为集合是在一个专用的、单独的 C 模块中重新实现的)。

    【讨论】:

    • 非常感谢您的深入回答。
    猜你喜欢
    • 1970-01-01
    • 2011-03-23
    • 2016-02-11
    • 2019-12-18
    • 2015-10-04
    • 2012-09-16
    • 2016-10-06
    • 2020-11-27
    • 2020-12-24
    相关资源
    最近更新 更多