【问题标题】:Why is the dictionary lookup not faster in this case?为什么在这种情况下字典查找速度不快?
【发布时间】:2021-11-04 11:10:48
【问题描述】:

我最近询问了the fastest way to create powers of ten,结果发现最快的方法实际上有点像sneaky workaround,您首先创建所有可能的值,然后在需要时简单地查找它们。

在解决方案中,list 用作查找表,但是,我刚刚了解到dicts should be much faster 在查找操作方面(参见例如here)。但是当我尝试使用dict 作为查找表时,过程实际上更慢

n = 200
 18 ns   18 ns   18 ns  f[n]  # list
 22 ns   22 ns   22 ns  g[n]  # dict

n = -200
 18 ns   18 ns   18 ns  f[n]  # list
 29 ns   29 ns   29 ns  g[n]  # dict

这是为什么呢?这是否与keys 是整数而不是字符串这一事实有关? (另外我猜sets不能用在这种情况下?)

这是我运行的代码:

from timeit import repeat


solutions = [
    'f[n]  # list',
    'g[n]  # dict',
]

for n in 200, -200:
    print(f'n = {n}')
    setup = f'''
n = {n}
f = [10.0 ** i for i in [*range(309), *range(-323, 0)]]
g = {{i: 10.0 ** i for i in range(-323, 309)}}
'''
    for solution in solutions:
        try:
            ts = sorted(repeat(solution, setup, repeat=50))[:3]
        except OverflowError:
            ts = [None] * 3
        print(
            *('%3d ns ' % (t * 1e3) if t else ' error ' for t in ts), solution
        )
    print()

【问题讨论】:

  • 对我来说,有趣的是 dict 对于负值变得明显变慢,而列表不受此影响。如果您将hash(n) 添加到测试的表达式中,您可能会发现对于负值也变得慢得多。还不知道为什么。
  • @KellyBundy 我认为这可能是由于 Python 缓存了小整数(在 -1 到 256 之类的范围内,IIRC)。必须将超出此范围的整数创建为新的 GC 对象。索引相同而不是仅仅相等的键稍微快一些,我想如果索引是唯一执行的操作,那么这种差异实际上可能相当显着。
  • 嗯,其实只有hash函数好像受影响,not dict access
  • 对于字典访问它seems to be due to collisions
  • @Jasmijn Argh,在我的 PC 上使用完整的 dict 测试整个范围 for n in range(-323, 309):(这样我可以运行更长时间)似乎再次受到 [-5, 256] 范围的影响。对于范围 [-323,-6],我得到非常一致的 137 ns,然后对于 [-5,0] 是 109 ns,除了 -1 的 183 ns,然后是 [1,256] 的 108 ns,然后是 [257,308] 的 136 ns。

标签: python list dictionary lookup


【解决方案1】:

collection[key_or_index] 对于listdict 都是O(1)。不同的是key_or_value in collection的表现。

这是“我的列表中 ith 的十次方是多少?”之间的区别?与“x 是我列表中的 10 的幂吗?”

列表的索引速度稍快,因为字典需要计算其键的哈希值,以及检查冲突。


混淆是因为“查找”既可以指索引操作,也可以指检查是否存在,具体取决于上下文。


以下是如何在列表和字典中执行等效操作的概述:

List Dictionary
Indexing lst[i] dct[key]
Checking for presence of key/index -len(lst) <= i < len(lst) key in dct
Checking for presence of value value in lst value in dct.values()
Looping over values for value in lst for value in dct.values()
Looping over keys/indexes for i in range(len(lst)) for key in dct
Looping over both for i, value in enumerate(lst) for key, value in dct.items()

【讨论】:

  • 谢谢!这很有意义。我不知道“查找”一词是模棱两可的。不幸的是,由于某种原因,我被否决了,所以我可能会删除帖子...
  • 希望你不要删帖,这是个好问题,我想你不会是最后一个有这种误解的人。我希望你也看看我刚刚添加的表格,列出了使用列表和字典的方式有何不同!
  • @mapf 不要删除这个问题,因为它也会删除这个答案。该表对初学者也非常有用。
猜你喜欢
  • 2014-09-13
  • 1970-01-01
  • 2020-07-27
  • 1970-01-01
  • 2012-08-01
  • 2019-12-10
  • 1970-01-01
  • 1970-01-01
  • 2017-01-07
相关资源
最近更新 更多