有效地访问索引:以最快的方式在索引中存储索引?

时间:2016-03-31 17:36:54

标签: python list dictionary

我有一个元组列表,我需要重复访问索引。最快的方法是将元组及其索引存储在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

2 个答案:

答案 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