迭代N个符号的所有L长度序列,包括所有N个符号

时间:2014-01-28 04:09:18

标签: python algorithm iterator

在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个符号,所以似乎更好的解决方案会以某种方式从置换解决方案开始,然后以某种方式添加额外重复的符号,但我无法弄清楚如何在不重复计算的情况下执行此操作(并且没有求助于保存所有先前的输出以检查重复。)

2 个答案:

答案 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)

您可以通过将问题分为两部分来实现:

  1. 查找N个符号大小为L的每个可能的多个集合,其中包含每个符号至少一次。

  2. 对于每个多重集,找到所有唯一的排列。

  3. 为简单起见,我们假设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)。