【问题标题】:How can I find all the subsets of a set, with exactly n elements?我怎样才能找到一个集合的所有子集,正好有 n 个元素?
【发布时间】:2008-12-17 14:09:18
【问题描述】:

我正在用 Python 编写一个程序,我意识到我需要解决的问题需要我,给定一组 Sn 元素 (|S|=n),在所有可能的情况下测试一个函数特定顺序m 的子集(即具有 m 个元素)。用答案产生部分解,然后用下一个顺序 m=m+1 再试一次,直到 m=n。

我正在写形式的解决方案:

def findsubsets(S, m):
    subsets = set([])
    ...
    return subsets

但我知道 Python 我希望解决方案已经存在。

最好的方法是什么?

【问题讨论】:

  • scipy.misc.comb(S, m) 给出了您将获得的子集数量。您最终应该在执行代码之前进行检查,因为 S 的 m 大小子集的数量会很快变得非常大。
  • 确实遇到了同样的问题,开始自己编写代码,然后意识到必须有一个 Python 库来解决这个问题!

标签: python


【解决方案1】:

itertools.combinations 是你的朋友,如果你有 Python 2.6 或更高版本。否则,请检查链接以获取等效功能的实现。

import itertools
def findsubsets(S,m):
    return set(itertools.combinations(S, m))

S:要为其查找子集的集合
m:子集中的元素个数

【讨论】:

  • 我不会返回一个集合,而是简单地返回迭代器(或者只使用组合()而不定义 findsubsets()...)
  • @hop OP 特别要求集合。省略 set cast 允许以不同的顺序重复,例如:(1,2,3), (2,3,1), (3,1,2)...
  • @JamesBradbury:对不起,我不明白你的意思。您是否将其与排列混淆了?
【解决方案2】:

使用规范函数从the itertools recipe页面获取powerset

from itertools import chain, combinations

def powerset(iterable):
    """
    powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)
    """
    xs = list(iterable)
    # note we return an iterator rather than a list
    return chain.from_iterable(combinations(xs,n) for n in range(len(xs)+1))

像这样使用:

>>> list(powerset("abc"))
[(), ('a',), ('b',), ('c',), ('a', 'b'), ('a', 'c'), ('b', 'c'), ('a', 'b', 'c')]

>>> list(powerset(set([1,2,3])))
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]

根据需要映射到集合,以便可以使用联合、交集等...:

>>> map(set, powerset(set([1,2,3])))
[set([]), set([1]), set([2]), set([3]), set([1, 2]), set([1, 3]), set([2, 3]), set([1, 2, 3])]

>>> reduce(lambda x,y: x.union(y), map(set, powerset(set([1,2,3]))))
set([1, 2, 3])

