提高Python中大词典的速度3.4

时间:2015-01-03 16:35:33

标签: python performance dictionary

所以我正在使用1 000 000个密钥在字典上工作,我的任务是让它在3秒内工作(在Intel 2.4 GHz上)。我尝试分析我的代码并且while循环有很多命中,但我无法找到一种方法来让我的代码在没有它的情况下运行得更快。有没有办法改进我的代码,以使其更快地工作?

代码应该(并且它会这样做,但是太慢)创建一个字典,其中键都是从2到999999的整数,并且值是从序列模式生成的列表的长度。模式是:如果整数是偶数,则将其除以2,如果整数是奇数且大于1,则将其乘以3并加1。这将持续到我们达到数字1。

示例:3 - > 10 - > 5 - > 16 - > 8 - > 4 - > 2 - >这个清单的长度是8。

代码:

import time
start = time.clock()

first = 2
last = 1000000

def function1(n,d):
    if n/2 in d:
        d[n] = d[n/2] + 1
    if n not in d:
        d[n] = 0
        temp = n
        while temp > 1:
            if temp%2 == 0:
                temp /= 2
                d[n] += 1
            else:
                temp = 3*temp + 1
                d[n] += 1
            if temp in d:
                d[n] += d[temp]
                break
    return d[n]

d={}
d[1]=1
d={key: function1(key,d) for key in range(first,last)}

print(time.clock() - start)

3 个答案:

答案 0 :(得分:4)

在我的系统上,你的代码确实需要花费不到3秒的时间(在最新的2.3 GHz Intel Core i7 Macbook Pro上使用Python 3.4)。

通过使用局部变量并避免两次构建字典,我可以在3秒内(到2.65秒,减少12%)得到它:

def function1(n,d):
    if n/2 in d:
        d[n] = d[n/2] + 1
        return
    if n not in d:
        length = 0
        temp = n
        while temp > 1:
            if temp%2 == 0:
                temp //= 2
            else:
                temp = 3*temp + 1
            length += 1
            if temp in d:
                length += d[temp]
                break
        d[n] = length

d={1: 1}
for key in range(first,last):
    function1(key, d)

请注意,我使用的是本地length变量,而不是始终从d[n]读取长度。 Python中的本地存储在C数组中,避免了对键进行散列并进行查找(可能包括散列冲突)。

我从/(浮点除法)切换到//(整数除法);当你感兴趣的是整数结果时,不需要处理小数点。

如果在字典中找到n/2,我也返回。在测试成功之后测试n not in d是没有意义的,因为我们刚刚添加了d[n]

词典理解完全是多余的,function1()已经就地改变了d,所以建立一个新的词典来取代现有的结果毫无意义。

下一步是使用您刚刚计算的temp值序列。当您从3开始时,您会计算其他几个值;完成后,所有这些内容都可以存储在d中,因此您无需重新计算10516,{{ 1}}和8

4

此处def function1(n,d): if n not in d: length = 0 seen = [] while n > 1: seen.append(n) if n % 2 == 0: n //= 2 else: n = 3 * n + 1 length += 1 if n in d: length += d[n] break for num in seen: d[num] = length length -= 1 需要8个步骤,但我们可以为3存储7个,为10存储6个等等。

我完全放弃了5测试,if n/2 in d循环已经处理了这种情况。由于while块中不再需要n,因此我完全放弃了if n not in d并继续使用temp

现在整个测试只需1.75秒。

答案 1 :(得分:1)

我相信一个有用的优化(在我的MB Air上获得2.4秒,在1.3 GHz时获得Core i5,在Python 2.7.3中获得最佳3次;使用3.4.1秒,2.7秒)是为了避免“浪费” temp的各种计算 - 保持它们可以让你非常直接地计算它们的d值。这是我的版本......:

import time
start = time.clock()

first = 2
last = 1000000

def function1(n, d):
    if n%2 == 0 and n//2 in d:
        d[n] = d[n//2] + 1
    if n not in d:
        temp = n
        intermediates = []
        while temp > 1:
            if temp%2 == 0:
                temp //= 2
            else:
                temp = 3 * temp + 1
            if temp in d:
                d[n] = res = d[temp] + len(intermediates) + 1
                for i, temp in enumerate(intermediates, 1):
                    d[temp] = res - i
                return res
            else:
                intermediates.append(temp)
    return d[n]

d={1: 1}
for k in range(first, last): function1(k, d)

print(time.clock() - start)

答案 2 :(得分:0)

这是Collatz conjecture。你可以查看一些关于互联网的信息,一些关于它的C或C ++代码,也许是Python。我想你会发现人们以前做过的一些有用的功能。

  

你也可以使用numpy模块,你可以用它制作一些公式,我认为用它会更快.Numpy是一个你可以轻松进行数学运算的模块。