在Python中避免(或加速)大循环?

时间:2018-03-03 11:15:17

标签: python loops for-loop sage

我正在使用SageMath执行一些数学计算,有一次我有一个看起来像这样的for循环:

uni = {}
end = (l[idx]^(e[idx] - 1)) * (l[idx] + 1) # where end in my case is about 2013265922, 
                                           # but can also be much much larger too.
for count in range(0, end):
    i = randint(1, 303325737249669131)     # this executes very fast in Sage
    if i in uni:
        uni[i] += 1
    else:
        uni[i] = 1

所以基本上,我想在给定范围内创建非常大量的随机整数,检查数字是否已经在字典中,如果是,则增加其计数,如果没有将其初始化为1.但是,循环需要很长一段时间它没有在合理的时间内完成,并且不是因为循环内部的操作很复杂,而是因为有大量的迭代要执行。因此,我想问一下在Python中是否有任何方法可以避免(或加速)这种循环?

2 个答案:

答案 0 :(得分:1)

我描述了你的代码(使用cProfile)并且花费了绝大部分时间,花费在为循环的每次迭代调用的randint函数中。

我建议你使用numpy随机数生成库来矢量化循环,然后调用Counter类来提取频率计数。

import numpy.random
import numpy
from collections import Counter

assert 303325737249669131 < 18446744073709551615 # limit for uint64
numbers = numpy.random.randint(low=0, high=303325737249669131, size=end, 
dtype=numpy.uint64)
frequency = Counter(numbers)

对于1000000次迭代的循环(小于你建议的循环),我观察到从6秒减少到大约1秒。因此,即使这样,您在计算时间方面也不会超过一个数量级的减少。

您可能认为在内存中保留所有值的数组是低效的,并且可能在计算结束之前导致内存耗尽。但是,由于&#34; end&#34;与随机整数的范围相比,您将记录冲突的速率很低,因此完整数组的内存成本并不比存储字典大得多。但是,如果这成为问题,您可能希望分批执行计算。在这种精神下,您可能还希望使用多处理工具在多个CPU甚至许多机器上分配计算(但如果您选择了这些计算机,请注意网络成本)。

答案 1 :(得分:0)

在没有低级魔法的情况下你可以使用defaultdict,即

uni = defaultdict(int)
for count in range(0, end):
    i = randint(1, 303325737249669131)     # this executes very fast in Sage
    uni[i] += 1

如果您使用的是python2,请将range更改为xrange

除此之外 - 我很确定它接近python的极限。循环是

  • 生成随机整数(尽可能优化,无需静态输入)
  • 计算哈希
  • 更新字典。 defaultdict if-else分支是更优化代码的因素。
  • 不时,malloc调用调整dict的大小 - 这很快(考虑到为dict预分配内存的可能性)
相关问题