Python 什么是itertools的更快替代方案?
问题是上面的代码工作速度慢,并且在变量值较大时出错。我试过sklearn.cartesian,但在需要组合时得到了置换。我知道有一种方法可以让它在numpy中运行得更快,但我还不知道如何实现它。有关于numpy的答案,但我不明白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会遇到内存错误。此
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)