Làm thế nào để nhận được tất cả các tập con của một tập hợp? (bộ quyền hạn)


103

Đưa ra một bộ

{0, 1, 2, 3}

Làm cách nào để tạo các tập hợp con:

[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}]

Câu trả lời:


145

itertoolsTrang Python có một powersetcông thức chính xác cho việc này:

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))

Đầu ra:

>>> 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')]

Nếu bạn không thích bộ giá trị trống đó ngay từ đầu, bạn chỉ có thể thay đổi rangecâu lệnh range(1, len(s)+1)để tránh kết hợp độ dài 0.


1
Đây là câu trả lời nhanh nhất mà tôi có thể tìm thấy, so sánh một số giải pháp khác trên trang này với giải pháp này bằng cách sử dụng mô-đun thời gian của Python. Tuy nhiên, trong một số trường hợp nhất định, nếu bạn cần sửa đổi kết quả đầu ra (ví dụ: nối các chữ cái để tạo thành chuỗi) thì việc viết một công thức tùy chỉnh bằng cách sử dụng trình tạo và xây dựng đầu ra bạn muốn (ví dụ: thêm hai chuỗi lại với nhau) có thể nhanh hơn nhiều.
Ceasar Bautista

tại sao s = list(iterable)cần thiết?
Jack Stevens

@JackStevens vì các tệp lặp không thể tua lại và không bắt buộc phải __len__triển khai; thử powerset((n for n in range(3)))mà không có gói danh sách.
hoefling

1
đối với các chuỗi lớn, điều này sẽ ăn rất nhiều bộ nhớ!
NoobEditor

1
@AlexandreHuat: Dải ô là chuỗi lười biếng, không phải trình lặp. powerset(range(3))sẽ hoạt động tốt ngay cả khi không cós = list(iterable) .
user2357112 hỗ trợ Monica

50

Đây là thêm mã cho bộ poweret. Điều này được viết từ đầu:

>>> 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]

Nhận xét của Mark Rushakoff có thể áp dụng ở đây: "Nếu bạn không thích bộ giá trị trống đó ngay từ đầu," bạn chỉ có thể thay đổi câu lệnh phạm vi thành phạm vi (1, len (s) +1) để tránh kết hợp độ dài 0 ", ngoại trừ trường hợp của tôi, bạn thay đổi for i in range(1 << x)thành for i in range(1, 1 << x).


Quay trở lại những năm này sau đó, bây giờ tôi viết nó như thế này:

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]

Và sau đó mã kiểm tra sẽ giống như thế này, giả sử:

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

Sử dụng yieldcó nghĩa là bạn không cần phải tính toán tất cả các kết quả trong một phần bộ nhớ. Tính toán trước các mặt nạ bên ngoài vòng lặp chính được coi là một cách tối ưu hóa đáng giá.


6
Đây là một câu trả lời sáng tạo. Tuy nhiên, tôi đã đo nó bằng timeit để so sánh với Mark Rushakoff và nhận thấy nó chậm hơn đáng kể. Để tạo ra bộ nguồn gồm 16 mục 100 lần, số đo của tôi là 0,55 so với 15,6.
Ceasar Bautista

19

Nếu bạn đang tìm kiếm câu trả lời nhanh, tôi vừa tìm kiếm "python power set" trên google và tìm ra cái này: Python Power Set Generator

Đây là bản sao-dán từ mã trong trang đó:

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

Điều này có thể được sử dụng như thế này:

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

Bây giờ r là danh sách tất cả các phần tử bạn muốn và có thể được sắp xếp và in:

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]]

1
Trong trường hợp của một mảng trống rỗng như là đầu vào, các mã trên sẽ quay trở lại [[][]], để sửa chữa mà chỉ riêng các trường hợp để kiểm tra chiều dàiif len(seq) == 0: yield [] elif len(seq) == 1: yield seq yield []
Ayush

3
Để tham khảo, tôi đã đo điều này (với bản chỉnh sửa của Ayush) bằng cách sử dụng timeit và so sánh nó với công thức powerset trong câu trả lời của Mark Rushakoff. Trên máy tính của tôi, để tạo ra bộ sức mạnh của 16 mục 100 lần, thuật toán này mất 1,36 giây trong khi thuật toán của Rushakoff mất 0,55.
Ceasar Bautista

Thời gian phức tạp cho việc này là gì?
CodeQuestor

