我的问题:
例如,我有4个数字
a, b, c, d = np.random.rand(4)
我需要所有可能的总和a + b - c
,a + b -d
,b + c - d
,b + c -a
等
我发现我可以像这样
from itertools import permutations
p = np.array(list(set(permutations([1, 1, -1, 0])))).T
sums = np.random.rand(4) @ p
到目前为止,到目前为止,我已经解决了我的问题...伤害我的感觉是使用permutations(…)
,因为n
个数字组合的排列数当然是n!
,而set
所操作的过滤器会将有效排列的数量减少到百分之几。
我的问题:
是否有可能获得例如
p = np.array(list(set(permutations([1]*5 + [-1]*4 + [0]*6)))).T
没有计算所有1,307,674,368,000(几乎相同)的排列?
按照Lukas Thaler和Chris_Rands的建议,我检查了sympy.utilities.iterables.multiset_permutations
的可能用法
In [1]: from itertools import permutations
In [2]: from sympy.utilities.iterables import multiset, multiset_permutations
In [3]: for n in (2,3):
...: l = [1]*n +[2]*n + [3]*n
...: ms = multiset(l)
...: print(n*3)
...: %timeit list(multiset_permutations(ms))
...: %timeit set(permutations(l))
...:
6
568 µs ± 3.71 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
91.3 µs ± 571 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
9
9.32 ms ± 209 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
58.3 ms ± 323 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
在我看来
set
和itertools.permutations
的蛮力方法所以我得出的结论是我的问题与Chris所提到的问题重复,我投票决定将其结束。
答案 0 :(得分:1)
使用combinations
可以降低复杂度:
from itertools import combinations
import numpy as np
z = 6 # number of zeros
p = 5 # number of 1s
n = 4 # number of -1s
indexes = set(range(z+p+n))
res = np.array([(*plus, *minus, *(zero for zero in indexes - set(plus) - set(minus)))
for plus in combinations(indexes, p)
for minus in combinations(indexes-set(plus), n)])
perm = np.zeros((res.shape[0], z+p+n), dtype=np.int8)
perm[np.arange(res[:,:p].shape[0])[:,None], res[:,:p]] = 1
perm[np.arange(res[:,p:p+n].shape[0])[:,None], res[:,p:p+n]] = -1
基本上,我没有选择所有排列(复杂度(n+p+z)!
),而是先选择了正数元素(复杂度(n+p+z)!/(p!(n+z)!)
),然后选择了否定元素(复杂度(n+z)!/(n!z!)
)。产生的复杂度应类似于(n+p+z)!/(n!p!z!)
答案 1 :(得分:0)
我们可以这样操作,首先使用combinations
来选择求和中使用的数字,然后再次使用它来选择正(和负)数字。
from itertools import combinations, chain
from random import random
def our_sums_of(nums, num_positive):
return (2 * sum(positives) - sum(nums)
for positives in combinations(nums, num_positive))
nums = [random() for _ in range(15)]
num_positive = 5
num_negative = 4
list(chain.from_iterable(map(lambda c: our_sums_of(c, num_positive),
combinations(nums, num_positive + num_negative))))