我有一个元组列表,我需要重复访问索引。最快的方法是将元组及其索引存储在dict中吗?或者有更好的方法吗?
给定元组有序列表中存在的元组,我需要有效地访问它的索引。我可以想到两种方法来做到这一点;在列表中调用.index
,或者将元组作为键存储在字典中,并将索引作为其值,并通过字典查找来查找索引。
order = list(itertools.product(range(4),repeat=6))
%timeit order.index(random.choice(order))
# 10000 loops, best of 3: 35 µs per loop
order_dic = {i:order.index(i) for i in order}
%timeit order_dic[random.choice(order)]
# 1000000 loops, best of 3: 443 ns per loop
答案 0 :(得分:2)
几乎总是,第二种选择会更快。这是因为在第一个示例中,您搜索列表以查找索引,列表上的 O(n)操作。而在第二个示例中,您在字典中查找了一个值,平均值为 O(1)。
但是,空间是需要考虑的因素,因为第二个选项具有额外字典的开销。在2014年Pycon的Brandon Rhodes的talk中,他说dicts倾向于使用超过他们需要的 3到4倍空间,因此添加新元素仍然很快,这可能很多如果你有一个庞大的元组列表,那就是开销。
如果您用作示例的元组列表实际上是您要搜索的列表,那么计算给定元组的索引有一种快速且内存有效的方法,依赖于方式itertools.product
有效:
a[5] + a[4] >> 2 + a[3] >> 4 + a[2] >> 6 + a[1] >> 8 + a[0] >> 10
这将给出元组a
的索引。这几乎与第二个选项一样快,但没有额外的空间要求额外的dict。
(这种方法背后的想法是元组本质上是一个六位数的四位数,并且由于itertools.product
按顺序输出它们,这将给出索引。)
答案 1 :(得分:0)
看起来真的很难比dict()
更快。只需查看random.choice()
在没有任何查找的情况下需要多长时间:
>>> order = list(itertools.product(range(4),repeat=6))
>>> order_dic = {i:order.index(i) for i in order}
>>> %timeit order.index(random.choice(order))
>>> %timeit order_dic[random.choice(order)]
>>> %timeit random.choice(order)
10000 loops, best of 3: 98.6 µs per loop
1000000 loops, best of 3: 1.6 µs per loop
1000000 loops, best of 3: 1.39 µs per loop
因此,查找仅占您测量总数的15%。
但也许您可以考虑将索引存储在元组中?或者将元组存储在另一个元组中,该元组也有索引?例如order = list(enumerate(order))
。
您可以为每个元组对象分配索引:
order = list(itertools.product(range(4),repeat=6))
order_dic = {i:order.index(i) for i in order}
%timeit order.index(random.choice(order))
%timeit order_dic[random.choice(order)]
%timeit random.choice(order)
class mytuple(tuple): pass
order = list(map(mytuple, order))
for i, t in enumerate(order): t.rev_index = i
%timeit random.choice(order).rev_index
## -- End pasted text --
10000 loops, best of 3: 101 µs per loop
1000000 loops, best of 3: 1.59 µs per loop
1000000 loops, best of 3: 1.44 µs per loop
1000000 loops, best of 3: 1.53 µs per loop
它比哈希映射更快。
你can't set attributes of builtins dynamically,所以我们必须继承它。我们也无法使用__slots__
,因为像tuple
这样的可变大小类型不支持它,因此每个mytuple
都会有一个额外的__dict__
。但显然这仍然比使用一个dict
映射所有索引的内存占用空间更小(几乎两倍):
>>> from pympler.asizeof import asizeof
>>> asizeof(order_dic) \
... / (asizeof(order) - asizeof(list(itertools.product(range(4),repeat=6))))
1.8341168569509738