13
def powerset(lst):
    return reduce(lambda result, x: result + [subset + [x] for subset in result],
                  lst, [[]])

8

Có một sự cải tiến của tập hợp quyền hạn:

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

8

TL; DR (chuyển trực tiếp đến Đơn giản hóa)

Tôi biết trước đây tôi đã thêm một câu trả lời, nhưng tôi thực sự thích cách triển khai mới của mình. Tôi đang lấy một tập hợp làm đầu vào, nhưng nó thực sự có thể là bất kỳ tập hợp nào có thể lặp lại và tôi đang trả về một tập hợp các tập hợp là tập hợp sức mạnh của đầu vào. Tôi thích cách tiếp cận này vì nó phù hợp hơn với định nghĩa toán học của tập hợp lũy thừa ( tập hợp của tất cả các tập hợp con ).

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:0{length}b}'
        subset = {l[j] for j, bit in enumerate(selector) if bit == '1'}
        ps.add(frozenset(subset))

    return ps

Nếu bạn muốn chính xác kết quả mà bạn đã đăng trong câu trả lời của mình, hãy sử dụng cái này:

>>> [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}]

Giải trình

Người ta biết rằng số phần tử của bộ nguồn là 2 ** len(A), do đó có thể nhìn thấy rõ ràng trong forvòng lặp.

Tôi cần chuyển đổi đầu vào (lý tưởng là một tập hợp) thành một danh sách bởi vì tập hợp là cấu trúc dữ liệu của các phần tử không có thứ tự duy nhất và thứ tự sẽ rất quan trọng để tạo ra các tập hợp con.

selectorlà chìa khóa trong thuật toán này. Lưu ý rằng selectorcó cùng độ dài với tập hợp đầu vào và để thực hiện điều này, sử dụng chuỗi f có đệm. Về cơ bản, điều này cho phép tôi chọn các phần tử sẽ được thêm vào mỗi tập hợp con trong mỗi lần lặp. Giả sử tập hợp đầu vào có 3 phần tử {0, 1, 2}, vì vậy bộ chọn sẽ nhận các giá trị từ 0 đến 7 (bao gồm), trong hệ nhị phân là:

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

Vì vậy, mỗi bit có thể dùng như một chỉ báo nếu một phần tử của tập hợp ban đầu có nên được thêm vào hay không. Nhìn vào các số nhị phân và chỉ nghĩ mỗi số như một phần tử của tập siêu, trong đó 1có nghĩa là một phần tử ở chỉ mục jnên được thêm vào và 0có nghĩa là phần tử này không nên được thêm vào.

Tôi đang sử dụng một tập hợp để tạo ra một tập hợp con ở mỗi lần lặp và tôi chuyển tập hợp con này thành một tập hợp con frozensetđể tôi có thể thêm nó vào ps(tập hợp sức mạnh). Nếu không, tôi sẽ không thể thêm nó vì một tập hợp trong Python chỉ bao gồm các đối tượng bất biến.

Đơn giản hóa

Bạn có thể đơn giản hóa mã bằng cách sử dụng một số hiểu về python, vì vậy bạn có thể loại bỏ những vòng lặp for. Bạn cũng có thể sử dụng zipđể tránh sử dụng jchỉ mục và mã sẽ kết thúc như sau:

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

Đó là nó. Điều tôi thích ở thuật toán này là rõ ràng và trực quan hơn những thuật toán khác vì nó trông khá ảo diệu để dựa vào itertoolsmặc dù nó hoạt động như mong đợi.


5
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

Ví dụ:

get_power_set([1,2,3])

năng suất

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

1
Việc sửa đổi một biến vòng lặp ( power_set) trong vòng lặp mà nó quản lý là một thực tế rất đáng nghi ngờ. Ví dụ, giả sử bạn là người viết thay vì mã biến sửa đổi đề xuất: power_set += [list(sub_set)+[elem]]. Sau đó, vòng lặp không kết thúc.
hughdbrown

5

Tôi đã tìm thấy thuật toán sau rất rõ ràng và đơn giản:

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

Một cách khác có thể tạo ra tập lũy thừa là tạo tất cả các số nhị phân có nbit. Như một bộ lũy thừa, số lượng có các nchữ số là 2 ^ n. Nguyên tắc của thuật toán này là một phần tử có thể có hoặc không trong một tập con dưới dạng chữ số nhị phân có thể là một hoặc không nhưng không phải là cả hai.

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

