如何获取集合的所有子集? (动力装置)

Posted

技术标签:

【中文标题】如何获取集合的所有子集? (动力装置)【英文标题】:How to get all subsets of a set? (powerset) 【发布时间】:2010-12-01 17:22:59 【问题描述】:

给定一个集合

0, 1, 2, 3

如何生成子集:

[set(),
 0,
 1,
 2,
 3,
 0, 1,
 0, 2,
 0, 3,
 1, 2,
 1, 3,
 2, 3,
 0, 1, 2,
 0, 1, 3,
 0, 2, 3,
 1, 2, 3,
 0, 1, 2, 3]

【问题讨论】:

powerset有哪些应用? @X10D 很多。例如:scholar.google.com/… 那里的大多数结果似乎与幂集无关。你能列出一些应用程序吗? @X10D 对于基于约束的因果发现算法,需要通过调节所涉及变量的所有可能子集来测试条件独立性,在计算布尔函数的傅里叶级数时,我也遇到过需要幂集。这显然是冰山一角 【参考方案1】:

Python itertools page 正好有一个 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)"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

输出:

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

如果您不喜欢开头的那个空元组,您可以将range 语句更改为range(1, len(s)+1) 以避免0 长度组合。

【讨论】:

这是我能找到的最快答案,将此页面上的其他一些解决方案与使用 Python 的 timeit 模块的这个解决方案进行比较。但是,在某些情况下,如果您需要修改结果输出(例如将字母连接成字符串),使用生成器编写自定义配方并构建您想要的输出(例如将两个字符串相加)会快得多。跨度> 为什么需要s = list(iterable) @JackStevens 因为可迭代对象不可回退,也不需要实现__len__;试试powerset((n for n in range(3))) 不换行。 对于大字符串,这会占用大量内存! @AlexandreHuat:范围是惰性序列,而不是迭代器。 powerset(range(3)) 可以正常工作 even without s = list(iterable)【参考方案2】:

这里有更多关于 powerset 的代码。这是从头开始写的:

>>> def powerset(s):
...     x = len(s)
...     for i in range(1 << x):
...         print [s[j] for j in range(x) if (i & (1 << j))]
...
>>> powerset([4,5,6])
[]
[4]
[5]
[4, 5]
[6]
[4, 6]
[5, 6]
[4, 5, 6]

Mark Rushakoff 的评论适用于此处:“如果您不喜欢开头的那个空元组,请打开。”您可以将 range 语句更改为 range(1, len(s)+1) 以避免 0 -length 组合”,除非在我的情况下您将 for i in range(1 &lt;&lt; x) 更改为 for i in range(1, 1 &lt;&lt; x)


多年后回到现在,我现在会这样写:

def powerset(s):
    x = len(s)
    masks = [1 << i for i in range(x)]
    for i in range(1 << x):
        yield [ss for mask, ss in zip(masks, s) if i & mask]

然后测试代码如下所示:

print(list(powerset([4, 5, 6])))

使用yield 意味着您不需要在一块内存中计算所有结果。在主循环之外预先计算掩码被认为是值得优化的。

【讨论】:

这是一个有创意的答案。但是,我使用 timeit 对其进行了测量,并将其与 Mark Rushakoff 进行了比较,发现它明显变慢了。要生成 16 个项目的幂集 100 次,我的测量结果是 0.55 对 15.6。 你如何处理重复的? python 中的任何重复问题都可以使用 set() 来解决。 @CeasarBautista 您无法将用户功能与内置功能进行比较。内置函数总是尽可能优化【参考方案3】:

如果你正在寻找一个快速的答案,我刚刚在谷歌上搜索了“python power set”并想出了这个:Python Power Set Generator

这是该页面中代码的复制粘贴:

def powerset(seq):
    """
    Returns all the subsets of this set. This is a generator.
    """
    if len(seq) <= 1:
        yield seq
        yield []
    else:
        for item in powerset(seq[1:]):
            yield [seq[0]]+item
            yield item

可以这样使用:

 l = [1, 2, 3, 4]
 r = [x for x in powerset(l)]

现在 r 是您想要的所有元素的列表,可以排序和打印:

r.sort()
print r
[[], [1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 4], [1, 3], [1, 3, 4], [1, 4], [2], [2, 3], [2, 3, 4], [2, 4], [3], [3, 4], [4]]

【讨论】:

如果输入的是空数组,上面的代码会返回[[][]],为了解决这个问题,只需将大小写分开检查if len(seq) == 0: yield [] elif len(seq) == 1: yield seq yield [] 作为参考,我使用 timeit 测量了这个(通过 Ayush 的编辑),并将其与 Mark Rushakoff 回答中的 powerset 配方进行了比较。在我的机器上,要生成 16 个项目的 powerset 100 次,这个算法需要 1.36 秒,而 Rushakoff 需要 0.55 秒。 这个的时间复杂度是多少? @CodeQuestor 我评估了复制粘贴部分的时间复杂度。对我来说,感觉就像 O(n^2)。 for 循环贡献 1 n,递归调用贡献 n-1。所以,总的来说它变成了O(n ^ 2)。除此之外,如果我们考虑调用 powerset(l) 的外部循环,另一个 n 与之前的结果相乘,使其成为 O(n^3)。我是这方面的初学者和学生。因此,如果我的观点有误,请纠正我。保持安全。【参考方案4】:
def powerset(lst):
    return reduce(lambda result, x: result + [subset + [x] for subset in result],
                  lst, [[]])

【讨论】:

【参考方案5】:

我只是想提供最通俗易懂的解决方案,即anti code-golf版本。

from itertools import combinations

l = ["x", "y", "z", ]

def powerset(items):
    combo = []
    for r in range(len(items) + 1):
        #use a list to coerce a actual list from the combinations generator
        combo.append(list(combinations(items,r)))
    return combo

l_powerset = powerset(l)

for i, item in enumerate(l_powerset):
    print "All sets of length ", i
    print item

结果

所有长度为0的集合

[()]

长度为1的所有集合

[('x',), ('y',), ('z',)]

所有长度为2的集合

[('x', 'y'), ('x', 'z'), ('y', 'z')]

长度为3的所有集合

[('x', 'y', 'z')]

更多see the itertools docs,还有***条目power sets

【讨论】:

【参考方案6】:

这很疯狂,因为这些答案都没有真正提供实际 Python 集的返回。这是一个凌乱的实现,它将提供一个实际上是 Python set 的 powerset。

test_set = set(['yo', 'whatup', 'money'])
def powerset( base_set ):
    """ modified from pydoc's itertools recipe shown above"""
    from itertools import chain, combinations
    base_list = list( base_set )
    combo_list = [ combinations(base_list, r) for r in range(len(base_set)+1) ]

    powerset = set([])
    for ll in combo_list:
        list_of_frozensets = list( map( frozenset, map( list, ll ) ) ) 
        set_of_frozensets = set( list_of_frozensets )
        powerset = powerset.union( set_of_frozensets )

    return powerset

print powerset( test_set )
# >>> set([ frozenset(['money','whatup']), frozenset(['money','whatup','yo']), 
#        frozenset(['whatup']), frozenset(['whatup','yo']), frozenset(['yo']),
#        frozenset(['money','yo']), frozenset(['money']), frozenset([]) ])

不过,我希望看到更好的实现。

【讨论】:

好点,但是 OP 想要一个集合列表作为输出,所以(在 ​​Python 3 中)你可以做 [*map(set, chain.from_iterable(combinations(s, r) for r in range(len(s)+1)))];如果您愿意,map 的函数 arg 可以是 frozenset【参考方案7】:
def get_power_set(s):
  power_set=[[]]
  for elem in s:
    # iterate over the sub sets so far
    for sub_set in power_set:
      # add a new subset consisting of the subset at hand added elem
      power_set=power_set+[list(sub_set)+[elem]]
  return power_set

例如:

get_power_set([1,2,3])

产量

