Python - 优化组合生成

时间:2017-09-15 11:26:14

标签: python performance combinations

为什么第一种方法这么慢?

速度可能会慢1000倍,有关如何加快速度的想法吗?

在这种情况下,性能是第一优先。在我的第一次尝试中,我试图让它成为多种价格,但它也很慢。

Python - Set the first element of a generator - Applied to itertools

import time
import operator as op
from math import factorial
from itertools import combinations


def nCr(n, r):

    # https://stackoverflow.com/a/4941932/1167783

    r = min(r, n-r)
    if r == 0: 
        return 1
    numer = reduce(op.mul, xrange(n, n-r, -1))
    denom = reduce(op.mul, xrange(1, r+1))
    return numer // denom


def kthCombination(k, l, r):

    # https://stackoverflow.com/a/1776884/1167783

    if r == 0:
        return []
    elif len(l) == r:
        return l
    else:
        i = nCr(len(l)-1, r-1)
        if k < i:
            return l[0:1] + kthCombination(k, l[1:], r-1)
        else:
            return kthCombination(k-i, l[1:], r)


def iter_manual(n, p):

    numbers_list = [i for i in range(n)]

    for comb in xrange(factorial(n)/(factorial(p)*factorial(n-p))):

        x = kthCombination(comb, numbers_list, p)

        # Do something, for example, store those combinations
        # For timing i'm going to do something simple



def iter(n, p):

    for i in combinations([i for i in range(n)], p):

    # Do something, for example, store those combinations
    # For timing i'm going to do something simple
        x = i


#############################


if __name__ == "__main__":  

    n = 40
    p = 5

    print '%s combinations' % (factorial(n)/(factorial(p)*factorial(n-p)))

    t0_man = time.time()

    iter_manual(n, p)

    t1_man = time.time()
    total_man = t1_man - t0_man


    t0_iter = time.time()

    iter(n, p)

    t1_iter = time.time()
    total_iter = t1_iter - t0_iter

    print 'Manual: %s' %total_man
    print 'Itertools: %s' %total_iter
    print 'ratio: %s' %(total_man/total_iter)

1 个答案:

答案 0 :(得分:1)

这里有几个因素在起作用。

最重要的是垃圾收集。由于GC暂停,任何产生大量不必要分配的方法都会变慢。在这种情况下,列表推导是 fast (对于Python),因为它们在分配和执行时被高度优化。在速度很重要的地方,更喜欢列表理解。

接下来你已经有了函数调用。正如@roganjosh在评论中指出的那样,函数调用相对昂贵。如果函数生成大量垃圾或持有长期闭包,那么(再次)尤其如此。

现在我们来到循环。垃圾也是最大的问题,将变量提升到循环之外,并在每次迭代时重复使用它们。

最后但同样重要的是,Python在某种意义上是托管的语言:通常在CPython运行时。在运行时本身实现的任何东西(特别是如果有问题的东西是用C而不是Python本身实现的话)将比你的(逻辑上等价的)代码更快。

注意

所有这些建议都会对代码质量产生不利影响。谨慎使用。首先介绍。另请注意,编译器通常足够聪明,可以为您完成所有这些操作,例如,PyPy通常对于与标准Python运行时相同的代码运行得更快,因为它在运行代码时会对您进行这样的优化。

注2

其中一个实现使用reduce。从理论上讲,减少可能很快。但这并不是出于很多原因,其中的主要原因可能总结为#34; Guido没有/不关心&#34;。因此,当速度很重要时,请不要使用reduce