Tôi đã tìm thấy cả hai thuật toán khi tôi tham gia MITx: 6.00.2x Giới thiệu về Tư duy Tính toán và Khoa học Dữ liệu và tôi coi đó là một trong những thuật toán dễ hiểu nhất mà tôi từng thấy.


3

Tôi chỉ muốn cung cấp giải pháp dễ hiểu nhất, phiên bản chống chơi gôn.

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

Kết quả

Tất cả các bộ độ dài 0

[()]

Tất cả các bộ độ dài 1

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

Tất cả các bộ chiều dài 2

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

Tất cả các bộ chiều dài 3

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

Để biết thêm, hãy xem tài liệu itertools , cũng là mục nhập wikipedia về bộ nguồn


2

Chỉ cần làm mới bộ nguồn nhanh chóng!

Tập hợp lũy thừa của một tập X, đơn giản là tập tất cả các tập con của X bao gồm cả tập rỗng

Ví dụ tập hợp X = (a, b, c)

Bộ công suất = {{a, b, c}, {a, b}, {a, c}, {b, c}, {a}, {b}, {c}, {}}

Đây là một cách khác để tìm bộ nguồn:

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]))

tín dụng đầy đủ cho nguồn


2

Với tập hợp trống, là một phần của tất cả các tập hợp con, bạn có thể sử dụng:

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

2

Điều này có thể được thực hiện rất tự nhiên với 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}

1
câu trả lời thanh lịch nhất cho câu hỏi này
Arthur B.

1

Một cách đơn giản sẽ là khai thác biểu diễn bên trong của các số nguyên dưới số học phần bù của 2.

Biểu diễn nhị phân của số nguyên là {000, 001, 010, 011, 100, 101, 110, 111} cho các số từ 0 đến 7. Đối với giá trị bộ đếm số nguyên, coi 1 là bao gồm phần tử tương ứng trong tập hợp và '0' như loại trừ, chúng ta có thể tạo các tập con dựa trên trình tự đếm. Số phải được tạo ra từ 0để pow(2,n) -1trong đó n là chiều dài của mảng số ví dụ của các bit trong biểu diễn nhị phân.

Hàm tạo tập hợp con đơn giản dựa trên nó có thể được viết như dưới đây. Về cơ bản nó dựa vào

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

và sau đó nó có thể được sử dụng như

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

Thử nghiệm

Thêm thông tin sau vào tệp cục bộ

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

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

đưa ra kết quả sau

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']]

Điều này có thể không thực tế liên quan đến khả năng bảo trì hoặc khả năng đọc, nhưng nó đã khiến tôi suy nghĩ. Cảm ơn bạn đã chia sẻ, giải pháp thông minh!
lorey

1

Hầu như tất cả các câu trả lời này đều sử dụng listthay vì sử dụng set, điều này khiến tôi cảm thấy hơi gian dối. Vì vậy, vì tò mò, tôi đã cố gắng tạo một phiên bản đơn giản thực sự setvà tóm tắt cho những người "mới làm quen với Python" khác.

Tôi nhận thấy có một vài điều kỳ lạ khi xử lý việc triển khai thiết lập của Python . Điều ngạc nhiên chính đối với tôi là xử lý các bộ trống. Điều này trái ngược với việc triển khai Ruby's Set , nơi tôi có thể đơn giản thực hiện Set[Set[]]và lấy một cái Setchứa trống Set, vì vậy ban đầu tôi thấy nó hơi khó hiểu.

Để xem lại, khi thực hiện powersetvới sets, tôi gặp phải hai vấn đề:

  1. set()lấy một có thể lặp lại, vì vậy set(set())sẽ trả về set() vì tập hợp trống có thể lặp lại là trống (tôi đoán vậy :))
  2. để có một tập hợp các bộ set({set()})set.add(set)sẽ không hoạt động vì set() không thể băm

Để giải quyết cả hai vấn đề, tôi đã sử dụng frozenset(), có nghĩa là tôi không hoàn toàn có được những gì mình muốn (nghĩa đen là loại set), nhưng sử dụng tổng thể setliên kết.

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

Dưới đây, chúng tôi nhận được 2² (16) frozensets chính xác làm đầu ra:

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})}

