【问题标题】:Create possible combinations of specific size创建特定尺寸的可能组合
【发布时间】:2016-02-12 21:38:31
【问题描述】:

字符串/列表是例如'foobar'。我需要在组数为 n 的所有可能组合中分解它,例如3.

这会给我例如

['foo', 'ba',  'r']
['f',  'ooba', 'r']
['fo', 'oo',   'bar']
['f',  'o',    'obar']

创建所有可能组合的最佳算法是什么?

【问题讨论】:

  • 提示:考虑组之间的分隔符。负空间。

标签: python algorithm


【解决方案1】:

听起来像是itertools 的工作:

from itertools import combinations

def compositions(s,k):
    n = len(s)
    for c in combinations(range(1,n),k-1):
        yield [s[i:j] for i,j in zip((0,)+c,c+(n,))]

它的工作方式是combinations 部分产生由可能的切割点组成的元组。例如(使用s = "foobar"k = 3(2,4) 是元组之一。打破这些索引应该产生["fo","ob","ar"],它对应于[s[0:2],s[2,4],s[4,6]],在这种情况下,表达式zip((0,)+c,c+(n,))zip((0,2,4),(2,4,6))相同,因此迭代它具有迭代连续切片的连续索引对的效果.

例如,

>>> for c in compositions("foobar",3): print(c)

['f', 'o', 'obar']
['f', 'oo', 'bar']
['f', 'oob', 'ar']
['f', 'ooba', 'r']
['fo', 'o', 'bar']
['fo', 'ob', 'ar']
['fo', 'oba', 'r']
['foo', 'b', 'ar']
['foo', 'ba', 'r']
['foob', 'a', 'r']

我选择了“组合”这个名称,因为您本质上是在谈论组合数学中的compositions。我的算法基于链接文章中的证明,即 n 项组成 k 件的数量为C(n-1,k-1)

【讨论】:

  • combos.append 替换为yield,这会变得很棒。您可以稍微解释一下zip((0,)+c,c+(n,)) 部分。
  • @arekolek 好主意,绝对更符合 itertools 的精神。谢谢。
  • 太棒了!此外,您忘记了您不再需要的 combos = [] 行。
  • @arekolek - 已修复,添加了解释
【解决方案2】:

这是一个简单的递归

def split(s, n):
    def rec_split(i, s, n):
        ans = []

        if n == 1:
            ans.append([s[i:]])
            return ans

        for j in range(i+1, len(s)):
            head = s[i:j]
            tails = rec_split(j, s, n-1)
            for tail in tails:
                ans.append([head] + tail)
        return ans

    return rec_split(0, s, n)

for e in split("foobar", 3):
    print(e)

# ['f', 'o', 'obar']
# ['f', 'oo', 'bar']
# ['f', 'oob', 'ar']
# ['f', 'ooba', 'r']
# ['fo', 'o', 'bar']
# ['fo', 'ob', 'ar']
# ['fo', 'oba', 'r']
# ['foo', 'b', 'ar']
# ['foo', 'ba', 'r']
# ['foob', 'a', 'r']

rec_split 返回n 部分中s 的所有分区,从i-th 索引开始

【讨论】:

    【解决方案3】:

    您可以为此使用backtracking。只要您将单词分成三个以上的部分,就会生成所有可能的拆分和回溯。

    【讨论】:

    • 如果您提供算法的更多细节,这可能是一个有价值的答案。
    【解决方案4】:

    您可以使用生成器递归地解决这个问题:

    def sections(s, n):
        if n == 1:
            yield [s]
    
        if n > 1:
            for i in range(1, len(s)):
                for tail in section(s[i:], n - 1):
                    yield [s[0:i]] + tail
    

    并像这样使用它:

    for s in sections("foobar", 3):
        print s
    

    【讨论】:

    • 糟糕,功能相同。发帖前改了名字,当然,我不应该这样做。编辑了帖子。
    【解决方案5】:

    看看Word break 算法,这是它的一个变体,有一个显着的区别是单词不是来自预定义的字典,而是在最终集合中需要有固定数量的单词。

    主要思想是从头开始迭代您的输入,对其进行切片并递归处理正确的部分。

    假设函数带有原型

    def rec(input, n):
    

    这是一个伪代码:

    if n == 1:
        final_set.append([input[i:]])
    else:
        for i in range (0, len(input) - n + 1):
           for rec_set in rec(input[i:], n - 1):
               final_set.append(merge(input[:i], rec_set))
    

    使用您的示例,我们有:

    rec('foobar', 3)
    = {['f', rec('oobar', 2)], ['fo', rec('obar', 2)], ['foo', rec('bar', 2)], ['foob', rec('ar', 2)]}
    
    = {['f','o', rec('obar', 1)], ['f','oo', rec('bar', 1)], ['f','oob', rec('ar', 1)], ['f','ooba', rec('r', 1)], ['fo', 'o', rec('bar', 1)], ['fo', 'ob', rec('ar', 1)], ['fo', 'oba', rec('r', 1)], ['foo', 'b', rec('ar', 1)], ['foo', 'ba', rec('r', 1)], ['foob', 'a', rec('r', 1)]}
    
    = {['f','o', 'obar'], ['f','oo', 'bar'], ['f','oob', 'ar'], ['f','ooba', 'r'], ['fo', 'o', 'bar'], ['fo', 'ob', 'ar'], ['fo', 'oba', 'r',], ['foo', 'b', 'ar'], ['foo', 'ba', 'r'], ['foob', 'a', 'r']}
    

    要记住的重要一点是缓存部分结果,以避免一遍又一遍地重复做同样的工作。例如,如果您已经计算了rec('oobar', 2),则将结果存储在某个缓存中(例如字典适用于此)并在函数的开头检查您是否已经计算了指定输入字符串和n 的所有可能组合。它将时间复杂度从指数降低到多项式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多