请原谅我以这样一般的方式询问,因为我确信他们的表现取决于他们如何使用它们,但在我的情况下,collections.deque
比collections.defaultdict
慢得多,当我想要验证是否存在值。
我使用spelling correction from Peter Norvig来验证用户对一小组单词的输入。因为我没有使用带字频率的字典,所以我最初使用了一个简单的list
而不是defaultdict
,但只要我注意到单个字词查找,就用deque
替换它大约25秒。
令人惊讶的是,这并不比使用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
答案 0 :(得分:10)
这三种数据结构不可互换,它们的用途非常不同,并且具有非常不同的特征:
所有这些属性都很重要,无论何时选择一个属性都要记住它们。在这种特殊情况下破坏你的脖子的是字典的最后一个属性和必须检查的可能更正的数量的组合。一些简单的组合应该得出这个代码为给定单词生成的编辑数量的具体公式,但是每个经常错误预测这些事情的人都会知道,即使对于普通单词,这个数字也会出乎意料地大。
对于这些编辑中的每个,都会检查edit in NWORDS
以清除导致未知单词的编辑内容。在Norvig的程序中没有一点问题,因为in
检查(密钥存在检查),如前所述,非常快。但是你用一个序列(一个双端队列)交换了字典!对于序列,in
必须迭代整个序列并将每个项目与搜索的值进行比较(它可以在找到匹配时停止,但由于编辑最少的是知道位于双端队列开头的单词,它通常仍会搜索所有或大部分双端队列。由于有很多单词并且对每个生成的编辑进行了测试,因此您最终会花费99%的时间在一个序列中进行线性搜索,在该序列中您可以对字符串进行散列并比较一次(或最多 - 在碰撞的情况 - 几次)。
如果你不需要权重,你可以在概念上使用你从未看过的虚假值,并且仍然可以获得O(1)in
检查的性能提升。实际上,你应该使用一个set
,它使用几乎与字典相同的算法,只是切掉它存储值的部分(它实际上是首先实现的,我不知道它到底有多远两个分歧,因为集合在一个专用的,单独的C模块中重新实现。)