Vì không có cách nào để có một settrong số sets trong Python, nếu bạn muốn biến những cái này frozensetthành sets, bạn sẽ phải ánh xạ chúng trở lại thành dấu list( list(map(set, powerset(set([1,2,3,4])))) ) hoặc sửa đổi ở trên.


1

Có lẽ câu hỏi đã cũ, nhưng tôi hy vọng mã của tôi sẽ giúp được ai đó.

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

ew, đệ quy! =)
étale-cohomology

Có lẽ không phải là cách hiệu quả nhất, nhưng luôn thú vị khi xem cách đệ quy!
Lisandro Di Meo

1

Sử dụng chức năng powerset()từ gói more_itertools.

Mang lại tất cả các tập con có thể lặp lại được

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

Nếu bạn muốn bộ, hãy sử dụng:

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

1
Vì vậy, nhiều người đang phát minh lại bánh xe ở đây, IMHO đây là câu trả lời tốt nhất vì nó có thể đã nằm trong phần phụ thuộc của bạn vì nó được yêu cầu bởi nhiều thư viện phổ biến, ví dụ như pytest. libraries.io/pypi/more-itertools/dependents
lorey

1

Nhận tất cả các tập hợp con bằng đệ quy. Một lớp lót điên cuồng

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 [[]]

Dựa trên giải pháp Haskell

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

NameError: name 'List' is not defined
4LegsDrivenCat

@ 4LegsDrivenCat Tôi đã thêm Listnhập
Paweł Rubin

1
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

Các câu trả lời chỉ có mã được coi là chất lượng thấp: hãy đảm bảo cung cấp lời giải thích mã của bạn hoạt động như thế nào và cách nó giải quyết vấn đề. Nó sẽ giúp ích cho cả người hỏi và người đọc trong tương lai nếu bạn có thể thêm thông tin vào bài đăng của mình. Xem Giải thích các câu trả lời hoàn toàn dựa trên mã
Calos

1

Bạn có thể làm như thế này:

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]))

Đầu ra:

[[], [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]]

Tôi có thể đề nghị rằng khi đăng một giải pháp mã, hãy đủ tử tế để đưa ra lời giải thích chi tiết về những gì mã đang làm và tại sao bạn sử dụng phương pháp này hoặc phương pháp đó để giải quyết vấn đề. Các lập trình viên mới không nên chỉ nhìn vào một khối mã và sao chép / dán nó mà không biết chính xác mã đang làm gì và tại sao. Cảm ơn và chào mừng bạn đến với Stackoverflow.
Yokai

Một câu trả lời thực sự ấn tượng và đệ quy.
John

1

Tôi biết điều này là quá muộn

Đã có nhiều giải pháp khác nhưng vẫn ...

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

0

Điều này là hoang đường vì không có câu trả lời nào trong số này thực sự cung cấp sự trở lại của một bộ Python thực. Đây là một triển khai lộn xộn sẽ cung cấp một bộ poweret thực sự là một Python set.

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([]) ])

Tuy nhiên, tôi muốn thấy một triển khai tốt hơn.


Điểm tốt, nhưng OP muốn một danh sách các bộ làm đầu ra, vì vậy (trong Python 3) bạn có thể làm [*map(set, chain.from_iterable(combinations(s, r) for r in range(len(s)+1)))]; hàm đối số của mapcó thể là frozensetnếu bạn thích.
PM 2Ring

0

Đây là cách triển khai nhanh chóng của tôi bằng cách sử dụng các kết hợp nhưng chỉ sử dụng các phần mềm cài sẵn.

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

0

Tất cả các tập con trong phạm vi n như tập hợp:

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 "{}")

0
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)

0

Một biến thể của câu hỏi, là một bài tập tôi thấy trong sách "Khám phá Khoa học Máy tính: Các vấn đề liên ngành, các nguyên tắc và lập trình Python. Ấn bản năm 2015". Trong bài tập 10.2.11 đó, đầu vào chỉ là một số nguyên và đầu ra phải là các bộ lũy thừa. Đây là giải pháp đệ quy của tôi (không sử dụng bất kỳ thứ gì khác ngoài python3 cơ bản)

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))

Và đầu ra là

[[], [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]] danh sách phụ: 16


0

Tôi đã không xem qua more_itertools.powersetchức năng và khuyên bạn nên sử dụng nó. Tôi cũng khuyên bạn không nên sử dụng thứ tự mặc định của đầu ra từ itertools.combinations, thay vào đó, bạn thường muốn giảm thiểu khoảng cách giữa các vị trí và sắp xếp các tập hợp con của các mục có khoảng cách ngắn hơn giữa chúng ở trên / trước các mục có khoảng cách lớn hơn giữa chúng.

