web-dev-qa-db-ja.com

セットのすべてのサブセットを取得する方法は? (パワーセット)

与えられたセット

{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}]
61
astronought

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の組み合わせを避けることができます。

84
Mark Rushakoff

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のコメントはここに適用できます:「最初に空のTupleが気に入らない場合は、on」 「ただし、私の場合はfor i in range(1 << x)for i in range(1, 1 << 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を使用すると、1つのメモリですべての結果を計算する必要がなくなります。メインループの外側でマスクを事前計算することは、価値のある最適化であると想定されます。

32
hughdbrown

あなたが簡単な答えを探しているなら、私はグーグルで「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]]
14
Edan Maor
def powerset(lst):
    return reduce(lambda result, x: result + [subset + [x] for subset in result],
                  lst, [[]])
11
newacct

パワーセットの改良点があります:

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
6
Jingguo Yao

次のアルゴリズムが非常に明確でシンプルであることがわかりました。

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_all_subsets(remaining_list):
        subsets.append(partial_subset)
        subsets.append(partial_subset[:] + [first_element])

    return subsets

Powersetを生成するもう1つの方法は、nビットを持つすべての2進数を生成することです。パワーセットとして、n桁の数値の量は2 ^ n。このアルゴリズムの原理は、2進数が1または0であるが両方ではない可能性があるため、要素がサブセット内に存在するかしないかということです。

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 Computational Thinking and Data Science入門時に両方のアルゴリズムを見つけましたが、これは私が見た中で最も理解しやすいアルゴリズムの1つであると思います。

4
lmiguelvargasf
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]]
4
jmkg

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:0{length}b}'
        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-stringを使用していることに注意してください。基本的に、これにより、各反復中に各サブセットに追加される要素を選択できます。入力セットに_{0, 1, 2}_という3つの要素があるため、セレクターは0から7までの値(2進数を含む)を取ります。

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

したがって、各ビットは、元のセットの要素を追加する必要があるかどうかを示すインジケータとして機能します。 2進数を見て、各番号をスーパーセットの要素と考えてください。_1_はインデックスjの要素を追加する必要があることを意味し、_0_はこれを意味します要素を追加しないでください。

セットの内包表記を使用して各反復でサブセットを生成し、このサブセットをfrozensetに変換して、ps(パワーセット)に追加できるようにします。そうしないと、Pythonのセットは不変オブジェクトのみで構成されるため、追加できません。

単純化

いくつかのpython内包表記を使用してコードを単純化できるため、ループのそれらを取り除くことができます。また、Zipを使用してjインデックスとコードの使用を避けることもできます次のようになります:

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

それでおしまい。このアルゴリズムが気に入っているのは、期待どおりに動作しても、itertoolsに依存することは非常に魔法のように見えるため、他のアルゴリズムよりも明確で直感的です。

3
lmiguelvargasf

簡単なパワーセットリフレッシャー!

セット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 への完全なクレジット

2
grepit

これらの答えのほとんどすべては、listではなくsetを使用しています。それで、好奇心から、単純にsetで簡単なバージョンを作成し、他の「Pythonの初心者」向けに要約しようとしました。

Pythonの set implementation を扱うには、いくつか奇妙な点があることがわかりました。私にとっての主な驚きは、空のセットを処理することでした。これは、Rubyの Set implementation とは対照的です。ここでは、単に_Set[Set[]]_を実行して、空のSetを1つ含むSetを取得できます。少しわかりにくい。

確認するには、powersetsでsetを実行すると、2つの問題が発生しました。

  1. set()はイテラブルをとるので、set(set())set()を返します イテラブルの空のセットが空のため (だと思います:))
  2. セットのセットを取得するには、set({set()})およびset.add(set)は、set()hashableではない なので動作しません

両方の問題を解決するために、私はfrozenset()を使用しました。つまり、必要なものがまったく得られません(タイプは文字通りset)が、全体のset interace。

_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に変換する場合は、マッピングする必要があります。 listlist(map(set, powerset(set([1,2,3,4])))))に戻すか、上記を変更します。

1

最もわかりやすいソリューションであるアンチコードゴルフバージョンを提供したかっただけです。

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

詳細 itertoolsのドキュメントを参照 、および power sets のウィキペディアエントリ

1
Gourneau

組み合わせを使用するが、ビルトインのみを使用する簡単な実装を次に示します。

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
Daniel

Python 3.5以降では、yield fromステートメントと itertools.combinations

def subsets(iterable):
    for n in range(len(iterable)):
        yield from combinations(iterable, n + 1)
0
Juan C. Roldán

すべてのサブセットの一部である空のセットでは、次を使用できます。

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

これは、これらの答えが実際にPythonセットのリターンを実際に提供しないためです。実際に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([]) ])

ただし、より良い実装を期待しています。

簡単な方法は、2の補数演算の下で整数の内部表現を利用することです。

整数のバイナリ表現は、0〜7の範囲の数値の{000、001、010、011、100、101、110、111}になります。整数カウンター値の場合、1をコレクション内の対応する要素と '0'に含めると見なします除外として、カウントシーケンスに基づいてサブセットを生成できます。数値は_0_からpow(2,n) -1に生成する必要があります。nは配列の長さ、つまりバイナリ表現のビット数です。

単純なSubset Generator Functionに基づいて、次のように記述できます。基本的に依存しています

_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 __== '__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']]
_
0
ViFI