【讨论】:

    【解决方案3】:

    这是一个函数,它为您提供整数 [0..n] 的所有子集,而不仅仅是给定长度的子集:

    from itertools import combinations, chain
    
    def allsubsets(n):
        return list(chain(*[combinations(range(n), ni) for ni in range(n+1)]))
    

    例如

    >>> allsubsets(3)
    [(), (0,), (1,), (2,), (0, 1), (0, 2), (1, 2), (0, 1, 2)]
    

    【讨论】:

    • 有用的公式,但使用chain.from_iterable 而不是扩展一个可能很长的集合。将组合迭代成一个列表 ([ ... ])、星形扩展、链接成一个迭代器 (chain),然后再次变成一个列表有什么意义? PS。更好的配方在itertools 文档中,在另一个(稍后)答案中。
    • 命名 lambda 是不好的做法。请改用def
    • @wjandrea 它是单线的。
    • @Kowalski 你是什么意思?
    • @Kowalski 是的,当然是单线。什么意思?
    【解决方案4】:

    这是一种简单易懂的算法。

    import copy
    
    nums = [2,3,4,5]
    subsets = [[]]
    
    for n in nums:
        prev = copy.deepcopy(subsets)
        [k.append(n) for k in subsets]
        subsets.extend(prev)
    
    print(subsets) 
    print(len(subsets))
    
    # [[2, 3, 4, 5], [3, 4, 5], [2, 4, 5], [4, 5], [2, 3, 5], [3, 5], [2, 5], [5], 
    # [2, 3, 4], [3, 4], [2, 4], [4], [2, 3], [3], [2], []]
    
    # 16 (2^len(nums))
    
    

    【讨论】:

    • 应该是k.append(n) 而不是k.extend(n)
    【解决方案5】:

    这里有一些伪代码 - 您可以通过在执行过程中存储每个调用的值以及在递归调用检查调用值是否已经存在之前来减少相同的递归调用。

    以下算法将包含除空集之外的所有子集。

    list * subsets(string s, list * v) {
    
        if(s.length() == 1) {
            list.add(s);    
            return v;
        }
        else
        {
            list * temp = subsets(s[1 to length-1], v);
            int length = temp->size();
    
            for(int i=0;i<length;i++) {
                temp.add(s[0]+temp[i]);
            }
    
            list.add(s[0]);
            return temp;
        }
    }
    

    所以,例如如果 s = "123" 那么输出是:

    1
    2
    3
    12
    13
    23
    123
    

    【讨论】:

      【解决方案6】:

      不使用itertools

      在 Python 3 中,您可以使用 yield from 将子集生成器方法添加到内置 set 类:

      class SetWithSubset(set):
          def subsets(self):
              s1 = []
              s2 = list(self)
      
              def recfunc(i=0):            
                  if i == len(s2):
                      yield frozenset(s1)
                  else:                
                      yield from recfunc(i + 1)
                      s1.append(s2[ i ])
                      yield from recfunc(i + 1)
                      s1.pop()
      
              yield from recfunc()
      

      例如下面的 sn-p 按预期工作:

      x = SetWithSubset({1,2,3,5,6})
      {2,3} in x.subsets()            # True
      set() in x.subsets()            # True
      x in x.subsets()                # True
      x|{7} in x.subsets()            # False
      set([5,3]) in x.subsets()       # True - better alternative: set([5,3]) < x
      len(x.subsets())                # 32
      

      【讨论】:

      • yield from 的大量使用和算法解释
      【解决方案7】:

      $ python -c "import itertools; a=[2,3,5,7,11]; print sum([list(itertools.combinations(a, i)) for i in range(len(a)+1)], [])" [(), (2,), (3,), (5,), (7,), (11,), (2, 3), (2, 5), (2, 7), (2, 11), (3, 5), (3, 7), (3, 11), (5, 7), (5, 11), (7, 11), (2, 3, 5), (2, 3, 7), (2, 3, 11), (2, 5, 7), (2, 5, 11), (2, 7, 11), (3, 5, 7), (3, 5, 11), (3, 7, 11), (5, 7, 11), (2, 3, 5, 7), (2, 3, 5, 11), (2, 3, 7, 11), (2, 5, 7, 11), (3, 5, 7, 11), (2, 3, 5, 7, 11)]

      【讨论】:

        【解决方案8】:
        >>>Set = ["A", "B","C","D"]
        >>>n = 2
        >>>Subsets=[[i for i,s in zip(Set, status) if int(s)  ] for status in [(format(bit,'b').zfill(len(Set))) for bit in range(2**len(Set))] if sum(map(int,status)) == n]
        >>>Subsets
        [['C', 'D'], ['B', 'D'], ['B', 'C'], ['A', 'D'], ['A', 'C'], ['A', 'B']]
        

        【讨论】:

        • 请解释为什么这样做会按照要求进行。
        • 试一试,你会发现它是否能满足要求
        【解决方案9】:

        使用递归的另一种解决方案:

        def subsets(nums: List[int]) -> List[List[int]]:
            n = len(nums)
            output = [[]]
            
            for num in nums:
                output += [curr + [num] for curr in output]
            
            return output
        

        从输出列表中的空子集开始。在每一步,我们都会考虑一个新整数,并从现有整数中生成新的子集。

        【讨论】:

          【解决方案10】:

          这是一个使用第一原理的简单递归的实现。 基本情况:如果没有元素,则返回空集。 递归案例:选择一个元素并返回其他元素的所有子集 添加和不添加所选元素。

          def _all_subsets(s):
              seq = list(s)
              if not seq:
                  yield set([])
              else:
                  choice = set([seq[0]])
                  others = seq[1:]
                  for without_choice in _all_subsets(others):
                      yield without_choice
                      yield choice | without_choice
          
          def all_subsets(iterable):
              s = set(iterable)
              for x in _all_subsets(s):
                  yield x
          

          【讨论】:

            猜你喜欢
            • 2021-06-29
            • 1970-01-01
            • 1970-01-01
            • 2023-03-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-10-31
            相关资源
            最近更新 更多