Các itertoolscông thức trang show nó sử dụngchain.from_iterable

  • Lưu ý rằng rký hiệu ở đây khớp với ký hiệu tiêu chuẩn cho phần dưới của hệ số nhị thức , sthường được gọi là ntrong các văn bản toán học và trên máy tính ("n Chọ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))

Các ví dụ khác ở đây cung cấp tập lũy thừa [1,2,3,4]theo cách mà 2 bộ giá trị được liệt kê theo thứ tự "từ vựng" (khi chúng tôi in các số dưới dạng số nguyên). Nếu tôi viết khoảng cách giữa các số dọc theo nó (tức là sự khác biệt), nó cho thấy quan điểm của tôi:

121
132
143
231
242
341

Thứ tự chính xác cho các tập hợp con phải là thứ tự 'sử dụng hết' khoảng cách tối thiểu trước, như sau:

121
231
341
132
242
143

Việc sử dụng các số ở đây làm cho thứ tự này trông 'sai', nhưng hãy xem xét ví dụ các chữ cái để ["a","b","c","d"]hiểu rõ hơn lý do tại sao điều này có thể hữu ích để có được bộ poweret theo thứ tự này:

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

Hiệu ứng này rõ ràng hơn với nhiều mục hơn và đối với mục đích của tôi, nó tạo ra sự khác biệt giữa việc có thể mô tả phạm vi của các chỉ mục của bộ poweret một cách có ý nghĩa.

(Có rất nhiều điều được viết trên mã Gray v.v. cho thứ tự đầu ra của các thuật toán trong tổ hợp, tôi không coi đó là vấn đề phụ).

Tôi thực sự vừa viết một chương trình khá liên quan sử dụng mã phân vùng số nguyên nhanh này để xuất các giá trị theo thứ tự thích hợp, nhưng sau đó tôi đã phát hiện ra more_itertools.powersetvà đối với hầu hết các trường hợp sử dụng, có lẽ chỉ cần sử dụng hàm đó như vậy là tốt:

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)

Tôi đã viết một số mã tham gia nhiều hơn mà sẽ in Powerset độc đáo (xem repo cho các chức năng tôi đã không bao gồm ở đây khá in: print_partitions, print_partitions_by_length, và pprint_tuple).

Tất cả điều này khá đơn giản, nhưng vẫn có thể hữu ích nếu bạn muốn một số mã cho phép bạn truy cập thẳng vào các cấp độ khác nhau của bộ poweret:

from itertools import permutations as permute
from numpy import cumsum

# http://jeromekelleher.net/generating-integer-partitions.html
# via
# /programming/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://stackoverflow.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

Ví dụ, tôi đã viết một chương trình demo CLI lấy một chuỗi làm đối số dòng lệnh:

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

0

Nếu bạn muốn bất kỳ độ dài cụ thể nào của các tập hợp con, bạn có thể làm như sau:

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

Nói chung hơn đối với các tập con độ dài trọng tài, bạn có thể sửa đổi phạm vi cung cấp. Đầu ra là

[(), (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 )]


0
def powerset(some_set):
    res = [(a,b) for a in some_set for b in some_set]
    return res

6
Mặc dù mã này có thể trả lời câu hỏi, nhưng việc cung cấp thêm ngữ cảnh liên quan đến lý do và / hoặc cách mã này trả lời câu hỏi sẽ cải thiện giá trị lâu dài của nó. Cân nhắc đọc Cách trả lờichỉnh sửa câu trả lời của bạn để cải thiện câu trả lời.
dimfus

2
@Blurfus luôn là một phương pháp hay, nhưng đặc biệt quan trọng khi bạn đang trả lời một câu hỏi cũ của một thập kỷ với 28 câu trả lời khác. Tại sao đây là một cải tiến so với câu trả lời được chấp nhận? Câu trả lời này đóng góp gì mà các câu trả lời khác không cung cấp?
Jeremy Caney

-1

Trong Python 3.5 trở lên, bạn có thể sử dụng yield fromcâu lệnh cùng với itertools.combination :

def subsets(iterable):
    for n in range(len(iterable)):
        yield from combinations(iterable, n + 1)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.