具有特定重复的元素数组的Python置换
如何以最快的方式计算元素数组的排列,每个元素在排列中重复一定次数 例如:具有特定重复的元素数组的Python置换,python,combinations,permutation,Python,Combinations,Permutation,如何以最快的方式计算元素数组的排列,每个元素在排列中重复一定次数 例如: elements = [0, 1] repetitions = [2, 3] 我希望它返回10个唯一的排列: [(0, 0, 1, 1, 1), (0, 1, 0, 1, 1), (1, 0, 1, 0, 1) ....] 这不是一个完整的答案,而是总体思路。 我几乎可以肯定,没有递归和组合(itertools),没有简单的方法可以做到这一点。 假设您已经定义了数据、元素和重复 n是元素的大小 s是所有重复项的总和,
elements = [0, 1]
repetitions = [2, 3]
我希望它返回10个唯一的排列:
[(0, 0, 1, 1, 1), (0, 1, 0, 1, 1), (1, 0, 1, 0, 1) ....]
这不是一个完整的答案,而是总体思路。 我几乎可以肯定,没有递归和组合(itertools),没有简单的方法可以做到这一点。 假设您已经定义了数据、元素和重复
- n是元素的大小
- s是所有重复项的总和, 所以它是每个排列的最终元素数 寻找
from itertools import combinations
def multipleElementsPermutations(elements, repetitions): #elements is En, repetitions is Rn
if len(elements) != len(repetitions) or len(elements) <2 or len(repetitions) <2:
return None
elif len(elements) == 2:
#Write some code using itertools to solve the case n=2
return list(permutations(elements[0]*repetitions[0] + elements[1]*repetitions[1], repetitions[0]+repetitions[1]))
else:
last_element = elements[-1]
last_repetition = repetitions[-1]
#Here thus function will call itself with the n-1 arguments
previous_case_solution = multipleElementsPermutation(
elements[:-1], #En-1
repetitions[:-1] #Rn-1
)
#Write some code here to find different ways to add last element
您可以使用
itertools
模块中的permutations
>>> from itertools import permutations
>>> list(permutations([0]*2 + [1]*3, 5))
[(0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0)]
更自动的版本:
>>> e = [0, 1]
>>> r = [2, 3]
>>> b = sum([[i]*j for i, j in zip(e, r)], [])
>>> list(permutations(b, len(b)))
[(0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 0, 1, 1, 1), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 0, 1, 1), (0, 1, 0, 1, 1), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (0, 1, 1, 0, 1), (0, 1, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 0, 1, 1), (1, 0, 0, 1, 1), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 0, 1, 0, 1), (1, 0, 1, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 0, 0, 1), (1, 1, 0, 1, 0), (1, 1, 1, 0, 0), (1, 1, 1, 0, 0)]
编辑:如@JosephWood所述,如果您不想重复(00111可能出现多次),请查看
Edit2:如果元素中只有0,1,并且不希望重复,那么如果和(j)==3,则可以使用这样的二进制表示法
(j表示j in([i%2,i//2%2,i//4%2,i//8%2,i//16%2])。我不知道它是否更快。类似于@Erwan,它也可以工作:
import itertools as it
elements = [0, 1]
repetitions = [2, 3]
arr = []
for j,i in enumerate(repetitions):
arr = arr +[elements[j]]*i
set(list(it.permutations(arr)))
这里有一个解决方案:想法是找到所有可能的索引,我们可以把第一个元素,然后递归地找到可能的索引为其余的。这样,我们就可以保证直接生成唯一的输出
from itertools import combinations
def combs(elements, repetitions, index=0, indices_left=None, already_set=None):
if indices_left is None:
already_set = [None] * sum(repetitions)
indices_left = set(range(sum(repetitions)))
element = elements[index]
number = repetitions[index]
for indices_choice in combinations(indices_left, number):
currently_set = already_set[:]
for i in indices_choice:
currently_set[i] = element
remaining_indices = indices_left - set(indices_choice)
if not remaining_indices:
yield currently_set
else:
yield from combs(elements, repetitions, index+1, remaining_indices, currently_set)
通过您的示例输入,我们可以:
elements = [0, 1]
repetitions = [2, 3]
for comb in combs(elements, repetitions):
print(comb)
# [0, 0, 1, 1, 1]
# [0, 1, 0, 1, 1]
# [0, 1, 1, 0, 1]
# [0, 1, 1, 1, 0]
# [1, 0, 0, 1, 1]
# [1, 0, 1, 0, 1]
# [1, 0, 1, 1, 0]
# [1, 1, 0, 0, 1]
# [1, 1, 0, 1, 0]
# [1, 1, 1, 0, 0]
另一个包含更多元素的示例:
elements = [0, 1, 2]
repetitions = [2, 3, 2]
c = combs(elements, repetitions)
print(next(c))
# [0, 0, 1, 1, 1, 2, 2]
print(next(c))
# [0, 0, 1, 1, 2, 1, 2]
print(len(list(combs(elements, repetitions))))
# 210
一点速度测试:
elements = [0, 1]
repetitions = [10, 10]
a = list(combs(elements, repetitions))
print(len(a))
# 184756
%timeit list(combs(elements, repetitions))
# 1.15 s ± 5.52 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
很好,也许我们可以找到元素的解决方案。你的元素向量中只有0和1,或者你想要任何随机整数吗?这些被称为多重集,可以通过sympy
的multiset\u置换生成。这段代码生成120个元组,虽然只有10个不同的输出。@Thierrylahuille更正。谢谢。但问题是,在开始使用set()
删除重复项之前,您的代码需要生成大量相同的排列,所有排列都存储在内存中。这将很快变得不正式:例如,对于重复=[10,10]
,您需要生成一个包含2432902008176640000(2.4e18)项的列表,这是不可能的,因为需要时间和空间,而只有184756个唯一的排列。
elements = [0, 1]
repetitions = [10, 10]
a = list(combs(elements, repetitions))
print(len(a))
# 184756
%timeit list(combs(elements, repetitions))
# 1.15 s ± 5.52 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)