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

Python 迭代N个符号的所有L长度序列,包括所有N个符号,python,algorithm,iterator,Python,Algorithm,Iterator,在python中执行以下操作的有效方法是什么? 给定N个符号,迭代N个符号的所有L长度序列,包括所有N个符号。 顺序并不重要,只要覆盖了所有序列,并且每个序列只覆盖一次 让我们把这个迭代器称为seq(symbols,L)。然后,例如, 列表(seq([1,2,3],2))=[] 列表(seq([1,2,3],3))=[(1,2,3)、(1,3,2)、(2,1,3)、(2,3,1)、(3,1,2)、(3,2,1)] 列表(seq([1,2,3],4))=[(1,1,2,3),(1,1,3,2),(

在python中执行以下操作的有效方法是什么?
给定N个符号,迭代N个符号的所有L长度序列,包括所有N个符号。

顺序并不重要,只要覆盖了所有序列,并且每个序列只覆盖一次

让我们把这个迭代器称为seq(symbols,L)。然后,例如,
列表(seq([1,2,3],2))=[]
列表(seq([1,2,3],3))=[(1,2,3)、(1,3,2)、(2,1,3)、(2,3,1)、(3,1,2)、(3,2,1)]
列表(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个符号,似乎更好的解决方案应该从置换的解决方案开始,然后以某种方式添加额外的重复符号,但我不知道如何在不重复计数的情况下做到这一点(并且不必保存所有以前的输出以检查重复)。

一个想法:

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)

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

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

  • 对于每个多重集,查找所有唯一排列

  • 为简单起见,假设N个符号是
    范围(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
    

    <> >不幸,ItTrimes.Exchange < /C>不产生具有重复元素的列表的唯一迭代。如果我们在C++中编写这个,我们可以使用它,它会产生独特的迭代。有一个示例实现(C++中,但它很简单地将其转换为Python)。在链接页面上。

    这就是你想要的吗?谢谢你关于位掩码的提示,因为我可能会在某个时候用C写这篇文章(我喜欢用python玩算法,因为在那里更容易思考)。不过,我不相信复杂性注释,因为这仍然是“检查”很多序列只是为了把它们扔掉。例如,它会尝试从(1,1,1,1)开始,然后意识到这不起作用,然后它会拒绝(1,1,1,2,1),然后拒绝(1,1,1,2,2),等等。所以它确实比我最初的慢解决方案修剪得更好,但它仍然感觉应该有一个利用itertools的解决方案。排列(符号)@EdBrown:当然,它只执行一个无效的递归,只会在
    if size
    行中立即进行删减。这仍然是
    O(1)
    每个转换;)不完全是O(1),因为构建该集可能更昂贵。@user2357112:是的,当然,但您可以使用位掩码来代替
    O(1)
    至少为合理的符号集大小设置操作(我在这里假设
    符号集足够小,算法可以在合理的时间内完成)。元组前缀从未实际生成,尽管我可能不理解。假设我们的大小为1,todo=(3,)。这将尝试所有N个符号,以发现只有3个有效。对我来说,这听起来不像O(1)。这听起来像O(N)。因此,我将把复杂性放在O(N^N),类似于我的示例解决方案。抱歉,如果我理解缓慢,我缺少了什么?我可能缺少了一些东西,但为什么
    len(list(msets(5,3)))=6
    ?@Niklas:你认为它会是什么?有六种可能的长度为5的元素的多集(0,1,2)这里至少有一个问题,但是C++代码对于一个算法问题来说是足够清楚的,但是我可以在上午写Bython等价物,BTW,你的函数本质上是代码>(元组(xLangar(n)))itertools中y的+y。带替换的组合(X范围(N),L-N))
    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