与えられたセット
{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}]
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の組み合わせを避けることができます。
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つのメモリですべての結果を計算する必要がなくなります。メインループの外側でマスクを事前計算することは、価値のある最適化であると想定されます。
あなたが簡単な答えを探しているなら、私はグーグルで「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]]
def powerset(lst):
return reduce(lambda result, x: result + [subset + [x] for subset in result],
lst, [[]])
パワーセットの改良点があります:
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
次のアルゴリズムが非常に明確でシンプルであることがわかりました。
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つであると思います。
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 (すべてのサブセットのセット)の数学的な定義により整合しているため、このアプローチが好きです。
_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
に依存することは非常に魔法のように見えるため、他のアルゴリズムよりも明確で直感的です。
簡単なパワーセットリフレッシャー!
セット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 への完全なクレジット
これらの答えのほとんどすべては、list
ではなくset
を使用しています。それで、好奇心から、単純にset
で簡単なバージョンを作成し、他の「Pythonの初心者」向けに要約しようとしました。
Pythonの set implementation を扱うには、いくつか奇妙な点があることがわかりました。私にとっての主な驚きは、空のセットを処理することでした。これは、Rubyの Set implementation とは対照的です。ここでは、単に_Set[Set[]]
_を実行して、空のSet
を1つ含むSet
を取得できます。少しわかりにくい。
確認するには、powerset
sでset
を実行すると、2つの問題が発生しました。
set()
はイテラブルをとるので、set(set())
はset()
を返します イテラブルの空のセットが空のため (だと思います:))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)frozenset
sを正しく取得します。
_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でset
sのset
を使用する方法がないため、これらのfrozenset
sをset
sに変換する場合は、マッピングする必要があります。 list
(list(map(set, powerset(set([1,2,3,4]))))
)に戻すか、上記を変更します。
最もわかりやすいソリューションであるアンチコードゴルフバージョンを提供したかっただけです。
_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 のウィキペディアエントリ
組み合わせを使用するが、ビルトインのみを使用する簡単な実装を次に示します。
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
Python 3.5以降では、yield from
ステートメントと itertools.combinations :
def subsets(iterable):
for n in range(len(iterable)):
yield from combinations(iterable, n + 1)
すべてのサブセットの一部である空のセットでは、次を使用できます。
def subsets(iterable):
for n in range(len(iterable) + 1):
yield from combinations(iterable, n)
これは、これらの答えが実際に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']]
_