How to print all the unique combinations of a list? - python

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.

Related

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

Include permutations of nested lists

I have the following function to get the number of permutations (without repeating elements) of a list:
import itertools
def permutations_without_repetition(samples, size):
return list(itertools.permutations(samples, size))
Which works fine for me as long as the list I provide doesn't contain nested lists. Itertools treats nested elements just as one whole element, and doesn't bother also generating permutations containing differently-ordered nested lists.
If I run permutations_without_repetition([[1, 2], 3], 2), the only results I get are:
[([1, 2], 3), (3, [1, 2])]
I know this is the expected behaviour, but I would like the result to be:
[([1, 2], 3), (3, [1, 2]), ([2, 1], 3), (3, [2, 1])]
What's the easiest way to also return permutations that contain permutations of nested lists, to produce the result above?
You can use a recursive generator function:
def combos(d, c = []):
if not isinstance(d, list):
yield d
elif not d:
yield c
else:
for i, a in enumerate(d):
for k in combos(a, c = []):
yield from combos(d[:i]+d[i+1:], c+[k])
print(list(combos([[1, 2], 3])))
Output:
[[[1, 2], 3], [[2, 1], 3], [3, [1, 2]], [3, [2, 1]]]
Shorter solution using itertools:
import itertools as it
def combos(d):
if not isinstance(d, list):
yield d
else:
for i in it.permutations(d):
yield from map(list, it.product(*[combos(j) for j in i]))
print(list(combos([[1, 2], 3])))
Output:
[[[1, 2], 3], [[2, 1], 3], [3, [1, 2]], [3, [2, 1]]]

Generate sets of subsets plus unused elements

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

A strange behavior when I append to a list in Python

I am looking for the Josephus_problem ,but the result is not my Expected. Why?
def J(n,x):
li=range(1,n+1)
k=0
res=[]
while len(li)>1:
k= (x+k-1) % len(li)
li.pop(k)
res.append(li)
#print li
return res
print J(5,3)
Expected Output:
[1, 2, 4, 5]
[2, 4, 5]
[2, 4]
[4]
Actual Output:
[[4], [4], [4], [4]]
You need to append copy of list here:
res.append(li[:]) # <-- not res.append(li) !!!
The actual reason of what's going on it that list is mutable data structure in Python. Look at this snippet
>>> l = [1,2,3]
>>> p = [l,l,l]
>>> p
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
>>> l.pop()
3
>>> p
[[1, 2], [1, 2], [1, 2]]

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