Generate sets of subsets plus unused elements - python

I want a python program that takes a list of integers and returns a list of lists of tuples that contain the subsets as well as the subsets with the rest of the values not used in the first subset, essentially getting all possibilities to combine the values in the list.
[1,2,3,4]
should return
[[(1,), (2,), (3,), (4,)], [(1,), (2,3,), (4,)], [(1,), (2,) (3,4,)], [(1,), (2,4,), (3,)],[(1,), (2,3,4,)], [(1,2,), (3,), (4,)], [(1,2,), (3,4)]...and so on]

Yes. This is surprisingly easy with library more_itertools. You can use the
for loop below to write your tuple level output and discard undesired
results.
import more_itertools
s1 = set([1,2,3,4])
subsubsets = more_itertools.set_partitions(s1)
print(*subsubsets)
subsubsets = more_itertools.set_partitions(s1)
for l in subsubsets:
if l1 != sum(len(k) for k in l):
print(f"{l} non-compliant")
break
Output (added extra linebreaks)
[[1, 2, 3, 4]]
[[1], [2, 3, 4]]
[[1, 2], [3, 4]]
[[2], [1, 3, 4]]
[[1, 2, 3], [4]]
[[2, 3], [1, 4]]
[[1, 3], [2, 4]]
[[3], [1, 2, 4]]
[[1], [2], [3, 4]]
[[1], [2, 3], [4]]
[[1], [3], [2, 4]]
[[1, 2], [3], [4]]
[[2], [1, 3], [4]]
[[2], [3], [1, 4]]
[[1], [2], [3], [4]]

Related

How to print all the unique combinations of a list?

