Python 什么是itertools的更快替代方案?

Python 什么是itertools的更快替代方案?,python,numpy,combinations,itertools,Python,Numpy,Combinations,Itertools,问题是上面的代码工作速度慢,并且在变量值较大时出错。我试过sklearn.cartesian,但在需要组合时得到了置换。我知道有一种方法可以让它在numpy中运行得更快,但我还不知道如何实现它。有关于numpy的答案,但我不明白np.column\u堆栈((np.repeat(a,b.size),np.tile(b,a.size))在我的例子中应该如何工作。正如我现在所看到的,我将是数组中的许多元素,并且会发生变化,我不完全理解如何处理这个事实。我猜当k和n变得足够大时,您的f会遇到内存错误。此

问题是上面的代码工作速度慢,并且在变量值较大时出错。我试过sklearn.cartesian,但在需要组合时得到了置换。我知道有一种方法可以让它在numpy中运行得更快,但我还不知道如何实现它。有关于numpy的答案,但我不明白
np.column\u堆栈((np.repeat(a,b.size),np.tile(b,a.size))
在我的例子中应该如何工作。正如我现在所看到的,我将是数组中的许多元素,并且会发生变化,我不完全理解如何处理这个事实。

我猜当
k
n
变得足够大时,您的
f
会遇到内存错误。此变量应在不使用(大量)内存的情况下获取长度

它返回与
f
相同的计数:

In [167]: def f1(k,n): 
     ...:     p = [] 
     ...:     for i in range(n): 
     ...:         g = itertools.combinations([x for x in range(2**k)],i) 
     ...:         cnt = 0 
     ...:         for x in g: cnt += 1 
     ...:         p.append(cnt) 
     ...:     return p 
不过速度较慢

In [168]: f1(5,5)                                                                              
Out[168]: [1, 32, 496, 4960, 35960]
In [169]: f(5,5)                                                                               
Out[169]: [1, 32, 496, 4960, 35960]
尝试对
f
重复这些时间,我立即将
杀死。我应该从另一端试试:

In [170]: timeit f1(5,5)                                                                       
3.47 ms ± 14 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [171]: timeit f(5,5)                                                                        
2.72 ms ± 122 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [175]: timeit -r1 -n1 f1(5,5)                                                               
3.66 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
In [176]: timeit -r1 -n1 f1(6,5)                                                               
61.4 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
In [177]: timeit -r1 -n1 f1(7,5)                                                               
1.01 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
In [178]: timeit -r1 -n1 f1(8,5)                                                               
14.6 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
无论如何,它确实表明,我在不累加的情况下进行的计数处理的值比你的方法大,即使它开始的速度较慢。

使用的公式,你可以像这样迭代地进行计算:

def(k,n):
p=[1]

f=1最快的解决方案由@jdehesa给出,它使用乘法公式(递归)计算。以下是几项其他尝试:

In [179]: timeit -r1 -n1 f(8,5)                                                                
Killed
一个可能的改进是使用对称关系:


编辑:不幸的是,scipy的
二项式
并不总是返回准确的结果,因为它使用一些近似值来计算N的大值的二项式系数。同样地,由于N的大值的舍入问题,
f_itertools_累计
,也没有给出准确的结果

您需要组合的数量。为此,您为这些组合创建一个迭代器,然后将其放入一个列表中,然后计算列表的长度(此时您可以只计算迭代器)。这是一个明显的减速。但它甚至不相关,因为组合的数量有一个-你甚至不需要生成器。你甚至不需要自己实现它:看看你会得到什么样的中断/错误?我怀疑这与记忆有关。在这种情况下,
numpy
方法不会更好。创建大型数组与创建大型元组列表一样占用大量内存。确切的断点可能不同,但对于足够大的k和n,将有一个……或者你可以只做
cnt=scipy.special.comb(2**i,k,exact=True)
而不需要列表或生成器。@Amadan如果你有自己的答案,将其作为答案,你可以使用
math.comb
而不是自己定义函数。@Boris Yes,对于一个值,新的可能会更快,但是因为这里我们正在构建一个组合值列表,我认为这可能会更快,因为使用会“重复”很多工作。但是,您可以尝试一下,看看您得到了什么(我没有Python3.8来测试它)。
In [179]: timeit -r1 -n1 f(8,5)                                                                
Killed
from itertools import accumulate
from scipy.special import binom, comb
import math

def f_math_comb(k, n):
    # works with python 3.8
    N = 1 << k  # N = 2**k
    return [math.comb(N, i) for i in range(n)]

def f_scipy_comb(k, n):
    N = 1 << k 
    return [comb(N, i, exact=True) for i in range(n)]

def f_scipy_binom(k, n):
    N = 1 << k 
    return list(map(int, binom(N, range(n))))

def f_itertools_accumulate(k, n):
    N = 1 << k
    p = (N + 1) / np.arange(1, n) - 1
    int_round = lambda x: int(round(x))
    return [1] + list(map(int_round, accumulate(p, mul)))

def f_multip(k, n):
    # jdehesa's solution
    p = [1]
    f = 1 << k
    for i in range(1, n):
        p.append((p[-1] * f) // i)
        f -= 1
    return p
k = 8
n = 2**k + 1

%timeit f_math_comb(k, n)
3.32 ms ± 45 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit f_scipy_comb(k, n)
3.23 ms ± 75.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit f_scipy_binom(k, n)
189 µs ± 2.15 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit f_itertools_accumulate(k, n)
1.03 ms ± 12.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit f_multip(k, n)
102 µs ± 10.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)