Python元组作为键慢?

时间:2012-02-19 14:23:36

标签: python dictionary tuples key

我正在尝试在字典中对已排序的元组实现快速查找;回答“元组(3,8)是否具有相关价值的问题,如果是,它是什么?”。让元组中的整数从下面绑定0,从上面绑定max_int。

我继续使用Python的dict,但发现它很慢。解决这个问题的另一种方法是创建一个包含max_int(通常为空)dicts的列表T,并为每个元组(3,8)设置T [3] [8] = value。 我虽然这正是Python用dicts做的bucket-hash方法,但后者在这里快了大约30倍(!)。

此外,它很难看(特别是因为我现在要实现3元组),所以我非常感谢这里的一些提示。

供参考,以下是我用来获取时间的代码:

import numpy as np
import time

# create a bunch of sorted tuples
num_tuples = 10
max_int = 100
a = np.random.rand(num_tuples,2) * max_int
a = a.astype(int)
for k in xrange(len(a)):
    a[k] = np.sort(a[k])

# create dictionary with tuples as keys
d = {}
for t in a:
    d[tuple(t)] = 42

print d

# do some lookups
m = 100000
start_time = time.time()
for k in xrange(m):
    (3,8) in d.keys()
elapsed = time.time() - start_time
print elapsed

# now create the bucket-list structure mentioned above
t = [{} for k in xrange(max_int)]
for k in xrange(len(a)):
    t[a[k][0]][a[k][1]] = 42

print t

# do some lookups
m = 10000
start_time = time.time()
for k in xrange(m):
    8 in t[3].keys()
elapsed = time.time() - start_time
print elapsed

3 个答案:

答案 0 :(得分:18)

以下是Python 2.7的精确计时结果:

>>> %timeit (3, 8) in d.keys()  # Slow, indeed
100000 loops, best of 3: 9.58 us per loop

>>> %timeit 8 in t[3].keys()  # Faster
1000000 loops, best of 3: 246 ns per loop

>>> %timeit (3, 8) in d  # Even faster!
10000000 loops, best of 3: 117 ns per loop

>>> %timeit 8 in t[3]  # Slightly slower
10000000 loops, best of 3: 127 ns per loop

它们表明标准(3, 8) in d(无.keys()列表构建)实际上比(不太常规)8 in t[3]方法快一点,并且快两倍作为问题的相对较快8 in t[3].keys()。这个.keys /没有.keys差异来自于(3, 8) in d.keys()构建密钥的列表(在Python 2中)然后在此列表中查找(3, 8)的事实,这是比在字典(3, 8)的哈希表中查找d要慢得多。

如评论中所述,时序结果与Python 3不同:Python 3的keys()具有快速in测试,因为keys()返回键上的视图,因此in运算符可以使用相应字典的哈希表。

原始问题的速度差异来自d.keys()t[3].keys()相比构建相对较长的列表这一事实。

PS:%timeit函数由优秀的IPython shell提供。原始程序可以通过IPython %run prog.py执行。

答案 1 :(得分:3)

您正在测试不同的值。在字典版本中,有一个100,000个键的查找,而在bucket-list结构中,查找只有10,000个键。

除此之外,这段代码正在减慢速度:(3,8) in d.keys()如果您刚刚编写(3,8) in d,那么两个版本中的查找时间将非常相似,差异可以忽略不计。尝试这个修改过的测试,看看自己:

import numpy as np
import time

# create a bunch of sorted tuples
num_tuples = 10
max_int = 100
a = np.random.rand(num_tuples,2) * max_int
a = a.astype(int)
for k in xrange(len(a)):
    a[k] = np.sort(a[k])

# create dictionary with tuples as keys
d = {}
for t in a:
    d[tuple(t)] = 42

# do some lookups
m = 100000
start_time = time.time()
for k in xrange(m):
    if (3,8) in d:
        pass

elapsed = time.time() - start_time
print elapsed

# now create the bucket-list structure mentioned above
t = [{} for k in xrange(max_int)]
for k in xrange(len(a)):
    t[a[k][0]][a[k][1]] = 42

# do some lookups
m = 100000
start_time = time.time()
for k in xrange(m):
    if 8 in t[3]:
        pass

elapsed = time.time() - start_time
print elapsed

观察到的行为的原因是(3,8) in d.keys()8 in t[3].keys()每次都创建一个新的临时密钥列表,但第二个版本创建更短的列表。如果您只是使用了成语key in dictionary,则不再创建临时列表,并且两种方法的性能开始相似。

我会使用第一个版本,更简单,更易于阅读和理解,并且惯用 - 如果使用得当,其性能与第二个版本一样好。

答案 2 :(得分:0)

比较set.seed(1) n = 50 x = rpois(n, 2.2) cdf = function(x,n) { v=c() for(z in 1:max(x)) { a = length(x[x<=z])/n v = c(v, a) } plot(v,type="l", main="empirical cumulative distribution function", xlab="x", ylab="cumulative probability", xlim=c(0,6), ylim=c(0,1.0)) } cdf(x, n) (a, b) in d的速度有点奇怪,因为后者假定b in t[a]必须存在。

无论如何,第一种方式应该总是更快。这两个版本都包含 a b 。第一个是分配元组并对其进行散列的额外开销。但是,第二种方法是进行两次单独的字典查找。