[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

【讨论】:

在其管理的循环中修改循环变量 (power_set) 是一种非常值得怀疑的做法。例如,假设您编写了以下代码而不是建议的变量修改代码:power_set += [list(sub_set)+[elem]]。然后循环不会终止。【参考方案8】:

这是我使用组合但仅使用内置插件的快速实现。

def powerSet(array):
    length = str(len(array))
    formatter = ':0' + length + 'b'
    combinations = []
    for i in xrange(2**int(length)):
        combinations.append(formatter.format(i))
    sets = set()
    currentSet = []
    for combo in combinations:
        for i,val in enumerate(combo):
            if val=='1':
                currentSet.append(array[i])
        sets.add(tuple(sorted(currentSet)))
        currentSet = []
    return sets

【讨论】:

【参考方案9】:

powerset 有一个细化:

def powerset(seq):
    """
    Returns all the subsets of this set. This is a generator.
    """
    if len(seq) <= 0:
        yield []
    else:
        for item in powerset(seq[1:]):
            yield [seq[0]]+item
            yield item

【讨论】:

【参考方案10】:

我发现以下算法非常清晰和简单:

def get_powerset(some_list):
    """Returns all subsets of size 0 - len(some_list) for some_list"""
    if len(some_list) == 0:
        return [[]]

    subsets = []
    first_element = some_list[0]
    remaining_list = some_list[1:]
    # Strategy: get all the subsets of remaining_list. For each
    # of those subsets, a full subset list will contain both
    # the original subset as well as a version of the subset
    # that contains first_element
    for partial_subset in get_powerset(remaining_list):
        subsets.append(partial_subset)
        subsets.append(partial_subset[:] + [first_element])

    return subsets

另一种生成幂集的方法是生成所有具有n 位的二进制数。作为幂集,n 数字的数量是2 ^ n。该算法的原理是一个元素可以存在或不存在于子集中,因为二进制数字可以是一或零,但不能同时是两者。

def power_set(items):
    N = len(items)
    # enumerate the 2 ** N possible combinations
    for i in range(2 ** N):
        combo = []
        for j in range(N):
            # test bit jth of integer i
            if (i >> j) % 2 == 1:
                combo.append(items[j])
        yield combo

我在学习 MITx: 6.00.2x Introduction to Computational Thinking and Data Science 时发现了这两种算法,我认为这是我见过的最容易理解的算法之一。

【讨论】:

【参考方案11】:

只是一个快速的电源组复习!

集合 X 的幂集,就是 X 的所有子集的集合,包括 空集

示例集 X = (a,b,c)

幂集 = a , b , c , a , b , a , c , b , c , a , b , c ,

这是另一种求幂集的方法:

def power_set(input):
    # returns a list of all subsets of the list a
    if (len(input) == 0):
        return [[]]
    else:
        main_subset = [ ]
        for small_subset in power_set(input[1:]):
            main_subset += [small_subset]
            main_subset += [[input[0]] + small_subset]
        return main_subset

print(power_set([0,1,2,3]))

感谢source

【讨论】:

【参考方案12】:

一种简单的方法是利用 2 的补码算法下的整数的内部表示。

整数的二进制表示形式为 000, 001, 010, 011, 100, 101, 110, 111 对于 0 到 7 的数字。对于整数计数器值,将 1 视为包含相应元素的集合和'0' 作为排除,我们可以根据计数序列生成子集。必须从0pow(2,n) -1 生成数字,其中 n 是数组的长度,即二进制表示的位数。

一个简单的基于它的子集生成函数可以写成如下。它基本上依赖于

def subsets(array):
    if not array:
        return
    else:
        length = len(array)
        for max_int in range(0x1 << length):
            subset = []
            for i in range(length):
                if max_int & (0x1 << i):
                    subset.append(array[i])
            yield subset

然后就可以当做

def get_subsets(array):
    powerset = []
    for i in subsets(array):
        powerser.append(i)
    return powerset

测试

在本地文件中添加以下内容

if __name__ == '__main__':
    sample = ['b',  'd',  'f']

    for i in range(len(sample)):
        print "Subsets for " , sample[i:], " are ", get_subsets(sample[i:])

给出以下输出

Subsets for  ['b', 'd', 'f']  are  [[], ['b'], ['d'], ['b', 'd'], ['f'], ['b', 'f'], ['d', 'f'], ['b', 'd', 'f']]
Subsets for  ['d', 'f']  are  [[], ['d'], ['f'], ['d', 'f']]
Subsets for  ['f']  are  [[], ['f']]

【讨论】:

这在可维护性或可读性方面可能不实用,但它让我大吃一惊。感谢分享,聪明的解决方案!【参考方案13】:

TL;DR(直接转到简化)

我知道我之前添加了一个答案,但我真的很喜欢我的新实现。我将一个集合作为输入,但它实际上可以是任何可迭代的,并且我正在返回一组集合,它是输入的幂集。我喜欢这种方法,因为它更符合power set 的数学定义(所有子集的集合)。

def power_set(A):
    """A is an iterable (list, tuple, set, str, etc)
    returns a set which is the power set of A."""
    length = len(A)
    l = [a for a in A]
    ps = set()

    for i in range(2 ** length):
        selector = f'i:0lengthb'
        subset = l[j] for j, bit in enumerate(selector) if bit == '1'
        ps.add(frozenset(subset))

    return ps

如果您想要您在答案中发布的确切输出,请使用:

>>> [set(s) for s in power_set(1, 2, 3, 4)]
[3, 4,
 2,
 1, 4,
 2, 3, 4,
 2, 3,
 1, 2, 4,
 1, 2,
 1, 2, 3,
 3,
 2, 4,
 1,
 1, 2, 3, 4,
 set(),
 1, 3,
 1, 3, 4,
 4]

说明

已知幂集的元素个数为2 ** len(A),因此在for循环中可以清楚地看到。

我需要将输入(最好是集合)转换为列表,因为集合是唯一无序元素的数据结构,并且顺序对于生成子集至关重要。

selector 是该算法的关键。请注意,selector 与输入集的长度相同,为了实现这一点,它使用了带有填充的 f 字符串。基本上,这允许我选择将在每次迭代期间添加到每个子集的元素。假设输入集有 3 个元素 0, 1, 2,因此选择器将采用 0 到 7(含)之间的值,二进制是:

000 # 0
001 # 1
010 # 2
011 # 3
100 # 4
101 # 5
110 # 6
111 # 7

因此,每个位都可以作为是否应添加原始集合的元素的指示符。查看二进制数,并将每个数字视为超集的一个元素,其中1 表示应添加索引j 处的元素,0 表示不应添加此元素。

我使用集合推导在每次迭代中生成一个子集,并将该子集转换为 frozenset,以便将其添加到 ps(幂集)。否则,我将无法添加它,因为 Python 中的集合仅包含不可变对象。

简化

您可以使用一些 python 推导来简化代码,这样您就可以摆脱那些 for 循环。您也可以使用zip 来避免使用j 索引,代码将如下所示:

def power_set(A):
    length = len(A)
    return 
        frozenset(e for e, b in zip(A, f'i:lengthb') if b == '1')
        for i in range(2 ** length)
    

就是这样。我喜欢这个算法的地方是它比其他算法更清晰、更直观,因为即使它按预期工作,但依赖 itertools 看起来很神奇。

【讨论】:

这与上一个答案***.com/a/1482320/4434666中的想法基本相同【参考方案14】:

对于作为所有子集的一部分的空集,您可以使用:

def subsets(iterable):
    for n in range(len(iterable) + 1):
        yield from combinations(iterable, n)

【讨论】:

【参考方案15】:

几乎所有这些答案都使用list 而不是set,这对我来说有点作弊。所以,出于好奇,我尝试在set 上真正做一个简单的版本,并为其他“Python 新手”做总结。

我发现在处理 Python 的 set implementation 时有一些奇怪之处。对我来说主要的惊喜是处理空集。这与 Ruby 的Set implementation 形成对比,我可以简单地执行Set[Set[]] 并获得一个包含一个空SetSet,所以我最初发现它有点混乱。

回顾一下,在做powersetsets的过程中,遇到了两个问题:

    set() 需要一个可迭代对象,所以 set(set()) 将返回 set() because the empty set iterable is empty(我猜是 :)) 要获得一组集合,set(set())set.add(set) 将不起作用,因为 set() isn't hashable

