【问题标题】:Python: Produce all possible sequence combination from a list with character limitPython:从具有字符限制的列表中生成所有可能的序列组合
【发布时间】:2018-04-08 09:27:27
【问题描述】:

我的问题和this question一模一样。我有字符数组(列表)。我想从该列表中获取所有可能的序列组合,但 字符限制(例如:最多 2 个字符)。此外,排列行中不能重复单个字符:

chars = ['a', 'b', 'c', 'd']

# output
output = [['a', 'b', 'c', 'd'],
          ['ab', 'c', 'd'],
          ['a', 'bc', 'd'],
          ['a', 'b', 'cd'],
          ['ab', 'cd'],
          ['abc', 'd'], # this one will be exempted
          ['a', 'bcd'],  # this one will be exempted
          ['abcd']]  # this one will be exempted

我知道我可以在生成和构建序列时检查条件以省略超限字符组合。但它会增加运行时间。我的目的是减少现有的执行时间。

没有字符数限制,组合将像 2^(N-1) 一样生成。如果列表超过 15 个字符,则执行程序需要很长时间。因此,我想按字符限制减少组合计数。

优先级是性能。我已经研究并尝试了两天,没有任何成功。

【问题讨论】:

  • 你没有尝试显示?
  • @usr2564301,确实是的。我已经使用修改后的版本尝试了上述重复问题的解决方案。我什至尝试了 Python itertools.combination 源代码并稍作修改。但所有的尝试甚至都没有达到预期。真的很抱歉我的无能。
  • 尝试其他答案并出于某种原因拒绝它们并不是无能,但我怀疑您的结论。这是您可以添加的内容:上一个问题有 4 个答案(但可能有些相同)。由于您担心性能,请添加这些答案的时间并指出指数问题。
  • @usr2564301,我还在努力解决,也会尝试你的建议。谢谢。

标签: python combinations permutation


【解决方案1】:

一种方法是遍历输入列表并逐渐建立组合。在每一步中,从输入列表中取出下一个字符并添加到之前生成的组合中。

from collections import defaultdict

def make_combinations(seq, maxlen):
    # memo is a dict of {length_of_last_word: list_of_combinations}
    memo = defaultdict(list)
    memo[1] = [[seq[0]]]  # put the first character into the memo

    seq_iter = iter(seq)
    next(seq_iter)  # skip the first character
    for char in seq_iter:
        new_memo = defaultdict(list)

        # iterate over the memo and expand it
        for wordlen, combos in memo.items():
            # add the current character as a separate word
            new_memo[1].extend(combo + [char] for combo in combos)

            # if the maximum word length isn't reached yet, add a character to the last word
            if wordlen < maxlen:
                word = combos[0][-1] + char

                new_memo[wordlen+1] = newcombos = []
                for combo in combos:
                    combo[-1] = word  # overwrite the last word with a longer one
                    newcombos.append(combo)

        memo = new_memo

    # flatten the memo into a list and return it
    return [combo for combos in memo.values() for combo in combos]

输出:

[['a', 'b', 'c', 'd'], ['ab', 'c', 'd'], ['a', 'bc', 'd'],
 ['a', 'b', 'cd'], ['ab', 'cd']]

这种实现比用于短输入的幼稚 itertools.product 方法慢:

input: a b c d
maxlen: 2
iterations: 10000

itertools.product: 0.11653625800136069 seconds
make_combinations: 0.16573870600041118 seconds

但当输入列表较长时,它会迅速恢复:

input: a b c d e f g h i j k
maxlen: 2
iterations: 10000

itertools.product: 6.9087735799985240 seconds
make_combinations: 1.2037671390007745 seconds

【讨论】:

    【解决方案2】:

    一般来说,更容易产生一个大的组合/排列列表,然后过滤结果以实现所需的输出。您可以使用递归生成器函数来获取组合,然后过滤并加入结果:

    chars = ['a', 'b', 'c', 'd']
    def get_combos(c):
      if len(c) == 1:
        yield c
      else:
         yield c
         for i in range(len(c)-1):
           yield from get_combos([c[d]+c[d+1] if d == i else c[d] if d < i else c[d+1] for d in range(len(c)-1)])
    
    final_listing = list(get_combos(chars))
    last_results = list(filter(lambda x:all(len(c) < 3 for c in x), [a for i, a in enumerate(final_listing) if a not in final_listing[:i]]))
    

    输出:

    [['a', 'b', 'c', 'd'], ['ab', 'c', 'd'], ['ab', 'cd'], ['a', 'bc', 'd'], ['a', 'b', 'cd']]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多