Given a set
{0, 1, 2, 3}
How can I produce the subsets:
[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}]
The Python itertools page has exactly a powerset recipe for this:
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))
Output:
>>> 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')]
If you don't like that empty tuple at the beginning, you can just change the range statement to range(1, len(s)+1) to avoid a 0-length combination.
Here is more code for a powerset. This is written from scratch:
>>> 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's comment is applicable here: "If you don't like that empty tuple at the beginning, on."you can just change the range statement to range(1, len(s)+1) to avoid a 0-length combination", except in my case you change for i in range(1 << x) to for i in range(1, 1 << x).
Returning to this years later, I'd now write it like this:
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]
And then the test code would look like this, say:
print(list(powerset([4, 5, 6])))
Using yield means that you do not need to calculate all results in a single piece of memory. Precalculating the masks outside the main loop is assumed to be a worthwhile optimization.
If you're looking for a quick answer, I just searched "python power set" on google and came up with this: Python Power Set Generator
Here's a copy-paste from the code in that page:
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
This can be used like this:
l = [1, 2, 3, 4]
r = [x for x in powerset(l)]
Now r is a list of all the elements you wanted, and can be sorted and printed:
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]]
Use function powerset() from package more_itertools.
Yields all possible subsets of the iterable
>>> list(powerset([1, 2, 3]))
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
If you want sets, use:
list(map(set, powerset(iterable)))
I have found the following algorithm very clear and simple:
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
Another way one can generate the powerset is by generating all binary numbers that have n bits. As a power set the amount of number with n digits is 2 ^ n. The principle of this algorithm is that an element could be present or not in a subset as a binary digit could be one or zero but not both.
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
I found both algorithms when I was taking MITx: 6.00.2x Introduction to Computational Thinking and Data Science, and I consider it is one of the easiest algorithms to understand I have seen.
from functools import reduce
def powerset(lst):
return reduce(lambda result, x: result + [subset + [x] for subset in result],
lst, [[]])
There is a refinement of 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
TL;DR (go directly to Simplification)
I know I have previously added an answer, but I really like my new implementation. I am taking a set as input, but it actually could be any iterable, and I am returning a set of sets which is the power set of the input. I like this approach because it is more aligned with the mathematical definition of power set (set of all subsets).
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
If you want exactly the output you posted in your answer use this:
>>> [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}]
Explanation
It is known that the number of elements of the power set is 2 ** len(A), so that could clearly be seen in the for loop.
I need to convert the input (ideally a set) into a list because by a set is a data structure of unique unordered elements, and the order will be crucial to generate the subsets.
selector is key in this algorithm. Note that selector has the same length as the input set, and to make this possible it is using an f-string with padding. Basically, this allows me to select the elements that will be added to each subset during each iteration. Let's say the input set has 3 elements {0, 1, 2}, so selector will take values between 0 and 7 (inclusive), which in binary are:
000 # 0
001 # 1
010 # 2
011 # 3
100 # 4
101 # 5
110 # 6
111 # 7
So, each bit could serve as an indicator if an element of the original set should be added or not. Look at the binary numbers, and just think of each number as an element of the super set in which 1 means that an element at index j should be added, and 0 means that this element should not be added.
I am using a set comprehension to generate a subset at each iteration, and I convert this subset into a frozenset so I can add it to ps (power set). Otherwise, I won't be able to add it because a set in Python consists only of immutable objects.
Simplification
You can simplify the code using some python comprehensions, so you can get rid of those for loops. You can also use zip to avoid using j index and the code will end up as the following:
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)
}
That's it. What I like of this algorithm is that is clearer and more intuitive than others because it looks quite magical to rely on itertools even though it works as expected.
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 to it
# effectively doubling the sets resulting in the 2^n sets in the powerset of s.
power_set=power_set+[list(sub_set)+[elem]]
return power_set
For example:
get_power_set([1,2,3])
yield
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
This can be done very naturally with 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}
I know this is too late
There are many other solutions already but still...
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
I just wanted to provide the most comprehensible solution, the anti code-golf version.
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
The results
All sets of length 0
[()]
All sets of length 1
[('x',), ('y',), ('z',)]
All sets of length 2
[('x', 'y'), ('x', 'z'), ('y', 'z')]
All sets of length 3
[('x', 'y', 'z')]
For more see the itertools docs, also the wikipedia entry on power sets
With empty set, which is part of all the subsets, you could use:
def subsets(iterable):
for n in range(len(iterable) + 1):
yield from combinations(iterable, n)
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))
Output:
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3)]
For sorted subsets, we can do:
# sorted subsets
print(sorted(subsets(a)))
Output:
[(), (1,), (1, 2), (1, 3), (2,), (2, 3), (3,)]
Just a quick power set refresher !
Power set of a set X, is simply the set of all subsets of X including
the empty set
Example set X = (a,b,c)
Power Set = { { a , b , c } , { a , b } , { a , c } , { b , c } , { a } , { b } , { c } , { } }
Here is another way of finding power set:
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]))
full credit to source
If you want any specific length of subsets you can do it like this:
from itertools import combinations
someSet = {0, 1, 2, 3}
([x for i in range(len(someSet)+1) for x in combinations(someSet,i)])
More generally for arbitary length subsets you can modify the range arugment. The output is
[(), (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)]
You can do it like this:
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]))
Output:
[[], [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]]
A simple way would be to harness the internal representation of integers under 2's complement arithmetic.
Binary representation of integers is as {000, 001, 010, 011, 100, 101, 110, 111} for numbers ranging from 0 to 7. For an integer counter value, considering 1 as inclusion of corresponding element in collection and '0' as exclusion we can generate subsets based on the counting sequence. Numbers have to be generated from 0 to pow(2,n) -1 where n is the length of array i.e. number of bits in binary representation.
A simple Subset Generator Function based on it can be written as below. It basically relies
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
and then it can be used as
def get_subsets(array):
powerset = []
for i in subsets(array):
powerser.append(i)
return powerset
Testing
Adding following in local file
if __name__ == '__main__':
sample = ['b', 'd', 'f']
for i in range(len(sample)):
print "Subsets for " , sample[i:], " are ", get_subsets(sample[i:])
gives following output
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']]
Almost all of these answers use list rather than set, which felt like a bit of a cheat to me. So, out of curiosity I tried to do a simple version truly on set and summarize for other "new to Python" folks.
I found there's a couple oddities in dealing with Python's set implementation. The main surprise to me was handling empty sets. This is in contrast to Ruby's Set implementation, where I can simply do Set[Set[]] and get a Set containing one empty Set, so I found it initially a little confusing.
To review, in doing powerset with sets, I encountered two problems:
set() takes an iterable, so set(set()) will return set() because the empty set iterable is empty (duh I guess :))
to get a set of sets, set({set()}) and set.add(set) won't work because set() isn't hashable
To solve both issues, I made use of frozenset(), which means I don't quite get what I want (type is literally set), but makes use of the overall 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
Below we get 2² (16) frozensets correctly as output:
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})}
As there's no way to have a set of sets in Python, if you want to turn these frozensets into sets, you'll have to map them back into a list (list(map(set, powerset(set([1,2,3,4]))))
) or modify the above.
Perhaps the question is getting old, but I hope my code will help someone.
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
Getting all the subsets with recursion. Crazy-ass one-liner
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 [[]]
Based on a Haskell solution
subsets :: [a] -> [[a]]
subsets [] = [[]]
subsets (x:xs) = map (x:) (subsets xs) ++ subsets xs
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
def powerSet(s):
sets = [[]]
for i in s:
newsets = []
for k in sets:
newsets.append(k+[i])
sets += newsets
return sets
Code first, for those who want a simple answer.
I have a good explanation here https://leetcode.com/problems/subsets/solutions/3138042/simple-python-solution/
But the short answer is that you start with the set of the empty set, i.e. "sets = [[]]". I recommend to put a print a statement under "for i in s" i.e. "print(sets)" and see that it doubles for each element i
This is wild because none of these answers actually provide the return of an actual Python set. Here is a messy implementation that will give a powerset that actually is a 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([]) ])
I'd love to see a better implementation, though.
Here is my quick implementation utilizing combinations but using only built-ins.
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
All subsets in range n as set:
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 "{}")
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)
A variation of the question, is an exercise I see on the book "Discovering Computer Science: Interdisciplinary Problems, Principles, and Python Programming. 2015 edition". In that exercise 10.2.11, the input is just an integer number, and the output should be the power sets. Here is my recursive solution (not using anything else but basic 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))
And the output is
[[], [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]]
Number of sublists: 16
I hadn't come across the more_itertools.powerset function and would recommend using that. I also recommend not using the default ordering of the output from itertools.combinations, often instead you want to minimise the distance between the positions and sort the subsets of items with shorter distance between them above/before the items with larger distance between them.
The itertools recipes page shows it uses chain.from_iterable
Note that the r here matches the standard notation for the lower part of a binomial coefficient, the s is usually referred to as n in mathematics texts and on calculators (“n Choose 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))
The other examples here give the powerset of [1,2,3,4] in such a way that the 2-tuples are listed in "lexicographic" order (when we print the numbers as integers). If I write the distance between the numbers alongside it (i.e. the difference), it shows my point:
12 ⇒ 1
13 ⇒ 2
14 ⇒ 3
23 ⇒ 1
24 ⇒ 2
34 ⇒ 1
The correct order for subsets should be the order which 'exhausts' the minimal distance first, like so:
12 ⇒ 1
23 ⇒ 1
34 ⇒ 1
13 ⇒ 2
24 ⇒ 2
14 ⇒ 3
Using numbers here makes this ordering look 'wrong', but consider for example the letters ["a","b","c","d"] it is clearer why this might be useful to obtain the powerset in this order:
ab ⇒ 1
bc ⇒ 1
cd ⇒ 1
ac ⇒ 2
bd ⇒ 2
ad ⇒ 3
This effect is more pronounced with more items, and for my purposes it makes the difference between being able to describe the ranges of the indexes of the powerset meaningfully.
(There is a lot written on Gray codes etc. for the output order of algorithms in combinatorics, I don't see it as a side issue).
I actually just wrote a fairly involved program which used this fast integer partition code to output the values in the proper order, but then I discovered more_itertools.powerset and for most uses it's probably fine to just use that function like so:
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)
I wrote some more involved code which will print the powerset nicely (see the repo for pretty printing functions I've not included here: print_partitions, print_partitions_by_length, and pprint_tuple).
Repo: ordered-powerset, specifically pset_partitions.py
This is all pretty simple, but still might be useful if you want some code that'll let you get straight to accessing the different levels of the powerset:
from itertools import permutations as permute
from numpy import cumsum
# http://jeromekelleher.net/generating-integer-partitions.html
# via
# https://stackoverflow.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://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
As an example, I wrote a CLI demo program which takes a string as a command line argument:
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
Here it is my solutions, it is similar (conceptually) with the solution of lmiguelvargasf.
Let me say that
-[math item] by defintion the powerset do contain the empty set
-[personal taste] and also that I don't like using frozenset.
So the input is a list and the output will be a list of lists. The function could close earlier, but I like the element of the power set to be order lexicographically, that essentially means nicely.
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
Related
Given a set
{0, 1, 2, 3}
How can I produce the subsets:
[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}]
The Python itertools page has exactly a powerset recipe for this:
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))
Output:
>>> 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')]
If you don't like that empty tuple at the beginning, you can just change the range statement to range(1, len(s)+1) to avoid a 0-length combination.
Here is more code for a powerset. This is written from scratch:
>>> 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's comment is applicable here: "If you don't like that empty tuple at the beginning, on."you can just change the range statement to range(1, len(s)+1) to avoid a 0-length combination", except in my case you change for i in range(1 << x) to for i in range(1, 1 << x).
Returning to this years later, I'd now write it like this:
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]
And then the test code would look like this, say:
print(list(powerset([4, 5, 6])))
Using yield means that you do not need to calculate all results in a single piece of memory. Precalculating the masks outside the main loop is assumed to be a worthwhile optimization.
If you're looking for a quick answer, I just searched "python power set" on google and came up with this: Python Power Set Generator
Here's a copy-paste from the code in that page:
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
This can be used like this:
l = [1, 2, 3, 4]
r = [x for x in powerset(l)]
Now r is a list of all the elements you wanted, and can be sorted and printed:
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]]
Use function powerset() from package more_itertools.
Yields all possible subsets of the iterable
>>> list(powerset([1, 2, 3]))
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
If you want sets, use:
list(map(set, powerset(iterable)))
I have found the following algorithm very clear and simple:
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
Another way one can generate the powerset is by generating all binary numbers that have n bits. As a power set the amount of number with n digits is 2 ^ n. The principle of this algorithm is that an element could be present or not in a subset as a binary digit could be one or zero but not both.
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
I found both algorithms when I was taking MITx: 6.00.2x Introduction to Computational Thinking and Data Science, and I consider it is one of the easiest algorithms to understand I have seen.
from functools import reduce
def powerset(lst):
return reduce(lambda result, x: result + [subset + [x] for subset in result],
lst, [[]])
There is a refinement of 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
TL;DR (go directly to Simplification)
I know I have previously added an answer, but I really like my new implementation. I am taking a set as input, but it actually could be any iterable, and I am returning a set of sets which is the power set of the input. I like this approach because it is more aligned with the mathematical definition of power set (set of all subsets).
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
If you want exactly the output you posted in your answer use this:
>>> [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}]
Explanation
It is known that the number of elements of the power set is 2 ** len(A), so that could clearly be seen in the for loop.
I need to convert the input (ideally a set) into a list because by a set is a data structure of unique unordered elements, and the order will be crucial to generate the subsets.
selector is key in this algorithm. Note that selector has the same length as the input set, and to make this possible it is using an f-string with padding. Basically, this allows me to select the elements that will be added to each subset during each iteration. Let's say the input set has 3 elements {0, 1, 2}, so selector will take values between 0 and 7 (inclusive), which in binary are:
000 # 0
001 # 1
010 # 2
011 # 3
100 # 4
101 # 5
110 # 6
111 # 7
So, each bit could serve as an indicator if an element of the original set should be added or not. Look at the binary numbers, and just think of each number as an element of the super set in which 1 means that an element at index j should be added, and 0 means that this element should not be added.
I am using a set comprehension to generate a subset at each iteration, and I convert this subset into a frozenset so I can add it to ps (power set). Otherwise, I won't be able to add it because a set in Python consists only of immutable objects.
Simplification
You can simplify the code using some python comprehensions, so you can get rid of those for loops. You can also use zip to avoid using j index and the code will end up as the following:
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)
}
That's it. What I like of this algorithm is that is clearer and more intuitive than others because it looks quite magical to rely on itertools even though it works as expected.
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
For example:
get_power_set([1,2,3])
yield
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
This can be done very naturally with 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}
I know this is too late
There are many other solutions already but still...
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
I just wanted to provide the most comprehensible solution, the anti code-golf version.
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
The results
All sets of length 0
[()]
All sets of length 1
[('x',), ('y',), ('z',)]
All sets of length 2
[('x', 'y'), ('x', 'z'), ('y', 'z')]
All sets of length 3
[('x', 'y', 'z')]
For more see the itertools docs, also the wikipedia entry on power sets
With empty set, which is part of all the subsets, you could use:
def subsets(iterable):
for n in range(len(iterable) + 1):
yield from combinations(iterable, n)
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))
Output:
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3)]
For sorted subsets, we can do:
# sorted subsets
print(sorted(subsets(a)))
Output:
[(), (1,), (1, 2), (1, 3), (2,), (2, 3), (3,)]
Just a quick power set refresher !
Power set of a set X, is simply the set of all subsets of X including
the empty set
Example set X = (a,b,c)
Power Set = { { a , b , c } , { a , b } , { a , c } , { b , c } , { a } , { b } , { c } , { } }
Here is another way of finding power set:
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]))
full credit to source
If you want any specific length of subsets you can do it like this:
from itertools import combinations
someSet = {0, 1, 2, 3}
([x for i in range(len(someSet)+1) for x in combinations(someSet,i)])
More generally for arbitary length subsets you can modify the range arugment. The output is
[(), (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)]
You can do it like this:
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]))
Output:
[[], [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]]
A simple way would be to harness the internal representation of integers under 2's complement arithmetic.
Binary representation of integers is as {000, 001, 010, 011, 100, 101, 110, 111} for numbers ranging from 0 to 7. For an integer counter value, considering 1 as inclusion of corresponding element in collection and '0' as exclusion we can generate subsets based on the counting sequence. Numbers have to be generated from 0 to pow(2,n) -1 where n is the length of array i.e. number of bits in binary representation.
A simple Subset Generator Function based on it can be written as below. It basically relies
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
and then it can be used as
def get_subsets(array):
powerset = []
for i in subsets(array):
powerser.append(i)
return powerset
Testing
Adding following in local file
if __name__ == '__main__':
sample = ['b', 'd', 'f']
for i in range(len(sample)):
print "Subsets for " , sample[i:], " are ", get_subsets(sample[i:])
gives following output
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']]
Almost all of these answers use list rather than set, which felt like a bit of a cheat to me. So, out of curiosity I tried to do a simple version truly on set and summarize for other "new to Python" folks.
I found there's a couple oddities in dealing with Python's set implementation. The main surprise to me was handling empty sets. This is in contrast to Ruby's Set implementation, where I can simply do Set[Set[]] and get a Set containing one empty Set, so I found it initially a little confusing.
To review, in doing powerset with sets, I encountered two problems:
set() takes an iterable, so set(set()) will return set() because the empty set iterable is empty (duh I guess :))
to get a set of sets, set({set()}) and set.add(set) won't work because set() isn't hashable
To solve both issues, I made use of frozenset(), which means I don't quite get what I want (type is literally set), but makes use of the overall 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
Below we get 2² (16) frozensets correctly as output:
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})}
As there's no way to have a set of sets in Python, if you want to turn these frozensets into sets, you'll have to map them back into a list (list(map(set, powerset(set([1,2,3,4]))))
) or modify the above.
Perhaps the question is getting old, but I hope my code will help someone.
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
Getting all the subsets with recursion. Crazy-ass one-liner
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 [[]]
Based on a Haskell solution
subsets :: [a] -> [[a]]
subsets [] = [[]]
subsets (x:xs) = map (x:) (subsets xs) ++ subsets xs
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
def powerSet(s):
sets = [[]]
for i in s:
newsets = []
for k in sets:
newsets.append(k+[i])
sets += newsets
return sets
Code first, for those who want a simple answer.
I have a good explanation here https://leetcode.com/problems/subsets/solutions/3138042/simple-python-solution/
But the short answer is that you start with the set of the empty set, i.e. "sets = [[]]". I recommend to put a print a statement under "for i in s" i.e. "print(sets)" and see that it doubles for each element i
This is wild because none of these answers actually provide the return of an actual Python set. Here is a messy implementation that will give a powerset that actually is a 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([]) ])
I'd love to see a better implementation, though.
Here is my quick implementation utilizing combinations but using only built-ins.
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
All subsets in range n as set:
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 "{}")
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)
A variation of the question, is an exercise I see on the book "Discovering Computer Science: Interdisciplinary Problems, Principles, and Python Programming. 2015 edition". In that exercise 10.2.11, the input is just an integer number, and the output should be the power sets. Here is my recursive solution (not using anything else but basic 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))
And the output is
[[], [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]]
Number of sublists: 16
I hadn't come across the more_itertools.powerset function and would recommend using that. I also recommend not using the default ordering of the output from itertools.combinations, often instead you want to minimise the distance between the positions and sort the subsets of items with shorter distance between them above/before the items with larger distance between them.
The itertools recipes page shows it uses chain.from_iterable
Note that the r here matches the standard notation for the lower part of a binomial coefficient, the s is usually referred to as n in mathematics texts and on calculators (“n Choose 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))
The other examples here give the powerset of [1,2,3,4] in such a way that the 2-tuples are listed in "lexicographic" order (when we print the numbers as integers). If I write the distance between the numbers alongside it (i.e. the difference), it shows my point:
12 ⇒ 1
13 ⇒ 2
14 ⇒ 3
23 ⇒ 1
24 ⇒ 2
34 ⇒ 1
The correct order for subsets should be the order which 'exhausts' the minimal distance first, like so:
12 ⇒ 1
23 ⇒ 1
34 ⇒ 1
13 ⇒ 2
24 ⇒ 2
14 ⇒ 3
Using numbers here makes this ordering look 'wrong', but consider for example the letters ["a","b","c","d"] it is clearer why this might be useful to obtain the powerset in this order:
ab ⇒ 1
bc ⇒ 1
cd ⇒ 1
ac ⇒ 2
bd ⇒ 2
ad ⇒ 3
This effect is more pronounced with more items, and for my purposes it makes the difference between being able to describe the ranges of the indexes of the powerset meaningfully.
(There is a lot written on Gray codes etc. for the output order of algorithms in combinatorics, I don't see it as a side issue).
I actually just wrote a fairly involved program which used this fast integer partition code to output the values in the proper order, but then I discovered more_itertools.powerset and for most uses it's probably fine to just use that function like so:
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)
I wrote some more involved code which will print the powerset nicely (see the repo for pretty printing functions I've not included here: print_partitions, print_partitions_by_length, and pprint_tuple).
Repo: ordered-powerset, specifically pset_partitions.py
This is all pretty simple, but still might be useful if you want some code that'll let you get straight to accessing the different levels of the powerset:
from itertools import permutations as permute
from numpy import cumsum
# http://jeromekelleher.net/generating-integer-partitions.html
# via
# https://stackoverflow.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://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
As an example, I wrote a CLI demo program which takes a string as a command line argument:
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
Here it is my solutions, it is similar (conceptually) with the solution of lmiguelvargasf.
Let me say that
-[math item] by defintion the powerset do contain the empty set
-[personal taste] and also that I don't like using frozenset.
So the input is a list and the output will be a list of lists. The function could close earlier, but I like the element of the power set to be order lexicographically, that essentially means nicely.
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
itertools.permutations generates where its elements are treated as unique based on their position, not on their value. So basically I want to avoid duplicates like this:
>>> list(itertools.permutations([1, 1, 1]))
[(1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1)]
Filtering afterwards is not possible because the amount of permutations is too large in my case.
Does anybody know of a suitable algorithm for this?
Thank you very much!
EDIT:
What I basically want is the following:
x = itertools.product((0, 1, 'x'), repeat=X)
x = sorted(x, key=functools.partial(count_elements, elem='x'))
which is not possible because sorted creates a list and the output of itertools.product is too large.
Sorry, I should have described the actual problem.
class unique_element:
def __init__(self,value,occurrences):
self.value = value
self.occurrences = occurrences
def perm_unique(elements):
eset=set(elements)
listunique = [unique_element(i,elements.count(i)) for i in eset]
u=len(elements)
return perm_unique_helper(listunique,[0]*u,u-1)
def perm_unique_helper(listunique,result_list,d):
if d < 0:
yield tuple(result_list)
else:
for i in listunique:
if i.occurrences > 0:
result_list[d]=i.value
i.occurrences-=1
for g in perm_unique_helper(listunique,result_list,d-1):
yield g
i.occurrences+=1
a = list(perm_unique([1,1,2]))
print(a)
result:
[(2, 1, 1), (1, 2, 1), (1, 1, 2)]
EDIT (how this works):
I rewrote the above program to be longer but more readable.
I usually have a hard time explaining how something works, but let me try.
In order to understand how this works, you have to understand a similar but simpler program that would yield all permutations with repetitions.
def permutations_with_replacement(elements,n):
return permutations_helper(elements,[0]*n,n-1)#this is generator
def permutations_helper(elements,result_list,d):
if d<0:
yield tuple(result_list)
else:
for i in elements:
result_list[d]=i
all_permutations = permutations_helper(elements,result_list,d-1)#this is generator
for g in all_permutations:
yield g
This program is obviously much simpler:
d stands for depth in permutations_helper and has two functions. One function is the stopping condition of our recursive algorithm, and the other is for the result list that is passed around.
Instead of returning each result, we yield it. If there were no function/operator yield we would have to push the result in some queue at the point of the stopping condition. But this way, once the stopping condition is met, the result is propagated through all stacks up to the caller. That is the purpose of
for g in perm_unique_helper(listunique,result_list,d-1): yield g
so each result is propagated up to caller.
Back to the original program:
we have a list of unique elements. Before we can use each element, we have to check how many of them are still available to push onto result_list. Working with this program is very similar to permutations_with_replacement. The difference is that each element cannot be repeated more times than it is in perm_unique_helper.
Because sometimes new questions are marked as duplicates and their authors are referred to this question it may be important to mention that sympy has an iterator for this purpose.
>>> from sympy.utilities.iterables import multiset_permutations
>>> list(multiset_permutations([1,1,1]))
[[1, 1, 1]]
>>> list(multiset_permutations([1,1,2]))
[[1, 1, 2], [1, 2, 1], [2, 1, 1]]
This relies on the implementation detail that any permutation of a sorted iterable are in sorted order unless they are duplicates of prior permutations.
from itertools import permutations
def unique_permutations(iterable, r=None):
previous = tuple()
for p in permutations(sorted(iterable), r):
if p > previous:
previous = p
yield p
for p in unique_permutations('cabcab', 2):
print p
gives
('a', 'a')
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'b')
('b', 'c')
('c', 'a')
('c', 'b')
('c', 'c')
Roughly as fast as Luka Rahne's answer, but shorter & simpler, IMHO.
def unique_permutations(elements):
if len(elements) == 1:
yield (elements[0],)
else:
unique_elements = set(elements)
for first_element in unique_elements:
remaining_elements = list(elements)
remaining_elements.remove(first_element)
for sub_permutation in unique_permutations(remaining_elements):
yield (first_element,) + sub_permutation
>>> list(unique_permutations((1,2,3,1)))
[(1, 1, 2, 3), (1, 1, 3, 2), (1, 2, 1, 3), ... , (3, 1, 2, 1), (3, 2, 1, 1)]
It works recursively by setting the first element (iterating through all unique elements), and iterating through the permutations for all remaining elements.
Let's go through the unique_permutations of (1,2,3,1) to see how it works:
unique_elements are 1,2,3
Let's iterate through them: first_element starts with 1.
remaining_elements are [2,3,1] (ie. 1,2,3,1 minus the first 1)
We iterate (recursively) through the permutations of the remaining elements: (1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)
For each sub_permutation, we insert the first_element: (1,1,2,3), (1,1,3,2), ... and yield the result.
Now we iterate to first_element = 2, and do the same as above.
remaining_elements are [1,3,1] (ie. 1,2,3,1 minus the first 2)
We iterate through the permutations of the remaining elements: (1, 1, 3), (1, 3, 1), (3, 1, 1)
For each sub_permutation, we insert the first_element: (2, 1, 1, 3), (2, 1, 3, 1), (2, 3, 1, 1)... and yield the result.
Finally, we do the same with first_element = 3.
You could try using set:
>>> list(itertools.permutations(set([1,1,2,2])))
[(1, 2), (2, 1)]
The call to set removed duplicates
A naive approach might be to take the set of the permutations:
list(set(it.permutations([1, 1, 1])))
# [(1, 1, 1)]
However, this technique wastefully computes replicate permutations and discards them. A more efficient approach would be more_itertools.distinct_permutations, a third-party tool.
Code
import itertools as it
import more_itertools as mit
list(mit.distinct_permutations([1, 1, 1]))
# [(1, 1, 1)]
Performance
Using a larger iterable, we will compare the performances between the naive and third-party techniques.
iterable = [1, 1, 1, 1, 1, 1]
len(list(it.permutations(iterable)))
# 720
%timeit -n 10000 list(set(it.permutations(iterable)))
# 10000 loops, best of 3: 111 µs per loop
%timeit -n 10000 list(mit.distinct_permutations(iterable))
# 10000 loops, best of 3: 16.7 µs per loop
We see more_itertools.distinct_permutations is an order of magnitude faster.
Details
From the source, a recursion algorithm (as seen in the accepted answer) is used to compute distinct permutations, thereby obviating wasteful computations. See the source code for more details.
This is my solution with 10 lines:
class Solution(object):
def permute_unique(self, nums):
perms = [[]]
for n in nums:
new_perm = []
for perm in perms:
for i in range(len(perm) + 1):
new_perm.append(perm[:i] + [n] + perm[i:])
# handle duplication
if i < len(perm) and perm[i] == n: break
perms = new_perm
return perms
if __name__ == '__main__':
s = Solution()
print s.permute_unique([1, 1, 1])
print s.permute_unique([1, 2, 1])
print s.permute_unique([1, 2, 3])
--- Result ----
[[1, 1, 1]]
[[1, 2, 1], [2, 1, 1], [1, 1, 2]]
[[3, 2, 1], [2, 3, 1], [2, 1, 3], [3, 1, 2], [1, 3, 2], [1, 2, 3]]
Here is a recursive solution to the problem.
def permutation(num_array):
res=[]
if len(num_array) <= 1:
return [num_array]
for num in set(num_array):
temp_array = num_array.copy()
temp_array.remove(num)
res += [[num] + perm for perm in permutation(temp_array)]
return res
arr=[1,2,2]
print(permutation(arr))
The best solution to this problem I have seen uses Knuth's "Algorithm L" (as noted previously by Gerrat in the comments to the original post):
http://stackoverflow.com/questions/12836385/how-can-i-interleave-or-create-unique-permutations-of-two-stings-without-recurs/12837695
Some timings:
Sorting [1]*12+[0]*12 (2,704,156 unique permutations):
Algorithm L → 2.43 s
Luke Rahne's solution → 8.56 s
scipy.multiset_permutations() → 16.8 s
To generate unique permutations of ["A","B","C","D"] I use the following:
from itertools import combinations,chain
l = ["A","B","C","D"]
combs = (combinations(l, r) for r in range(1, len(l) + 1))
list_combinations = list(chain.from_iterable(combs))
Which generates:
[('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')]
Notice, duplicates are not created (e.g. items in combination with D are not generated, as they already exist).
Example: This can then be used in generating terms of higher or lower order for OLS models via data in a Pandas dataframe.
import statsmodels.formula.api as smf
import pandas as pd
# create some data
pd_dataframe = pd.Dataframe(somedata)
response_column = "Y"
# generate combinations of column/variable names
l = [col for col in pd_dataframe.columns if col!=response_column]
combs = (combinations(l, r) for r in range(1, len(l) + 1))
list_combinations = list(chain.from_iterable(combs))
# generate OLS input string
formula_base = '{} ~ '.format(response_column)
list_for_ols = [":".join(list(item)) for item in list_combinations]
string_for_ols = formula_base + ' + '.join(list_for_ols)
Creates...
Y ~ 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'
Which can then be piped to your OLS regression
model = smf.ols(string_for_ols, pd_dataframe).fit()
model.summary()
It sound like you are looking for itertools.combinations() docs.python.org
list(itertools.combinations([1, 1, 1],3))
[(1, 1, 1)]
Bumped into this question while looking for something myself !
Here's what I did:
def dont_repeat(x=[0,1,1,2]): # Pass a list
from itertools import permutations as per
uniq_set = set()
for byt_grp in per(x, 4):
if byt_grp not in uniq_set:
yield byt_grp
uniq_set.update([byt_grp])
print uniq_set
for i in dont_repeat(): print i
(0, 1, 1, 2)
(0, 1, 2, 1)
(0, 2, 1, 1)
(1, 0, 1, 2)
(1, 0, 2, 1)
(1, 1, 0, 2)
(1, 1, 2, 0)
(1, 2, 0, 1)
(1, 2, 1, 0)
(2, 0, 1, 1)
(2, 1, 0, 1)
(2, 1, 1, 0)
set([(0, 1, 1, 2), (1, 0, 1, 2), (2, 1, 0, 1), (1, 2, 0, 1), (0, 1, 2, 1), (0, 2, 1, 1), (1, 1, 2, 0), (1, 2, 1, 0), (2, 1, 1, 0), (1, 0, 2, 1), (2, 0, 1, 1), (1, 1, 0, 2)])
Basically, make a set and keep adding to it. Better than making lists etc. that take too much memory..
Hope it helps the next person looking out :-) Comment out the set 'update' in the function to see the difference.
You can make a function that uses collections.Counter to get unique items and their counts from the given sequence, and uses itertools.combinations to pick combinations of indices for each unique item in each recursive call, and map the indices back to a list when all indices are picked:
from collections import Counter
from itertools import combinations
def unique_permutations(seq):
def index_permutations(counts, index_pool):
if not counts:
yield {}
return
(item, count), *rest = counts.items()
rest = dict(rest)
for indices in combinations(index_pool, count):
mapping = dict.fromkeys(indices, item)
for others in index_permutations(rest, index_pool.difference(indices)):
yield {**mapping, **others}
indices = set(range(len(seq)))
for mapping in index_permutations(Counter(seq), indices):
yield [mapping[i] for i in indices]
so that [''.join(i) for i in unique_permutations('moon')] returns:
['moon', 'mono', 'mnoo', 'omon', 'omno', 'nmoo', 'oomn', 'onmo', 'nomo', 'oonm', 'onom', 'noom']
This is my attempt without resorting to set / dict, as a generator using recursion, but using string as input. Output is also ordered in natural order:
def perm_helper(head: str, tail: str):
if len(tail) == 0:
yield head
else:
last_c = None
for index, c in enumerate(tail):
if last_c != c:
last_c = c
yield from perm_helper(
head + c, tail[:index] + tail[index + 1:]
)
def perm_generator(word):
yield from perm_helper("", sorted(word))
example:
from itertools import takewhile
word = "POOL"
list(takewhile(lambda w: w != word, (x for x in perm_generator(word))))
# output
# ['LOOP', 'LOPO', 'LPOO', 'OLOP', 'OLPO', 'OOLP', 'OOPL', 'OPLO', 'OPOL', 'PLOO', 'POLO']
ans=[]
def fn(a, size):
if (size == 1):
if a.copy() not in ans:
ans.append(a.copy())
return
for i in range(size):
fn(a,size-1);
if size&1:
a[0], a[size-1] = a[size-1],a[0]
else:
a[i], a[size-1] = a[size-1],a[i]
https://www.geeksforgeeks.org/heaps-algorithm-for-generating-permutations/
Came across this the other day while working on a problem of my own. I like Luka Rahne's approach, but I thought that using the Counter class in the collections library seemed like a modest improvement. Here's my code:
def unique_permutations(elements):
"Returns a list of lists; each sublist is a unique permutations of elements."
ctr = collections.Counter(elements)
# Base case with one element: just return the element
if len(ctr.keys())==1 and ctr[ctr.keys()[0]] == 1:
return [[ctr.keys()[0]]]
perms = []
# For each counter key, find the unique permutations of the set with
# one member of that key removed, and append the key to the front of
# each of those permutations.
for k in ctr.keys():
ctr_k = ctr.copy()
ctr_k[k] -= 1
if ctr_k[k]==0:
ctr_k.pop(k)
perms_k = [[k] + p for p in unique_permutations(ctr_k)]
perms.extend(perms_k)
return perms
This code returns each permutation as a list. If you feed it a string, it'll give you a list of permutations where each one is a list of characters. If you want the output as a list of strings instead (for example, if you're a terrible person and you want to abuse my code to help you cheat in Scrabble), just do the following:
[''.join(perm) for perm in unique_permutations('abunchofletters')]
I came up with a very suitable implementation using itertools.product in this case (this is an implementation where you want all combinations
unique_perm_list = [''.join(p) for p in itertools.product(['0', '1'], repeat = X) if ''.join(p).count() == somenumber]
this is essentially a combination (n over k) with n = X and somenumber = k
itertools.product() iterates from k = 0 to k = X subsequent filtering with count ensures that just the permutations with the right number of ones are cast into a list. you can easily see that it works when you calculate n over k and compare it to the len(unique_perm_list)
Adapted to remove recursion, use a dictionary and numba for high performance but not using yield/generator style so memory usage is not limited:
import numba
#numba.njit
def perm_unique_fast(elements): #memory usage too high for large permutations
eset = set(elements)
dictunique = dict()
for i in eset: dictunique[i] = elements.count(i)
result_list = numba.typed.List()
u = len(elements)
for _ in range(u): result_list.append(0)
s = numba.typed.List()
results = numba.typed.List()
d = u
while True:
if d > 0:
for i in dictunique:
if dictunique[i] > 0: s.append((i, d - 1))
i, d = s.pop()
if d == -1:
dictunique[i] += 1
if len(s) == 0: break
continue
result_list[d] = i
if d == 0: results.append(result_list[:])
dictunique[i] -= 1
s.append((i, -1))
return results
import timeit
l = [2, 2, 3, 3, 4, 4, 5, 5, 6, 6]
%timeit list(perm_unique(l))
#377 ms ± 26 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
ltyp = numba.typed.List()
for x in l: ltyp.append(x)
%timeit perm_unique_fast(ltyp)
#293 ms ± 3.37 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
assert list(sorted(perm_unique(l))) == list(sorted([tuple(x) for x in perm_unique_fast(ltyp)]))
About 30% faster but still suffers a bit due to list copying and management.
Alternatively without numba but still without recursion and using a generator to avoid memory issues:
def perm_unique_fast_gen(elements):
eset = set(elements)
dictunique = dict()
for i in eset: dictunique[i] = elements.count(i)
result_list = list() #numba.typed.List()
u = len(elements)
for _ in range(u): result_list.append(0)
s = list()
d = u
while True:
if d > 0:
for i in dictunique:
if dictunique[i] > 0: s.append((i, d - 1))
i, d = s.pop()
if d == -1:
dictunique[i] += 1
if len(s) == 0: break
continue
result_list[d] = i
if d == 0: yield result_list
dictunique[i] -= 1
s.append((i, -1))
May be we can use set here to obtain unique permutations
import itertools
print('unique perms >> ', set(itertools.permutations(A)))
What about
np.unique(itertools.permutations([1, 1, 1]))
The problem is the permutations are now rows of a Numpy array, thus using more memory, but you can cycle through them as before
perms = np.unique(itertools.permutations([1, 1, 1]))
for p in perms:
print p
itertools.permutations generates where its elements are treated as unique based on their position, not on their value. So basically I want to avoid duplicates like this:
>>> list(itertools.permutations([1, 1, 1]))
[(1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1)]
Filtering afterwards is not possible because the amount of permutations is too large in my case.
Does anybody know of a suitable algorithm for this?
Thank you very much!
EDIT:
What I basically want is the following:
x = itertools.product((0, 1, 'x'), repeat=X)
x = sorted(x, key=functools.partial(count_elements, elem='x'))
which is not possible because sorted creates a list and the output of itertools.product is too large.
Sorry, I should have described the actual problem.
class unique_element:
def __init__(self,value,occurrences):
self.value = value
self.occurrences = occurrences
def perm_unique(elements):
eset=set(elements)
listunique = [unique_element(i,elements.count(i)) for i in eset]
u=len(elements)
return perm_unique_helper(listunique,[0]*u,u-1)
def perm_unique_helper(listunique,result_list,d):
if d < 0:
yield tuple(result_list)
else:
for i in listunique:
if i.occurrences > 0:
result_list[d]=i.value
i.occurrences-=1
for g in perm_unique_helper(listunique,result_list,d-1):
yield g
i.occurrences+=1
a = list(perm_unique([1,1,2]))
print(a)
result:
[(2, 1, 1), (1, 2, 1), (1, 1, 2)]
EDIT (how this works):
I rewrote the above program to be longer but more readable.
I usually have a hard time explaining how something works, but let me try.
In order to understand how this works, you have to understand a similar but simpler program that would yield all permutations with repetitions.
def permutations_with_replacement(elements,n):
return permutations_helper(elements,[0]*n,n-1)#this is generator
def permutations_helper(elements,result_list,d):
if d<0:
yield tuple(result_list)
else:
for i in elements:
result_list[d]=i
all_permutations = permutations_helper(elements,result_list,d-1)#this is generator
for g in all_permutations:
yield g
This program is obviously much simpler:
d stands for depth in permutations_helper and has two functions. One function is the stopping condition of our recursive algorithm, and the other is for the result list that is passed around.
Instead of returning each result, we yield it. If there were no function/operator yield we would have to push the result in some queue at the point of the stopping condition. But this way, once the stopping condition is met, the result is propagated through all stacks up to the caller. That is the purpose of
for g in perm_unique_helper(listunique,result_list,d-1): yield g
so each result is propagated up to caller.
Back to the original program:
we have a list of unique elements. Before we can use each element, we have to check how many of them are still available to push onto result_list. Working with this program is very similar to permutations_with_replacement. The difference is that each element cannot be repeated more times than it is in perm_unique_helper.
Because sometimes new questions are marked as duplicates and their authors are referred to this question it may be important to mention that sympy has an iterator for this purpose.
>>> from sympy.utilities.iterables import multiset_permutations
>>> list(multiset_permutations([1,1,1]))
[[1, 1, 1]]
>>> list(multiset_permutations([1,1,2]))
[[1, 1, 2], [1, 2, 1], [2, 1, 1]]
This relies on the implementation detail that any permutation of a sorted iterable are in sorted order unless they are duplicates of prior permutations.
from itertools import permutations
def unique_permutations(iterable, r=None):
previous = tuple()
for p in permutations(sorted(iterable), r):
if p > previous:
previous = p
yield p
for p in unique_permutations('cabcab', 2):
print p
gives
('a', 'a')
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'b')
('b', 'c')
('c', 'a')
('c', 'b')
('c', 'c')
Roughly as fast as Luka Rahne's answer, but shorter & simpler, IMHO.
def unique_permutations(elements):
if len(elements) == 1:
yield (elements[0],)
else:
unique_elements = set(elements)
for first_element in unique_elements:
remaining_elements = list(elements)
remaining_elements.remove(first_element)
for sub_permutation in unique_permutations(remaining_elements):
yield (first_element,) + sub_permutation
>>> list(unique_permutations((1,2,3,1)))
[(1, 1, 2, 3), (1, 1, 3, 2), (1, 2, 1, 3), ... , (3, 1, 2, 1), (3, 2, 1, 1)]
It works recursively by setting the first element (iterating through all unique elements), and iterating through the permutations for all remaining elements.
Let's go through the unique_permutations of (1,2,3,1) to see how it works:
unique_elements are 1,2,3
Let's iterate through them: first_element starts with 1.
remaining_elements are [2,3,1] (ie. 1,2,3,1 minus the first 1)
We iterate (recursively) through the permutations of the remaining elements: (1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)
For each sub_permutation, we insert the first_element: (1,1,2,3), (1,1,3,2), ... and yield the result.
Now we iterate to first_element = 2, and do the same as above.
remaining_elements are [1,3,1] (ie. 1,2,3,1 minus the first 2)
We iterate through the permutations of the remaining elements: (1, 1, 3), (1, 3, 1), (3, 1, 1)
For each sub_permutation, we insert the first_element: (2, 1, 1, 3), (2, 1, 3, 1), (2, 3, 1, 1)... and yield the result.
Finally, we do the same with first_element = 3.
You could try using set:
>>> list(itertools.permutations(set([1,1,2,2])))
[(1, 2), (2, 1)]
The call to set removed duplicates
A naive approach might be to take the set of the permutations:
list(set(it.permutations([1, 1, 1])))
# [(1, 1, 1)]
However, this technique wastefully computes replicate permutations and discards them. A more efficient approach would be more_itertools.distinct_permutations, a third-party tool.
Code
import itertools as it
import more_itertools as mit
list(mit.distinct_permutations([1, 1, 1]))
# [(1, 1, 1)]
Performance
Using a larger iterable, we will compare the performances between the naive and third-party techniques.
iterable = [1, 1, 1, 1, 1, 1]
len(list(it.permutations(iterable)))
# 720
%timeit -n 10000 list(set(it.permutations(iterable)))
# 10000 loops, best of 3: 111 µs per loop
%timeit -n 10000 list(mit.distinct_permutations(iterable))
# 10000 loops, best of 3: 16.7 µs per loop
We see more_itertools.distinct_permutations is an order of magnitude faster.
Details
From the source, a recursion algorithm (as seen in the accepted answer) is used to compute distinct permutations, thereby obviating wasteful computations. See the source code for more details.
This is my solution with 10 lines:
class Solution(object):
def permute_unique(self, nums):
perms = [[]]
for n in nums:
new_perm = []
for perm in perms:
for i in range(len(perm) + 1):
new_perm.append(perm[:i] + [n] + perm[i:])
# handle duplication
if i < len(perm) and perm[i] == n: break
perms = new_perm
return perms
if __name__ == '__main__':
s = Solution()
print s.permute_unique([1, 1, 1])
print s.permute_unique([1, 2, 1])
print s.permute_unique([1, 2, 3])
--- Result ----
[[1, 1, 1]]
[[1, 2, 1], [2, 1, 1], [1, 1, 2]]
[[3, 2, 1], [2, 3, 1], [2, 1, 3], [3, 1, 2], [1, 3, 2], [1, 2, 3]]
Here is a recursive solution to the problem.
def permutation(num_array):
res=[]
if len(num_array) <= 1:
return [num_array]
for num in set(num_array):
temp_array = num_array.copy()
temp_array.remove(num)
res += [[num] + perm for perm in permutation(temp_array)]
return res
arr=[1,2,2]
print(permutation(arr))
The best solution to this problem I have seen uses Knuth's "Algorithm L" (as noted previously by Gerrat in the comments to the original post):
http://stackoverflow.com/questions/12836385/how-can-i-interleave-or-create-unique-permutations-of-two-stings-without-recurs/12837695
Some timings:
Sorting [1]*12+[0]*12 (2,704,156 unique permutations):
Algorithm L → 2.43 s
Luke Rahne's solution → 8.56 s
scipy.multiset_permutations() → 16.8 s
To generate unique permutations of ["A","B","C","D"] I use the following:
from itertools import combinations,chain
l = ["A","B","C","D"]
combs = (combinations(l, r) for r in range(1, len(l) + 1))
list_combinations = list(chain.from_iterable(combs))
Which generates:
[('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')]
Notice, duplicates are not created (e.g. items in combination with D are not generated, as they already exist).
Example: This can then be used in generating terms of higher or lower order for OLS models via data in a Pandas dataframe.
import statsmodels.formula.api as smf
import pandas as pd
# create some data
pd_dataframe = pd.Dataframe(somedata)
response_column = "Y"
# generate combinations of column/variable names
l = [col for col in pd_dataframe.columns if col!=response_column]
combs = (combinations(l, r) for r in range(1, len(l) + 1))
list_combinations = list(chain.from_iterable(combs))
# generate OLS input string
formula_base = '{} ~ '.format(response_column)
list_for_ols = [":".join(list(item)) for item in list_combinations]
string_for_ols = formula_base + ' + '.join(list_for_ols)
Creates...
Y ~ 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'
Which can then be piped to your OLS regression
model = smf.ols(string_for_ols, pd_dataframe).fit()
model.summary()
It sound like you are looking for itertools.combinations() docs.python.org
list(itertools.combinations([1, 1, 1],3))
[(1, 1, 1)]
Bumped into this question while looking for something myself !
Here's what I did:
def dont_repeat(x=[0,1,1,2]): # Pass a list
from itertools import permutations as per
uniq_set = set()
for byt_grp in per(x, 4):
if byt_grp not in uniq_set:
yield byt_grp
uniq_set.update([byt_grp])
print uniq_set
for i in dont_repeat(): print i
(0, 1, 1, 2)
(0, 1, 2, 1)
(0, 2, 1, 1)
(1, 0, 1, 2)
(1, 0, 2, 1)
(1, 1, 0, 2)
(1, 1, 2, 0)
(1, 2, 0, 1)
(1, 2, 1, 0)
(2, 0, 1, 1)
(2, 1, 0, 1)
(2, 1, 1, 0)
set([(0, 1, 1, 2), (1, 0, 1, 2), (2, 1, 0, 1), (1, 2, 0, 1), (0, 1, 2, 1), (0, 2, 1, 1), (1, 1, 2, 0), (1, 2, 1, 0), (2, 1, 1, 0), (1, 0, 2, 1), (2, 0, 1, 1), (1, 1, 0, 2)])
Basically, make a set and keep adding to it. Better than making lists etc. that take too much memory..
Hope it helps the next person looking out :-) Comment out the set 'update' in the function to see the difference.
You can make a function that uses collections.Counter to get unique items and their counts from the given sequence, and uses itertools.combinations to pick combinations of indices for each unique item in each recursive call, and map the indices back to a list when all indices are picked:
from collections import Counter
from itertools import combinations
def unique_permutations(seq):
def index_permutations(counts, index_pool):
if not counts:
yield {}
return
(item, count), *rest = counts.items()
rest = dict(rest)
for indices in combinations(index_pool, count):
mapping = dict.fromkeys(indices, item)
for others in index_permutations(rest, index_pool.difference(indices)):
yield {**mapping, **others}
indices = set(range(len(seq)))
for mapping in index_permutations(Counter(seq), indices):
yield [mapping[i] for i in indices]
so that [''.join(i) for i in unique_permutations('moon')] returns:
['moon', 'mono', 'mnoo', 'omon', 'omno', 'nmoo', 'oomn', 'onmo', 'nomo', 'oonm', 'onom', 'noom']
This is my attempt without resorting to set / dict, as a generator using recursion, but using string as input. Output is also ordered in natural order:
def perm_helper(head: str, tail: str):
if len(tail) == 0:
yield head
else:
last_c = None
for index, c in enumerate(tail):
if last_c != c:
last_c = c
yield from perm_helper(
head + c, tail[:index] + tail[index + 1:]
)
def perm_generator(word):
yield from perm_helper("", sorted(word))
example:
from itertools import takewhile
word = "POOL"
list(takewhile(lambda w: w != word, (x for x in perm_generator(word))))
# output
# ['LOOP', 'LOPO', 'LPOO', 'OLOP', 'OLPO', 'OOLP', 'OOPL', 'OPLO', 'OPOL', 'PLOO', 'POLO']
ans=[]
def fn(a, size):
if (size == 1):
if a.copy() not in ans:
ans.append(a.copy())
return
for i in range(size):
fn(a,size-1);
if size&1:
a[0], a[size-1] = a[size-1],a[0]
else:
a[i], a[size-1] = a[size-1],a[i]
https://www.geeksforgeeks.org/heaps-algorithm-for-generating-permutations/
Came across this the other day while working on a problem of my own. I like Luka Rahne's approach, but I thought that using the Counter class in the collections library seemed like a modest improvement. Here's my code:
def unique_permutations(elements):
"Returns a list of lists; each sublist is a unique permutations of elements."
ctr = collections.Counter(elements)
# Base case with one element: just return the element
if len(ctr.keys())==1 and ctr[ctr.keys()[0]] == 1:
return [[ctr.keys()[0]]]
perms = []
# For each counter key, find the unique permutations of the set with
# one member of that key removed, and append the key to the front of
# each of those permutations.
for k in ctr.keys():
ctr_k = ctr.copy()
ctr_k[k] -= 1
if ctr_k[k]==0:
ctr_k.pop(k)
perms_k = [[k] + p for p in unique_permutations(ctr_k)]
perms.extend(perms_k)
return perms
This code returns each permutation as a list. If you feed it a string, it'll give you a list of permutations where each one is a list of characters. If you want the output as a list of strings instead (for example, if you're a terrible person and you want to abuse my code to help you cheat in Scrabble), just do the following:
[''.join(perm) for perm in unique_permutations('abunchofletters')]
I came up with a very suitable implementation using itertools.product in this case (this is an implementation where you want all combinations
unique_perm_list = [''.join(p) for p in itertools.product(['0', '1'], repeat = X) if ''.join(p).count() == somenumber]
this is essentially a combination (n over k) with n = X and somenumber = k
itertools.product() iterates from k = 0 to k = X subsequent filtering with count ensures that just the permutations with the right number of ones are cast into a list. you can easily see that it works when you calculate n over k and compare it to the len(unique_perm_list)
Adapted to remove recursion, use a dictionary and numba for high performance but not using yield/generator style so memory usage is not limited:
import numba
#numba.njit
def perm_unique_fast(elements): #memory usage too high for large permutations
eset = set(elements)
dictunique = dict()
for i in eset: dictunique[i] = elements.count(i)
result_list = numba.typed.List()
u = len(elements)
for _ in range(u): result_list.append(0)
s = numba.typed.List()
results = numba.typed.List()
d = u
while True:
if d > 0:
for i in dictunique:
if dictunique[i] > 0: s.append((i, d - 1))
i, d = s.pop()
if d == -1:
dictunique[i] += 1
if len(s) == 0: break
continue
result_list[d] = i
if d == 0: results.append(result_list[:])
dictunique[i] -= 1
s.append((i, -1))
return results
import timeit
l = [2, 2, 3, 3, 4, 4, 5, 5, 6, 6]
%timeit list(perm_unique(l))
#377 ms ± 26 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
ltyp = numba.typed.List()
for x in l: ltyp.append(x)
%timeit perm_unique_fast(ltyp)
#293 ms ± 3.37 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
assert list(sorted(perm_unique(l))) == list(sorted([tuple(x) for x in perm_unique_fast(ltyp)]))
About 30% faster but still suffers a bit due to list copying and management.
Alternatively without numba but still without recursion and using a generator to avoid memory issues:
def perm_unique_fast_gen(elements):
eset = set(elements)
dictunique = dict()
for i in eset: dictunique[i] = elements.count(i)
result_list = list() #numba.typed.List()
u = len(elements)
for _ in range(u): result_list.append(0)
s = list()
d = u
while True:
if d > 0:
for i in dictunique:
if dictunique[i] > 0: s.append((i, d - 1))
i, d = s.pop()
if d == -1:
dictunique[i] += 1
if len(s) == 0: break
continue
result_list[d] = i
if d == 0: yield result_list
dictunique[i] -= 1
s.append((i, -1))
May be we can use set here to obtain unique permutations
import itertools
print('unique perms >> ', set(itertools.permutations(A)))
What about
np.unique(itertools.permutations([1, 1, 1]))
The problem is the permutations are now rows of a Numpy array, thus using more memory, but you can cycle through them as before
perms = np.unique(itertools.permutations([1, 1, 1]))
for p in perms:
print p
Given a set
{0, 1, 2, 3}
How can I produce the subsets:
[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}]
The Python itertools page has exactly a powerset recipe for this:
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))
Output:
>>> 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')]
If you don't like that empty tuple at the beginning, you can just change the range statement to range(1, len(s)+1) to avoid a 0-length combination.
Here is more code for a powerset. This is written from scratch:
>>> 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's comment is applicable here: "If you don't like that empty tuple at the beginning, on."you can just change the range statement to range(1, len(s)+1) to avoid a 0-length combination", except in my case you change for i in range(1 << x) to for i in range(1, 1 << x).
Returning to this years later, I'd now write it like this:
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]
And then the test code would look like this, say:
print(list(powerset([4, 5, 6])))
Using yield means that you do not need to calculate all results in a single piece of memory. Precalculating the masks outside the main loop is assumed to be a worthwhile optimization.
If you're looking for a quick answer, I just searched "python power set" on google and came up with this: Python Power Set Generator
Here's a copy-paste from the code in that page:
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
This can be used like this:
l = [1, 2, 3, 4]
r = [x for x in powerset(l)]
Now r is a list of all the elements you wanted, and can be sorted and printed:
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]]
Use function powerset() from package more_itertools.
Yields all possible subsets of the iterable
>>> list(powerset([1, 2, 3]))
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
If you want sets, use:
list(map(set, powerset(iterable)))
I have found the following algorithm very clear and simple:
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
Another way one can generate the powerset is by generating all binary numbers that have n bits. As a power set the amount of number with n digits is 2 ^ n. The principle of this algorithm is that an element could be present or not in a subset as a binary digit could be one or zero but not both.
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
I found both algorithms when I was taking MITx: 6.00.2x Introduction to Computational Thinking and Data Science, and I consider it is one of the easiest algorithms to understand I have seen.
from functools import reduce
def powerset(lst):
return reduce(lambda result, x: result + [subset + [x] for subset in result],
lst, [[]])
There is a refinement of 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
TL;DR (go directly to Simplification)
I know I have previously added an answer, but I really like my new implementation. I am taking a set as input, but it actually could be any iterable, and I am returning a set of sets which is the power set of the input. I like this approach because it is more aligned with the mathematical definition of power set (set of all subsets).
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
If you want exactly the output you posted in your answer use this:
>>> [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}]
Explanation
It is known that the number of elements of the power set is 2 ** len(A), so that could clearly be seen in the for loop.
I need to convert the input (ideally a set) into a list because by a set is a data structure of unique unordered elements, and the order will be crucial to generate the subsets.
selector is key in this algorithm. Note that selector has the same length as the input set, and to make this possible it is using an f-string with padding. Basically, this allows me to select the elements that will be added to each subset during each iteration. Let's say the input set has 3 elements {0, 1, 2}, so selector will take values between 0 and 7 (inclusive), which in binary are:
000 # 0
001 # 1
010 # 2
011 # 3
100 # 4
101 # 5
110 # 6
111 # 7
So, each bit could serve as an indicator if an element of the original set should be added or not. Look at the binary numbers, and just think of each number as an element of the super set in which 1 means that an element at index j should be added, and 0 means that this element should not be added.
I am using a set comprehension to generate a subset at each iteration, and I convert this subset into a frozenset so I can add it to ps (power set). Otherwise, I won't be able to add it because a set in Python consists only of immutable objects.
Simplification
You can simplify the code using some python comprehensions, so you can get rid of those for loops. You can also use zip to avoid using j index and the code will end up as the following:
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)
}
That's it. What I like of this algorithm is that is clearer and more intuitive than others because it looks quite magical to rely on itertools even though it works as expected.
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 to it
# effectively doubling the sets resulting in the 2^n sets in the powerset of s.
power_set=power_set+[list(sub_set)+[elem]]
return power_set
For example:
get_power_set([1,2,3])
yield
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
This can be done very naturally with 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}
I know this is too late
There are many other solutions already but still...
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
I just wanted to provide the most comprehensible solution, the anti code-golf version.
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
The results
All sets of length 0
[()]
All sets of length 1
[('x',), ('y',), ('z',)]
All sets of length 2
[('x', 'y'), ('x', 'z'), ('y', 'z')]
All sets of length 3
[('x', 'y', 'z')]
For more see the itertools docs, also the wikipedia entry on power sets
With empty set, which is part of all the subsets, you could use:
def subsets(iterable):
for n in range(len(iterable) + 1):
yield from combinations(iterable, n)
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))
Output:
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3)]
For sorted subsets, we can do:
# sorted subsets
print(sorted(subsets(a)))
Output:
[(), (1,), (1, 2), (1, 3), (2,), (2, 3), (3,)]
Just a quick power set refresher !
Power set of a set X, is simply the set of all subsets of X including
the empty set
Example set X = (a,b,c)
Power Set = { { a , b , c } , { a , b } , { a , c } , { b , c } , { a } , { b } , { c } , { } }
Here is another way of finding power set:
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]))
full credit to source
If you want any specific length of subsets you can do it like this:
from itertools import combinations
someSet = {0, 1, 2, 3}
([x for i in range(len(someSet)+1) for x in combinations(someSet,i)])
More generally for arbitary length subsets you can modify the range arugment. The output is
[(), (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)]
You can do it like this:
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]))
Output:
[[], [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]]
A simple way would be to harness the internal representation of integers under 2's complement arithmetic.
Binary representation of integers is as {000, 001, 010, 011, 100, 101, 110, 111} for numbers ranging from 0 to 7. For an integer counter value, considering 1 as inclusion of corresponding element in collection and '0' as exclusion we can generate subsets based on the counting sequence. Numbers have to be generated from 0 to pow(2,n) -1 where n is the length of array i.e. number of bits in binary representation.
A simple Subset Generator Function based on it can be written as below. It basically relies
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
and then it can be used as
def get_subsets(array):
powerset = []
for i in subsets(array):
powerser.append(i)
return powerset
Testing
Adding following in local file
if __name__ == '__main__':
sample = ['b', 'd', 'f']
for i in range(len(sample)):
print "Subsets for " , sample[i:], " are ", get_subsets(sample[i:])
gives following output
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']]
Almost all of these answers use list rather than set, which felt like a bit of a cheat to me. So, out of curiosity I tried to do a simple version truly on set and summarize for other "new to Python" folks.
I found there's a couple oddities in dealing with Python's set implementation. The main surprise to me was handling empty sets. This is in contrast to Ruby's Set implementation, where I can simply do Set[Set[]] and get a Set containing one empty Set, so I found it initially a little confusing.
To review, in doing powerset with sets, I encountered two problems:
set() takes an iterable, so set(set()) will return set() because the empty set iterable is empty (duh I guess :))
to get a set of sets, set({set()}) and set.add(set) won't work because set() isn't hashable
To solve both issues, I made use of frozenset(), which means I don't quite get what I want (type is literally set), but makes use of the overall 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
Below we get 2² (16) frozensets correctly as output:
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})}
As there's no way to have a set of sets in Python, if you want to turn these frozensets into sets, you'll have to map them back into a list (list(map(set, powerset(set([1,2,3,4]))))
) or modify the above.
Perhaps the question is getting old, but I hope my code will help someone.
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
Getting all the subsets with recursion. Crazy-ass one-liner
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 [[]]
Based on a Haskell solution
subsets :: [a] -> [[a]]
subsets [] = [[]]
subsets (x:xs) = map (x:) (subsets xs) ++ subsets xs
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
def powerSet(s):
sets = [[]]
for i in s:
newsets = []
for k in sets:
newsets.append(k+[i])
sets += newsets
return sets
Code first, for those who want a simple answer.
I have a good explanation here https://leetcode.com/problems/subsets/solutions/3138042/simple-python-solution/
But the short answer is that you start with the set of the empty set, i.e. "sets = [[]]". I recommend to put a print a statement under "for i in s" i.e. "print(sets)" and see that it doubles for each element i
This is wild because none of these answers actually provide the return of an actual Python set. Here is a messy implementation that will give a powerset that actually is a 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([]) ])
I'd love to see a better implementation, though.
Here is my quick implementation utilizing combinations but using only built-ins.
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
All subsets in range n as set:
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 "{}")
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)
A variation of the question, is an exercise I see on the book "Discovering Computer Science: Interdisciplinary Problems, Principles, and Python Programming. 2015 edition". In that exercise 10.2.11, the input is just an integer number, and the output should be the power sets. Here is my recursive solution (not using anything else but basic 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))
And the output is
[[], [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]]
Number of sublists: 16
I hadn't come across the more_itertools.powerset function and would recommend using that. I also recommend not using the default ordering of the output from itertools.combinations, often instead you want to minimise the distance between the positions and sort the subsets of items with shorter distance between them above/before the items with larger distance between them.
The itertools recipes page shows it uses chain.from_iterable
Note that the r here matches the standard notation for the lower part of a binomial coefficient, the s is usually referred to as n in mathematics texts and on calculators (“n Choose 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))
The other examples here give the powerset of [1,2,3,4] in such a way that the 2-tuples are listed in "lexicographic" order (when we print the numbers as integers). If I write the distance between the numbers alongside it (i.e. the difference), it shows my point:
12 ⇒ 1
13 ⇒ 2
14 ⇒ 3
23 ⇒ 1
24 ⇒ 2
34 ⇒ 1
The correct order for subsets should be the order which 'exhausts' the minimal distance first, like so:
12 ⇒ 1
23 ⇒ 1
34 ⇒ 1
13 ⇒ 2
24 ⇒ 2
14 ⇒ 3
Using numbers here makes this ordering look 'wrong', but consider for example the letters ["a","b","c","d"] it is clearer why this might be useful to obtain the powerset in this order:
ab ⇒ 1
bc ⇒ 1
cd ⇒ 1
ac ⇒ 2
bd ⇒ 2
ad ⇒ 3
This effect is more pronounced with more items, and for my purposes it makes the difference between being able to describe the ranges of the indexes of the powerset meaningfully.
(There is a lot written on Gray codes etc. for the output order of algorithms in combinatorics, I don't see it as a side issue).
I actually just wrote a fairly involved program which used this fast integer partition code to output the values in the proper order, but then I discovered more_itertools.powerset and for most uses it's probably fine to just use that function like so:
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)
I wrote some more involved code which will print the powerset nicely (see the repo for pretty printing functions I've not included here: print_partitions, print_partitions_by_length, and pprint_tuple).
Repo: ordered-powerset, specifically pset_partitions.py
This is all pretty simple, but still might be useful if you want some code that'll let you get straight to accessing the different levels of the powerset:
from itertools import permutations as permute
from numpy import cumsum
# http://jeromekelleher.net/generating-integer-partitions.html
# via
# https://stackoverflow.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://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
As an example, I wrote a CLI demo program which takes a string as a command line argument:
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
Here it is my solutions, it is similar (conceptually) with the solution of lmiguelvargasf.
Let me say that
-[math item] by defintion the powerset do contain the empty set
-[personal taste] and also that I don't like using frozenset.
So the input is a list and the output will be a list of lists. The function could close earlier, but I like the element of the power set to be order lexicographically, that essentially means nicely.
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
I have a list of numbers
l = [1,2,3,4,5]
and a list of tuples which describe which items should not be in the output together.
gl_distribute = [(1, 2), (1,4), (1, 5), (2, 3), (3, 4)]
the possible lists are
[1,3]
[2,4,5]
[3,5]
and I want my algorithm to give me the second one [2,4,5]
I was thinking to do it recursively.
In the first case (t1) I call my recursive algorithm with all the items except the 1st, and in the second case (t2) I call it again removing the pairs from gl_distribute where the 1st item appears.
Here is my algorithm
def check_distribute(items, distribute):
i = sorted(items[:])
d = distribute[:]
if not i:
return []
if not d:
return i
if len(remove_from_distribute(i, d)) == len(d):
return i
first = i[0]
rest = items[1:]
distr_without_first = remove_from_distribute([first], d)
t1 = check_distribute(rest, d)
t2 = check_distribute(rest, distr_without_first)
t2.append(first)
if len(t1) >= len(t2):
return t1
else:
return t2
The remove_from_distribute(items, distr_list) removes the pairs from distr_list that include any of the items in items.
def remove_from_distribute(items, distribute_list):
new_distr = distribute_list[:]
for item in items:
for pair in distribute_list:
x, y = pair
if x == item or y == item and pair in new_distr:
new_distr.remove((x,y))
if new_distr:
return new_distr
else:
return []
My output is [4, 5, 3, 2, 1] which obviously is not correct. Can you tell me what I am doing wrong here? Or can you give me a better way to approach this?
I will suggest an alternative approach.
Assuming your list and your distribution are sorted and your list is length of n, and your distribution is length of m.
First, create a list of two tuples with all valid combinations. This should be a O(n^2) solution.
Once you have the list, it's just a simple loop through the valid combination and find the longest list. There are probably some better solutions to further reduce the complexity.
Here are my sample codes:
def get_valid():
seq = [1, 2, 3, 4, 5]
gl_dist = [(1, 2), (1,4), (1, 5), (2, 3), (3, 4)]
gl_index = 0
valid = []
for i in xrange(len(seq)):
for j in xrange(i+1, len(seq)):
if gl_index < len(gl_dist):
if (seq[i], seq[j]) != gl_dist[gl_index] :
valid.append((seq[i], seq[j]))
else:
gl_index += 1
else:
valid.append((seq[i], seq[j]))
return valid
>>>> get_valid()
[(1, 3), (2, 4), (2, 5), (3, 5), (4, 5)]
def get_list():
total = get_valid()
start = total[0][0]
result = [start]
for i, j in total:
if i == start:
result.append(j)
else:
start = i
return_result = list(result)
result = [i, j]
yield return_result
yield list(result)
raise StopIteration
>>> list(get_list())
[[1, 3], [2, 4, 5], [3, 5], [4, 5]]
I am not sure I fully understand your output as I think 4,5 and 5,2 should be possible lists as they are not in the list of tuples:
If so you could use itertools to get the combinations and filter based on the gl_distribute list using sets to see if any two numbers in the different combinations in combs contains two elements that should not be together, then get the max
combs = (combinations(l,r) for r in range(2,len(l)))
final = []
for x in combs:
final += x
res = max(filter(lambda x: not any(len(set(x).intersection(s)) == 2 for s in gl_distribute),final),key=len)
print res
(2, 4, 5)