在python中执行以下操作的有效方法是什么? 给定N个符号,迭代N个符号的所有L长度序列,包括所有N个符号。
顺序无关紧要,只要所有序列都被覆盖,并且每次只有一次。
我们称之为迭代器seq(符号,L)。然后,例如,
列表(SEQ([1,2,3],2))= []
list(seq([1,2,3],3))= [(1,2,3),(1,3,2),(2,1,3),(2,3,1),( 3,1,2),(3,2,1)]
list(seq([1,2,3],4))= [(1,1,2,3),(1,1,3,2),(1,2,1,3),...
这是一个直观而又缓慢的实现:
import itertools
def seq(symbols,L):
for x in itertools.product(symbols,repeat=L):
if all(s in x for s in symbols):
yield x
当N很大而L接近N时,会浪费很多精力。例如,当L == N时,使用itertools.permutations()会好得多。由于每个序列都需要包含所有N个符号,所以似乎更好的解决方案会以某种方式从置换解决方案开始,然后以某种方式添加额外重复的符号,但我无法弄清楚如何在不重复计算的情况下执行此操作(并且没有求助于保存所有先前的输出以检查重复。)
答案 0 :(得分:2)
一个想法:
import itertools
def solve(size, symbols, todo = None):
if todo is None: todo = frozenset(symbols)
if size < len(todo): return
if size == len(todo):
yield from itertools.permutations(todo) # use sorted(todo) here
# for lexicographical order
return
for s in symbols:
for xs in solve(size - 1, symbols, todo - frozenset((s,))):
yield (s,) + xs
for x in solve(5, (1,2,3)):
print(x)
将打印所有大小为5的序列,其中包含1,2,3和2个任意元素。如果你的目标是提高效率,你可以使用位掩码而不是set
,但我想你不是因为你使用的是Python :)从输出大小是线性的,这种复杂性是最佳的。
一些“证明”:
$ python3 test.py | wc -l # number of output lines
150
$ python3 test.py | sort | uniq | wc -l # unique output lines
150
$ python3 test.py | grep "1"|grep "2"|grep "3"| wc -l # lines with 1,2,3
150
答案 1 :(得分:1)
您可以通过将问题分为两部分来实现:
查找N个符号大小为L的每个可能的多个集合,其中包含每个符号至少一次。
对于每个多重集,找到所有唯一的排列。
为简单起见,我们假设N个符号是range(N)
中的整数。然后我们可以将多个集合表示为长度为N的向量,其值为非负整数,并且总和为L.要限制多个集合至少包含一个符号,我们要求向量中的值都是严格正的。
def msets(L, N):
if L == N:
yield (1,) * L
elif N == 1:
yield (L,)
elif N > 0:
for i in range(L - N + 1):
for m in msets(L - i - 1, N - 1):
yield (i + 1,) + m
不幸的是,itertools.permutations
不会产生具有重复元素的列表的唯一迭代。如果我们用C ++编写它,我们可以使用std::next_permutation
,它确实产生了独特的迭代。在链接页面上有一个示例实现(在C ++中,但它很容易将其转换为Python)。