为了解决这两个问题,我使用了frozenset(),这意味着我并没有完全得到我想要的(类型字面意思是set),但使用了整个set 交互。

def powerset(original_set):
  # below gives us a set with one empty set in it
  ps = set(frozenset()) 
  for member in original_set:
    subset = set()
    for m in ps:
      # to be added into subset, needs to be
      # frozenset.union(set) so it's hashable
      subset.add(m.union(set([member]))
    ps = ps.union(subset)
  return ps

下面我们正确地得到 2² (16) frozensets 作为输出:

In [1]: powerset(set([1,2,3,4]))
Out[2]:
frozenset(),
 frozenset(3, 4),
 frozenset(2),
 frozenset(1, 4),
 frozenset(3),
 frozenset(2, 3),
 frozenset(2, 3, 4),
 frozenset(1, 2),
 frozenset(2, 4),
 frozenset(1),
 frozenset(1, 2, 4),
 frozenset(1, 3),
 frozenset(1, 2, 3),
 frozenset(4),
 frozenset(1, 3, 4),
 frozenset(1, 2, 3, 4)

由于在 Python 中无法拥有 sets 中的 set,因此如果要将这些 frozensets 转换为 sets,则必须将它们映射回 list (list(map(set, powerset(set([1,2,3,4])))) ) 或修改上述内容。

【讨论】:

【参考方案16】:

也许这个问题已经过时了,但我希望我的代码能对某人有所帮助。

def powSet(set):
    if len(set) == 0:
       return [[]]
    return addtoAll(set[0],powSet(set[1:])) + powSet(set[1:])

def addtoAll(e, set):
   for c in set:
       c.append(e)
   return set

【讨论】:

哇,递归! =) 可能不是最有效的方式,但看到递归方式总是很有趣!【参考方案17】:

使用包more_itertools中的函数powerset()

产生所有可能的迭代子集

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

如果你想要集合,使用:

list(map(set, powerset(iterable)))

【讨论】:

这么多人在这里重新发明***,恕我直言,这是最好的答案,因为它可能已经存在于您的依赖项中,因为许多常见库都需要它,例如pytest。 libraries.io/pypi/more-itertools/dependents 为三行代码引入依赖项并不总是正确的选择。 1) 三线?您是否引用了itertools 中给出的实现? 2)这么轻的包是不是有问题的依赖?【参考方案18】:

递归获取所有子集。疯狂的单线

from typing import List

def subsets(xs: list) -> List[list]:
    return subsets(xs[1:]) + [x + [xs[0]] for x in subsets(xs[1:])] if xs else [[]]

基于 Haskell 解决方案

subsets :: [a] -> [[a]]
subsets [] = [[]]
subsets (x:xs) = map (x:) (subsets xs) ++ subsets xs

【讨论】:

NameError: name 'List' is not defined @4LegsDrivenCat 我添加了List import【参考方案19】:
def findsubsets(s, n): 
    return list(itertools.combinations(s, n)) 

def allsubsets(s) :
    a = []
    for x in range(1,len(s)+1):
        a.append(map(set,findsubsets(s,x)))      
    return a

【讨论】:

仅代码的答案被认为是低质量的:请务必说明您的代码的作用以及它如何解决问题。如果您可以在帖子中添加更多信息,它将帮助提问者和未来的读者。见Explaining entirely code-based answers【参考方案20】:

集合 n 范围内的所有子集:

n = int(input())
l = [i for i in range (1, n + 1)]

for number in range(2 ** n) :
    binary = bin(number)[: 1 : -1]
    subset = [l[i] for i in range(len(binary)) if binary[i] == "1"]
    print(set(sorted(subset)) if number > 0 else "")

【讨论】:

【参考方案21】:
import math    
def printPowerSet(set,set_size): 
    pow_set_size =int(math.pow(2, set_size))
    for counter in range(pow_set_size):
    for j in range(set_size):  
        if((counter & (1 << j)) > 0):
            print(set[j], end = "")
    print("")
set = ['a', 'b', 'c']
printPowerSet(set,3)

【讨论】:

【参考方案22】:

这个问题的一个变体,是我在“发现计算机科学:跨学科问题、原则和 Python 编程。2015 版”一书中看到的一个练习。在那个练习 10.2.11 中,输入只是一个整数,输出应该是幂集。这是我的递归解决方案(除了基本的 python3 之外不使用其他任何东西)

def powerSetR(n):
    assert n >= 0
    if n == 0:
        return [[]]
    else:
        input_set = list(range(1, n+1)) # [1,2,...n]
        main_subset = [ ]
        for small_subset in powerSetR(n-1):
            main_subset += [small_subset]
            main_subset += [ [input_set[-1]] + small_subset]
        return main_subset

superset = powerSetR(4)
print(superset)       
print("Number of sublists:", len(superset))

输出是

[[], [4], [3], [4, 3], [2], [4, 2], [3, 2], [4, 3, 2], [1], [ 4, 1], [3, 1], [4, 3, 1], [2, 1], [4, 2, 1], [3, 2, 1], [4, 3, 2, 1] ] 子列表数:16

【讨论】:

【参考方案23】:

我没有遇到过more_itertools.powerset 函数,我建议使用它。我还建议不要使用来自itertools.combinations 的输出的默认排序,通常您希望最小化位置之间的距离,并对项目上方/之前距离较短的项目子集进行排序它们之间的距离更大。

itertools recipes page 表明它使用chain.from_iterable

请注意,这里的r 与binomial coefficient 下部的标准符号相匹配,s 在数学课本和计算器上通常称为n(“n 选择 r”)
def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

这里的其他示例给出了[1,2,3,4] 的幂集,使得 2 元组按“字典”顺序列出(当我们将数字打印为整数时)。如果我在旁边写下数字之间的距离(即差异),它说明了我的观点:

12 ⇒ 1
13 ⇒ 2
14 ⇒ 3
23 ⇒ 1
24 ⇒ 2
34 ⇒ 1

子集的正确顺序应该是首先“耗尽”最小距离的顺序,如下所示:

12 ⇒ 1
23 ⇒ 1
34 ⇒ 1
13 ⇒ 2
24 ⇒ 2
14 ⇒ 3

在此处使用数字会使此排序看起来“错误”,但例如考虑字母 ["a","b","c","d"] 会更清楚为什么按此顺序获取 powerset 可能有用:

ab ⇒ 1
bc ⇒ 1
cd ⇒ 1
ac ⇒ 2
bd ⇒ 2
ad ⇒ 3

这种效果在项目越多时越明显,就我的目的而言,它使得能够有意义地描述 powerset 的索引范围有所不同。

(在Gray codes等上写了很多关于组合算法的输出顺序,我不认为这是一个附带问题)。

我实际上只是编写了一个相当复杂的程序,它使用这个快速整数分区代码以正确的顺序输出值,但后来我发现more_itertools.powerset 并且对于大多数用途来说,像这样使用该函数可能很好:

from more_itertools import powerset
from numpy import ediff1d

def ps_sorter(tup):
    l = len(tup)
    d = ediff1d(tup).tolist()
    return l, d

ps = powerset([1,2,3,4])

ps = sorted(ps, key=ps_sorter)

for x in ps:
    print(x)

()
(1,)
(2,)
(3,)
(4,)
(1, 2)
(2, 3)
(3, 4)
(1, 3)
(2, 4)
(1, 4)
(1, 2, 3)
(2, 3, 4)
(1, 2, 4)
(1, 3, 4)
(1, 2, 3, 4)

我编写了一些更复杂的代码,可以很好地打印 powerset(请参阅 repo 以了解我在这里没有包含的漂亮打印功能:print_partitionsprint_partitions_by_lengthpprint_tuple)。

回购:ordered-powerset,特别是pset_partitions.py

这一切都非常简单,但如果您想要一些可以让您直接访问不同级别的 powerset 的代码,它仍然很有用:

from itertools import permutations as permute
from numpy import cumsum

# http://jeromekelleher.net/generating-integer-partitions.html
# via
# https://***.com/questions/10035752/elegant-python-code-for-integer-partitioning#comment25080713_10036764

