How to get every possible combination without changing position efficiently? - python

I have a list of 2D list which row number is 60 and column number is not fixed. Here, row numbers mean position. How to get every possible combination without changing position?
Let me explain with an example:
Let's make it shorter to understand. Let's assume, row number is 3 instead of 60.
List:
list_val = [[1, 2, 3], [2, 3], [5]]
I want to get the combination:
{1, 2, 5}, {1, 3, 5}, {2, 2, 5}, {2, 3, 5}, {3, 2, 5}, {3, 3, 5}
For three, it should be easy to write three nested loops. But for 60, writing 60 nested loops is not a good idea. Is there any better efficient way to write code in python?

What you're looking for is itertools.product() which implements it in a reasonably efficient way so that you don't need to reinvent it and it's fine for large iterables.
Why is that? It's implemented in C (1, 2) therefore the performance is faster than with your standard pure-Python implementation of the loops unless you use tricks that could achieve comparable speed.
Don't forget to unpack the iterable that has iterables you want to use with star/asterisk (*) for the function or supply it with multiple variables (product(one, two, three)), otherwise it'll behave differently.
>>> from itertools import product
>>> list(product(*[[1, 2, 3], [2,3], [5]]))
[(1, 2, 5), (1, 3, 5), (2, 2, 5), (2, 3, 5), (3, 2, 5), (3, 3, 5)]
>>>

Related

Python - Finding out all r combinations from a set of cardinality n [duplicate]