list_a = [1, 2, 3]
I want to print all the unique combinations from the list like this
[ [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3] ]
Note: Without using any module
You need a variant of the powerset recipe from itertools:
from itertools import combinations, chain
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(1, len(s)+1))
list_a = [1, 2, 3]
out = list(map(list, powerset(list_a)))
output: [[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
If you don't want to use itertools you can quite easily reimplement combinations and chain using list comprehensions or functions.

Combinations using Recursion [duplicate]

I have the following python function to print all subsets of a list of numbers:
def subs(l):
if len(l) == 1:
return [l]
res = []
for sub in subs(l[0:-1]):
res.append(sub)
res.append([l[-1]])
res.append(sub+[l[-1]])
return res
li = [2, 3, 5, 8]
print(subs(li))
This returns:
[[2], [8], [2, 8], [5], [8], [5, 8], [2, 5], [8], [2, 5, 8], [3], [8], [3, 8], [5], [8], [5, 8], [3, 5], [8], [3, 5, 8], [2, 3], [8], [2, 3, 8], [5], [8], [5, 8], [2, 3, 5], [8], [2, 3, 5, 8]]
Which is not the expected answer. It looks like python takes the list l into the function by reference. So when I append l[-1], it appends the last element of original list, not the smaller list sent into the recursive method. Is there any way to solve this?
This could possibly be solved using tuples but I'm wondering if there is a solution using lists.
def subs(l):
if l == []:
return [[]]
x = subs(l[1:])
return x + [[l[0]] + y for y in x]
Results:
>>> print (subs([1, 2, 3]))
[[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
There is a convenient Python module to help:
import itertools
def subs(l):
res = []
for i in range(1, len(l) + 1):
for combo in itertools.combinations(l, i):
res.append(list(combo))
return res
The results are:
>>> subs([1,2,3])
[[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
Actually there is no problem with Python call by reference as I originally thought. In that case l[-1] would be 8 in all recursive calls. But l[-1] is 3, 5, 8 respectively in the recursive calls. This modified function solves the issue:
def subs(l):
if len(l) == 1:
return [l]
res = []
subsets = subs(l[0:-1])
res = res+subsets
res.append([l[-1]])
for sub in subsets:
res.append(sub+[l[-1]])
return res
returns:
[[2], [3], [2, 3], [5], [2, 5], [3, 5], [2, 3, 5], [8], [2, 8], [3, 8], [2, 3, 8], [5, 8], [2, 5, 8], [3, 5, 8], [2, 3, 5, 8]]
Improving on #Miguel Matos answer
def subsets(set_inp):
if set_inp == []:
return [[]]
x = subsets(set_inp[1:])
return sorted( x + [[set_inp[0]] + y for y in x])
print(subsets([1,2,3]))
using #Miguel Matos idea
we can get these in lexicographical order by,
def foo(l, p = [], d = {}):
if len(l)==0:
return [[]]
x = foo(l[:-1])
return x+ [[l[-1]] + y for y in x]
returns
[[], [1], [2], [2, 1], [3], [3, 1], [3, 2], [3, 2, 1], [4], [4, 1], [4, 2], [4, 2, 1], [4, 3], [4, 3, 1], [4, 3, 2], [4, 3, 2, 1]]
You can avoid using comprehensions or for-loops by using lambda functions and map.
I consider this a proper 'functional' powerset function in Python:
def powerSet(input):
# at tree leaf, return leaf
if len(input)==0:
return [[]];
# if not at a leaf, trim and recurse
# recursion is illustrated as follows:
# e.g. S = {1,2,3}
# S_trim = S without first element:
# {(),(2),(3),(2,3)}
# S_trim concatenated with first element:
# {(1),(1,2),(1,3),(1,2,3)}
# we keep the character sliced from front and concat it
# with result of recursion
# use map to apply concatenation to all output from powerset
leading = (input[0])
new_input = input[1:len(input)]
ps1 = list((powerSet(new_input)))
# concatenate over powerset-ed set
ps2 = map(lambda x: [leading]+x,ps1)
ps_list = list(map(lambda x: list(x),ps2))
return ps1+ ps_list
Continuing on the answers provided by #Miguel and #Abdul...the following function insures a lexicographically ordered output.
def subset(A):
if A == []:
return [[]]
sub = subset(A[:-1])
return sub + [rem + [A[-1]] for rem in sub]
tests = [[], [1], [1,2], [1,2,3]]
for test in tests:
print(subset(test))
RESULT
[[]]
[[], [1]]
[[], [1], [2], [1, 2]]
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

python - Concatenate list elements in every possible way

My issue might be quite difficult to explain (maybe that's also the reason I did not find a solution or a similar problem).
What I have is a list with some elements (in my specific case also lists).
What I want is having every possible combinations of concatenations of this list in the same order.
For example:
[[1], [2], [3], [4]] # what I have
{ # what I want
[[1], [2], [3], [4]],
[[1, 2], [3], [4]],
[[1], [2, 3], [4]],
[[1], [2], [3, 4]],
[[1, 2], [3, 4]], # Update 1
[[1, 2, 3], [4]],
[[1], [2, 3, 4]],
[[1, 2, 3, 4]]
}
In general the length of the sublists is greater then 1; also the list itself may have more than 4 elements.
Any help is highly appreciated.
UPDATE 1:
added missing combination in code.
Try this:
def concats(l):
if len(l) < 2:
return [l]
return [[l[0]] + x for x in concats(l[1:])] + \
concats([l[0] + l[1]] + l[2:])
Here's a sample case:
l = [[1], [2], [3], [4]]
r = concats(l)
And the result:
[[[1], [2], [3], [4]],
[[1], [2], [3, 4]],
[[1], [2, 3], [4]],
[[1], [2, 3, 4]],
[[1, 2], [3], [4]],
[[1, 2], [3, 4]],
[[1, 2, 3], [4]],
[[1, 2, 3, 4]]]
Edit: It wasn't clear to me how the empty list should be handled, but in that case you may want to simply return an empty list without wrapping it in an outer list - I'll leave that case up to you. A simple check at the top of the function can handle it any way you choose (and it won't affect the larger cases).

Data structure to represent multiple equivalent keys in set in Python?

Currently, I want to find the correct data structure to meet the following requirement.
There are multiple arrays with disordered element, for example,
[1, 2], [2, 1], [3, 2, 2], [2], [2, 1, 3], [2, 2, 3]
After processing those data, the result is,
[1, 2], [2, 2, 3], [2], [1, 2, 3]
With sorted element in each array and filter the duplicate arrays.
Here are my thoughts:
Data structure Set(Arrays)? - Failed. It seems there is only one array in the build-in set
set([])
Data structure Array(Sets)? - Failed. However, there is no duplicate element in the build-in set. I want to know whether there is one data structure like multiset in C++ within Python?
Transform your list to tuple(thus can be a item of set), then back to list.
>>> [list(i) for i in set([tuple(sorted(i)) for i in a])]
[[1, 2], [2], [2, 2, 3], [1, 2, 3]]
lst = [[1, 2], [2, 1], [3, 2, 2], [2], [2, 1, 3], [2, 2, 3]]
map(list, set(map(tuple, map(sorted, lst)))
Output:
[[1, 2], [2], [2, 2, 3], [1, 2, 3]]
Try this:
[list(i) for i in set(map(tuple, a))]
EDIT:
Assuming that list is already sorted. Thanks to #PM2RING to remind me.
If not, then add this line above
a = [sorted(i) for i in a]
Thanks again to #PM2RING: one liner
[list(i) for i in set(map(tuple, (sorted(i) for i in a)))]
Demo
Some of the solutions currently here are destroying ordering. I'm not sure if that's important to you or not, but here is a version which preserves original ordering:
>>> from collections import OrderedDict
>>> A = [[1, 2], [2, 1], [3, 2, 2], [2], [2, 1, 3], [2, 2, 3]]
>>> [list(k) for k in OrderedDict.fromkeys(tuple(sorted(a)) for a in A)]
[[1, 2], [2, 2, 3], [2], [1, 2, 3]]
No Python, doesn't have a built-in multiset; the closest equivalent in the standard modules is collections.Counter, which is a type of dictionary. A Counter may be suitable for your needs, but it's hard to tell without more context.
Note that sets do not preserve order of addition. If you need to preserve the initial ordering of the lists, you can do what you want like this:
data = [[1, 2], [2, 1], [3, 2, 2], [2], [2, 1, 3], [2, 2, 3]]
a = set()
outlist = []
for s in data:
t = tuple(sorted(s))
if t not in a:
a.add(t)
outlist.append(list(t))
print(outlist)
output
[[1, 2], [2, 2, 3], [2], [1, 2, 3]]
If the number of input lists is fairly small you don't need the set (and the list<->tuple conversions), just test membership in outlist. However, that's not efficient for larger input lists since it performs a linear search on the list.

Iterating over consecutive sublists in Python

Does Python offer a way to iterate over all "consecutive sublists" of a given list L - i.e. sublists of L where any two consecutive elements are also consecutive in L - or should I write my own?
(Example: if L = [1, 2, 3], then the set over which I want to iterate is {[1], [2], [3], [1, 2], [2,3], [1, 2, 3]}. [1, 3] is skipped since 1 and 3 are not consecutive in L.)
I don't think there's a built-in for exactly that; but it probably wouldn't be too difficult to code up by hand - you're basically just looping through all of the possible lengths from 1 to L.length, and then taking all substrings of each length.
You could probably use itertools.chain() to combine the sequences for each length of substring together into a generator for all of them.
Example:
>>> a = [1,2,3,4]
>>> list(
... itertools.chain(
... *[[a[i:i+q] for q in xrange(1,len(a)-i+1)] for i in xrange(len(a))]
... )
... )
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [2], [2, 3], [2, 3, 4], [3], [3, 4], [4]]
If you prefer them in the increasing-length-and-then-lexographical-order sequence that you described, you'd want this instead:
itertools.chain(*[[a[q:i+q] for q in xrange(len(a)-i+1)] for i in xrange(1,len(a)+1)])
Try something like this:
def iter_sublists(l):
n = len(l)+1
for i in xrange(n):
for j in xrange(i+1, n):
yield l[i:j]
>>> print list(iter_sublists([1,2,3]))
[[1], [1, 2], [1, 2, 3], [2], [2, 3], [3]]
This should work:
def sublists(lst):
for sublen in xrange(1,len(lst)+1):
for idx in xrange(0,len(lst)-sublen+1):
yield lst[idx:idx+sublen]

Categories