def asc_int_partitions(n):
    a = [0 for i in range(n + 1)]
    k = 1
    y = n - 1
    while k != 0:
        x = a[k - 1] + 1
        k -= 1
        while 2 * x <= y:
            a[k] = x
            y -= x
            k += 1
        l = k + 1
        while x <= y:
            a[k] = x
            a[l] = y
            yield tuple(a[:k + 2])
            x += 1
            y -= 1
        a[k] = x + y
        y = x + y - 1
        yield tuple(a[:k + 1])

# https://***.com/a/6285330/2668831
def uniquely_permute(iterable, enforce_sort=False, r=None):
    previous = tuple()
    if enforce_sort: # potential waste of effort (default: False)
        iterable = sorted(iterable)
    for p in permute(iterable, r):
        if p > previous:
            previous = p
            yield p

def sum_min(p):
    return sum(p), min(p)

def partitions_by_length(max_n, sorting=True, permuting=False):
    partition_dict = 0: ()
    for n in range(1,max_n+1):
        partition_dict.setdefault(n, [])
        partitions = list(asc_int_partitions(n))
        for p in partitions:
            if permuting:
                perms = uniquely_permute(p)
                for perm in perms:
                    partition_dict.get(len(p)).append(perm)
            else:
                partition_dict.get(len(p)).append(p)
    if not sorting:
        return partition_dict
    for k in partition_dict:
        partition_dict.update(k: sorted(partition_dict.get(k), key=sum_min))
    return partition_dict

def print_partitions_by_length(max_n, sorting=True, permuting=True):
    partition_dict = partitions_by_length(max_n, sorting=sorting, permuting=permuting)
    for k in partition_dict:
        if k == 0:
            print(tuple(partition_dict.get(k)), end="")
        for p in partition_dict.get(k):
            print(pprint_tuple(p), end=" ")
        print()
    return

def generate_powerset(items, subset_handler=tuple, verbose=False):
    """
    Generate the powerset of an iterable `items`.

    Handling of the elements of the iterable is by whichever function is passed as
    `subset_handler`, which must be able to handle the `None` value for the
    empty set. The function `string_handler` will join the elements of the subset
    with the empty string (useful when `items` is an iterable of `str` variables).
    """
    ps = 0: [subset_handler()]
    n = len(items)
    p_dict = partitions_by_length(n-1, sorting=True, permuting=True)
    for p_len, parts in p_dict.items():
        ps.setdefault(p_len, [])
        if p_len == 0:
            # singletons
            for offset in range(n):
                subset = subset_handler([items[offset]])
                if verbose:
                    if offset > 0:
                        print(end=" ")
                    if offset == n - 1:
                        print(subset, end="\n")
                    else:
                        print(subset, end=",")
                ps.get(p_len).append(subset)
        for pcount, partition in enumerate(parts):
            distance = sum(partition)
            indices = (cumsum(partition)).tolist()
            for offset in range(n - distance):
                subset = subset_handler([items[offset]] + [items[offset:][i] for i in indices])
                if verbose:
                    if offset > 0:
                        print(end=" ")
                    if offset == n - distance - 1:
                        print(subset, end="\n")
                    else:
                        print(subset, end=",")
                ps.get(p_len).append(subset)
        if verbose and p_len < n-1:
            print()
    return ps

作为一个例子,我写了一个 CLI 演示程序,它接受一个字符串作为命令行参数:

python string_powerset.py abcdef

a, b, c, d, e, f

ab, bc, cd, de, ef
ac, bd, ce, df
ad, be, cf
ae, bf
af

abc, bcd, cde, def
abd, bce, cdf
acd, bde, cef
abe, bcf
ade, bef
ace, bdf
abf
aef
acf
adf

abcd, bcde, cdef
abce, bcdf
abde, bcef
acde, bdef
abcf
abef
adef
abdf
acdf
acef

abcde, bcdef
abcdf
abcef
abdef
acdef

abcdef

【讨论】:

【参考方案24】:

如果你想要任何特定长度的子集,你可以这样做:

from itertools import combinations
someSet = 0, 1, 2, 3
([x for i in range(len(someSet)+1) for x in combinations(someSet,i)])

更一般地,对于任意长度子集,您可以修改范围参数。输出是

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

【讨论】:

【参考方案25】:

你可以这样做:

def powerset(x):
    m=[]
    if not x:
        m.append(x)
    else:
        A = x[0]
        B = x[1:]
        for z in powerset(B):
            m.append(z)
            r = [A] + z
            m.append(r)
    return m

print(powerset([1, 2, 3, 4]))

输出:

[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3], [4], [1, 4], [2, 4], [1, 2, 4], [3, 4], [1, 3, 4], [2, 3, 4], [1, 2, 3, 4]]

【讨论】:

我可以建议在发布代码解决方案时,请详细说明代码在做什么以及为什么使用这种或那种方法来解决问题。新编码人员不应该只查看代码块并复制/粘贴它而不知道代码在做什么以及为什么。感谢并欢迎使用 ***。 一个真正令人印象深刻的递归答案。【参考方案26】:

itertools.product 可以很自然地做到这一点:

import itertools

def powerset(l):
    for sl in itertools.product(*[[[], [i]] for i in l]):
        yield j for i in sl for j in i

【讨论】:

对这个问题给出的最优雅的答案 @ArthurB. ***.com/a/59883397/8729073 不仅是最优雅的,而且也是最快的,似乎是gist.github.com/ciphergoth/22569ed316a61e40f7ef49f986e9704f(在此线程中搜索“timeit”)【参考方案27】:

我知道为时已晚

已经有许多其他解决方案,但仍然......

def power_set(lst):
    pw_set = [[]]

    for i in range(0,len(lst)):
        for j in range(0,len(pw_set)):
            ele = pw_set[j].copy()
            ele = ele + [lst[i]]
            pw_set = pw_set + [ele]

    return pw_set

【讨论】:

【参考方案28】:
def powerset(some_set):
    res = [(a,b) for a in some_set for b in some_set]
    return res

【讨论】:

虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。考虑阅读How to Answer 和edit 你的答案以改进它。 @blurfus 始终是一个很好的做法,但当您用 28 个其他答案回答一个十年前的问题时,这一点尤为重要。为什么这是对公认答案的改进?这个答案有什么其他答案没有提供的? 此外,此代码不会返回集合的幂集,而是仅返回基数大小为 2 的元组的集合【参考方案29】:

这是我的解决方案,它(在概念上)与 lmiguelvargasf 的解决方案相似。

让我这么说 -[数学项] 根据定义,幂集确实包含空集 -[个人口味] 以及我不喜欢使用frozenset。

所以输入是一个列表,输出是一个列表。该函数可以提前关闭,但我喜欢按字典顺序对幂集的元素进行排序,这基本上意味着很好。

def power_set(L):
    """
    L is a list.
    The function returns the power set, but as a list of lists.
    """
    cardinality=len(L)
    n=2 ** cardinality
    powerset = []
    
    for i in range(n):
        a=bin(i)[2:]
        subset=[]
        for j in range(len(a)):
            if a[-j-1]=='1':
                subset.append(L[j])
        powerset.append(subset)
        
    #the function could stop here closing with
    #return powerset

    powerset_orderred=[]
    for k in range(cardinality+1):
        for w in powerset:
            if len(w)==k:
                powerset_orderred.append(w)
        
    return powerset_orderred

【讨论】:

【参考方案30】:
from itertools import combinations
def subsets(arr: set) -> list:
   subsets = []
   [subsets.extend(list(combinations(arr,n))) for n in range(len(arr))]
   return subsets 

a = 1,2,3
print(subsets(a))

输出:

[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3)]

对于排序的子集,我们可以这样做:

# sorted subsets
print(sorted(subsets(a)))

输出:

[(), (1,), (1, 2), (1, 3), (2,), (2, 3), (3,)]

【讨论】:

以上是关于如何获取集合的所有子集? (动力装置)的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Firebase + Firestore + Flutter 中使用 StreamBuilder 将所有子集合数据获取到 Widget

如何使用 Firebase Firestore 获取子集合? [复制]

Firestore 获取根集合的所有文档和子集合

如何从 Firestore 获取子集合数据

如何从 React JS 中的 firebase 文档的所有子集合中获取数据? [复制]

我如何在颤振中获得 Firestore 集合和子集合