This question already has answers here:
How to get all combinations of length n in python
(3 answers)
Closed 1 year ago.
I am trying to generate all possible 3 combinations from the set {1,2,3,4,5} using recursion.
Expected output: [[1,2,3],[1,2,4],[1,2,5],[2,3,4],[2,3,5],[3,4,5],[1,3,4],[1,3,5],[1,4,5],[2,4,5]]
The logic that I am using is that any 3-set combination will either have the first element in it or not have. I am also using the concatenation of lists. Example:
[[1,2,3]] + [[a,b]] gives [[1,2,3],[a,b]]
The below code that uses the above logic doesn't seem to work. I am self-learning so, if I make a mistake, please be patient with me. I am aware there are errors in my recursion. But, trying to backtrack the output in recursion problems is proving quite difficult for me.
Could you please let me know where could be the flaw in this program and guide me towards suitable resources where such types of questions can be better handled. What is the right way of thinking in such questions? Thanks a lot for helping.
Code:
sol = [1,2,3,4,5]
b=3
y= []
def combinations(sol, b):
global y
if len(sol) == b or len(sol)==1 :
return [sol]
y.append([[sol[0]] + combinations(sol[1:], b-1)] + combinations(sol[1:],b))
return y
print(combinations(sol,b)
Use the machinery provided in itertools:
from itertools import combinations
list(combinations(v, 3))
OUTPUT
[(1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5), (1, 4, 5), (2, 3, 4), (2, 3, 5), (2, 4, 5), (3, 4, 5)]
You CAN do this, by having your function be a generator. At each step, you loop through the possible starting cells, then loop through the results returned by the next step in the recursion.
sol = [1,2,3,4,5]
b=3
def combinations(sol, b):
if b == 0:
yield []
else:
for i in range(len(sol)-b+1):
for j in combinations(sol[i+1:],b-1):
yield [sol[i]]+j
print(list(combinations(sol,b)))
Output:
[[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]

Powerset algorithm in Python: difference between + and append on lists

I’m working through the powerset problem in Python.
The powerset P(S) of a set S is the set of all subsets of S. For example, if S = {a, b, c} then P(s) = {{}, {a}, {b}, {c}, {a,b}, {a, c}, {b, c}, {a, b, c}}.
This solution works just fine:
def powerset(array):
powerset = [[]]
for num in array:
for i in range(len(powerset)):
curr_subset = powerset[i]
powerset.append(curr_subset + [num])
return powerset
However, this solution does not:
def powerset(array):
powerset = [[]]
for num in array:
for i in range(len(powerset)):
curr_subset = powerset[i]
curr_subset.append(num)
powerset.append(curr_subset)
return powerset
It seems to overwrite every array in the powerset on each powerset.append operation. For an input of [1, 2, 3], I end up with a return value of:
[[1, 2, 2, 3, 3, 3, 3],
[1, 2, 2, 3, 3, 3, 3],
[1, 2, 2, 3, 3, 3, 3],
[1, 2, 2, 3, 3, 3, 3],
[1, 2, 2, 3, 3, 3, 3],
[1, 2, 2, 3, 3, 3, 3],
[1, 2, 2, 3, 3, 3, 3],
[1, 2, 2, 3, 3, 3, 3]]
Any idea what I’m not fully understanding here?
The problem with your algorithm is that lists are mutable, and you are creating a list full of references to the same list. Any time you append to one of them, you are appending to all of them, because there is only one of them. They are all the same list, you just have more than one reference to it.
Imagine having a list of 10 copies of somebody's phone number; if you call up the first phone number and ask them to put on a hat, then when you call the second phone number, the person on the other end will already be wearing a hat, because it is the same person. If you call each number and say "put on a hat" each time, you'll end up with a list of 10 phone numbers for one person wearing 10 hats, when you actually wanted phone numbers for 10 people wearing one hat each.
The simplest way to design this kind of algorithm is to avoid mutation completely; use tuples instead of lists. This way, every time you add on another element to the tuple, you are creating a new tuple instead of changing the existing one.
Note that this is quite similar to your first solution using curr_subset + [num]; the + operation creates a new list, unlike append which changes the state of an existing list.
def powerset(array):
# a list containing an empty tuple
powerset = [()]
for num in array:
for i in range(len(powerset)):
curr_subset = powerset[i]
# append one more number to the tuple
curr_subset += (num,)
powerset.append(curr_subset)
return powerset
Example:
>>> powerset([1, 2, 3])
[(), (1,), (2,), (1, 2), (3,), (1, 3), (2, 3), (1, 2, 3)]

Find minimum and maximum value at each index of a dictionary of lists

I have a dictionary having following structure
{key1: [1,2,3,4,5], key2: [2,0,4,5,6]}
I need to find maximum and minimum value of each index of the value list, so at index 0, we compare 1 and 2 and choose 2 as the maximum, and 1 as the minimum, etc.
Expected output for my example:
min = [1,0,3,4,5]
max = [2,2,4,5,6]
I cannot use operator as I am not allowed to import it. I tried to used following approach but failed (syntax error). Also I won't iterate through the value set as is not the elegant way (IMO).
maxVal = max(myDictionary.items(), key=(lambda k: myDictionary[k]))
gives me
TypeError: unhashable type: 'list'
Can you correct it or suggest any alternative approach.
You may use zip with min and max:
dct = {'key1': [1,2,3,4,5], 'key2': [2,0,4,5,6]}
[min(i) for i in zip(*dct.values())]
[max(i) for i in zip(*dct.values())]
Output:
[1, 0, 3, 4, 5]
[2, 2, 4, 5, 6]
If you want to get really fancy, you can also use the transpose trick of zip twice to turn this into a one-liner:
min_list, max_list = map(list, zip(*[(min(i), max(i)) for i in zip(*dct.values())]))
min_list
[1, 0, 3, 4, 5]
max_list
[2, 2, 4, 5, 6]
This fancy method behaves badly with empty lists
For example:
dct = {1: [], 2: []}
Will break this method. In fact, pretty much all the ways to break this method involve using an empty list somewhere.
I've mentioned the zip transpose trick twice, so here is why it is necessary here:
If you simply use list(zip(dct.values())), you will get the following output:
[([2, 0, 4, 5, 6],), ([1, 2, 3, 4, 5],)]
This is not the desired result, we want a pairwise comparison of every element at each index of our sublists. However we can leverage the fact that zip is its own tranpose when you use the * operator.
So using list(zip(*dct.values())) provides us with our desired pairwise grouping for comparison:
[(2, 1), (0, 2), (4, 3), (5, 4), (6, 5)]

Convert tuple elements to a dictionnary

I need to convert a structure like this:
(1, 2, 3, 4, 5, 6)
To a dictionnary like this:
{1: 2, 3: 4, 5: 6}
How would you proceed ?
Pair up the elements 2 by two and pass the result to dict():
result = dict(zip(*[iter(values)] * 2))
By using iter() here you avoid creating two in-memory list objects the way [::2] and [1::2] slicing would. See Iterating over every two elements in a list as to why this works.
Demo:
>>> values = (1, 2, 3, 4, 5, 6)
>>> dict(zip(*[iter(values)] * 2))
{1: 2, 3: 4, 5: 6}
The idea is to zip together two slices of the tuple with step 2 - first one starting with the 0th element and the second - with the 1st element, and then pass the result to the dict() constructor:
>>> t = (1, 2, 3, 4, 5, 6)
>>> dict(zip(t[::2], t[1::2]))
{1: 2, 3: 4, 5: 6}
Just to expand that, here are the intermediate values:
>>> t[::2]
(1, 3, 5)
>>> t[1::2]
(2, 4, 6)
Now the zip() would aggregate the elements together, group by position:
>>> zip(t[::2], t[1::2])
[(1, 2), (3, 4), (5, 6)]
Then, the dict() would make a dictionary out of the list of pairs using first elements of the items as keys and second elements as values.
Note that the downside of this approach is that there would be two extra lists created in memory as opposed to #Martijn's proposal. Though, this approach is arguably simpler and more readable.

How to define duplicate items in a Python tuple?

What are some good ways to define a tuple consisting of integers where the number of occurrences of each item is known ?
For example,
I want to define a tuple with 3 2's, 2 4's and 1, 3, 5 occur once.
For this, I can always go the manual way :
foo = (1, 2, 2, 2, 3, 4, 4, 5)
However, this becomes a bit messy when the number of items in the list is large.
So, I want to know what are some ways to automate the task of generating the desired number of duplicates of each item.
You can do it like this:
>>> (1,) * 1 + (2,) * 3 + (4,) * 2 + (5,) * 1
(1, 2, 2, 2, 4, 4, 5)
One way is to use sequence multiplication. Here's a simple version that makes no attempt to avoid creating unnecessary intermediate objects:
accumulator = ()
for (val, count) in some_data_structure:
accumulator += (val,) * count
That can be improved, the main point is to demonstrate that (1,) * 5 gives you (1, 1, 1, 1, 1). Note that this copies the object reference - that's fine for integers, but can cause confusion if you're trying to multiply a sequence of mutable objects.
If you have a tuple of tuples denoting the value and frequency, you can do the following:
tuples = ((1,1), (2,3), (3,1), (4,2), (5,1))
tuple(i for i, n in tuples for _ in range(n)) # Use xrange in Python 2.X
# (1, 2, 2, 2, 3, 4, 4, 5)
Or, if you know that the values are always going to be 1, 2, 3, ..., n, you can use enumerate with a tuple of the frequencies.
freqs = (1, 3, 1, 2, 1)
tuple(i for i, n in enumerate(freqs, 1) for _ in range(n))
# (1, 2, 2, 2, 3, 4, 4, 5)
If you're curious about the use of the double comprehension in the generator expression, you may want to check out this question.
If your tuple has not many number, you can do it in the simplest way.
(1,)+(2,)*3+(3,)+(4,)*2+(5,)
Otherwise, just turn it into a function.
def myTuple(*val):
return sum(((i,) * n for i, n in val), ())
myTuple((1,1),(2,3),(3,1),(4,2),(5,1))
>>>(1, 2, 2, 2, 3, 4, 4, 5)
you can also call it with:
val = ((1,1),(2,3),(3,1),(4,2),(5,1))
myTuple(*val)
>>>(1, 2, 2, 2, 3, 4, 4, 5)
Something like this could work:
>>> result = tuple()
>>> for item, repeat in ((1, 1), (2, 3), (3, 1), (4, 2), (5, 1)):
... result = result + (item,) * repeat
>>> result
(1, 2, 2, 2, 3, 4, 4, 5)
So you want the inverse function of collections.Counter. Here is how you could do it,
# make a dict of counts (list of tuples is better)
counts = {1: 1, 2: 3, 4: 2, 3:1, 5: 1}
t = tuple(k for k,v in sorted(counts.items()) for _ in range(v))
(1, 2, 2, 2, 3, 4, 4, 5)
# for k,v in list_of_tuples, for a list of tuples
You can define the following function
def a_tuple(*data):
l = []
for i, cnt in data: l.extend([i]*cnt)
return tuple(l)
and use it like this
print(a_tuple((1,1), (2,3), (3,1), (4,2), (5,1)))
to produce the following output
(1, 2, 2, 2, 3, 4, 4, 5)
Have a look to the .extend() method of list if you don't understand how the function works.

Categories