subset of a multilist- recursive solution - python-3.x

I need to implement a function to find all subsets of a multiset (I.E an element can appear twice)- but I can not use loops or any modules, only recursion.
My idea was to initialize an empty list and use it to save the results into.
For the first step, we put the original list inside, and then we run the recursive function on the same list after popping the first element. and we run it again on the same function without the second element, and so on.
This was my attempt:
def element_pop(inputlist:list,idx:int)->list: # returns a list without arr[idx]
temp=inputlist.copy()
temp.pop(idx)
return temp
def allsubsets(inputlist:list,resultlist:list)->list:#recurse through the first given list and in the end put all subsets of all sizes inside
idx=len(inputlist)-1
resultlist.append(inputlist)
if(idx>0):
temp=element_pop(inputlist,idx)
idx-=1
return allsubsets(temp,resultlist)
return resultlist
However,these are the results I get:
list= [1, 2, 3]
F(List)= [[1, 2, 3], [1, 2], [1]]
But what I expect to get is:
f(list)= [[1, 2, 3], [1, 2], [1, 3], [2, 3], [1], [2], [3]]
I'm uncertain as to how to iterate through the list in the recursive step, and what promises me I wont get multiple [1]s or [2]s in the final stage?
I also tried breaking the problem down to finding all subsets of size(1,2,3..len(list)) but it hasn't lead me to anything either.
Note: the same element can appear multiple times, for example:
list= [1, 2, 3, 1]
f(list)= [[1], [2], [3], [1], [1, 2], [1, 3], [1, 1], [1, 2, 3], [1, 2, 1], [1, 3, 1], [2, 3, 1], [1, 2, 3, 1]]

Related

Element wise addition of two n dimensional lists

When I need to add two 2D lists element wise, the approach I am using is
l1 = [[1, 1, 1],
[2, 2, 2],
[3, 3, 3]]
l2 = [[1, 1, 1],
[2, 2, 2],
[3, 3, 3]]
new = list(map(lambda e: [sum(x) for x in zip(*e)], zip(l1, l2)))
print(new)
output : [[2, 2, 2],
[4, 4, 4],
[6, 6, 6]]
This code is already difficult to read.
So how would I add two n dimensional lists element wise? Is there a pythonic way to do it or should I use numpy?

divides each element of the matrix by 2 if the element is an even number

i need to write a function in python that takes a matrix as an argument and divides each element of the matrix by 2 if the element is an even number (otherwise, does nothing).
i also need to use list comprehension for this.
as an example, if i have a matrix like m = [[5, 4], [2, 3], [6, 7]] output: [[5, 2], [1, 3], [3, 7]]
Thanks.
def f(matrix):
return [ [x//2 if x%2==0 else x for x in m ] for m in matrix]
print(f([[5, 4], [2, 3], [6, 7]]))

sort a list according to keys and for elements with same keys as per values in python

I started learning python recently. I am trying to sort a list of lists similar to this. However, I'm not able to find the correct method to do so please help.
Consider the list [[1,4], [3,3], [3,2] ,[1,2], [1,3], [2,3], [1,5]]
now, using
def keyFunc(j):
return j[0]
job = sorted(job, key=keyFunc, reverse=True)
I got it down to [[all 3s], [all 2s], [all 1s]]
However, now I want to further sort it so that the lists with common keys are in the order of descending values of their keys.
i.e. [[3,3], [3,2], [2,3], [1,5], [1,4], [1,3], [1,2]]
How does one do that in python?
Why do you use a wrong key function when not using a key function already does what you want?
>>> sorted(job, reverse=True)
[[3, 3], [3, 2], [2, 3], [1, 5], [1, 4], [1, 3], [1, 2]]
Or since you're assigning back to job anyway, you might want to do job.sort(reverse=True) instead of creating a new list.
You can change the keyFunc to be like
def keyFunc(j):
return j[0]*10+j[1]
or
ls = [[3, 3], [3, 2], [2, 3], [1, 5], [1, 4], [1, 3], [1, 2]]
sorted(ls, key=lambda x: x[0]*10+x[1], reverse=True)
That will sort both of the numbers as you described.
I think you can just negate the sorting keys to sort descending twice:
>>> lst = [[1,4], [3,3], [3,2] ,[1,2], [1,3], [2,3], [1,5]]
>>> sorted(lst, key=lambda x: (-x[0], -x[1]))
[[3, 3], [3, 2], [2, 3], [1, 5], [1, 4], [1, 3], [1, 2]]
(-x[0], -x[1]) will first sort by the first item, then if any ties occur sort on the second item, both in descending manner. We can make it descending by negating with the minus - sign.
But as suggested by #Heap Overflow, we don't need to do this because we can just pass reverse=True, and sorted() will naturally sort by the first item then the second in descending order. No need for a sorting key.
You can test this by running the following:
>>> sorted(lst, reverse=True)
[[3, 3], [3, 2], [2, 3], [1, 5], [1, 4], [1, 3], [1, 2]]
>>> sorted(lst, key=lambda x: (x[0], x[1]), reverse=True)
[[3, 3], [3, 2], [2, 3], [1, 5], [1, 4], [1, 3], [1, 2]]
Which both give the same results.

The pythonic way to remove a list item thats is 'sub-list' of another

I have this method that gets all valid moves from a checkers board.
def get_all_moves(self) -> list:
moves = []
pieces = self.__get_all_turn_pieces()
for p in pieces:
self.__jump_moves(p.coord, moves)
self.__b.wipe()
self.__simple_moves(p, moves)
moves.sort(key=len, reverse=True)
for i in range(len(moves)):
for j in range(len(moves)):
if moves[i][:len(moves[j])] == moves[j] and len(moves[i]) > len(moves[j]):
moves.remove(moves[j])
return moves
The problem is: by the rules of checkers the player has to execute the most number of captures in his move. Therefore I need to remove the invalid moves.
Lets analysis this output:
[[3, 0, 5, 2, 3, 4], [5, 0, 3, 2, 5, 4], [1, 0, 2, 1], [1, 2, 2, 3], [1, 2, 2, 1], [1, 4, 2, 3], [2, 5, 3, 6], [2, 5, 3, 4], [2, 7, 3, 6], [3, 0, 5, 2], [5, 0, 3, 2]]
The first and second items of output list contain, respectively, the last two ones. So I need to remove them because they are 'sub-list' of the first ones.
The problem is my nested loop cannot solve this problem. The program spill this error:
Traceback (most recent call last):
File "damas.py", line 435, in <module>
print(m.get_all_moves())
File "damas.py", line 418, in get_all_moves
if moves[i][:len(moves[j])] == moves[j] and len(moves[i]) > len(moves[j]):
IndexError: list index out of range
After awhile, I'm wondering what is the most pythonic way to solve this problem.
As you may have worked out, the problem is that you're only checking the length of moves for i once at the beginning. When you remove items from it, you shorten it, which results in the loop variable i eventually advancing beyond the size of the list.
There are a variety of ways to remedy this. My usual approach for this sort of situation, where you have to evaluate every element in a list for removal, is to start at the end of the list and work back toward the start.
for i in reversed(range(len(moves))):
for j in range(len(moves)):
if i != j and moves[i] == moves[j][:len(moves[i])]:
del moves[i]
break
Note that I am evaluating moves[i] for removal, and not moves[j]. If I find that I want to remove it, I do so and then immediately break out of the inner for loop. This will only affect the structure of the list at and after position i (the items we've already considered and have decided to keep), not before (the items we have yet to consider). Therefore, we won't run into any pesky IndexErrors.
I don't know if it's the most pythonic way of doing things, but this is a variant of something I've used for checking substrings which is pretty concise:
def check_for_sublist(sub_list,longer_list):
return any(sub_list == longer_list[offset:offset+len(sub_list)] for offset in range(len(longer_list)))
Or a lambda version, which I've been advised not to use in the comments. Left it here as a learning tool as I learnt something from the comment!
x_sublist_y = lambda x, y: any(x == y[offset:offset+len(x)] for offset in range(len(y)))
And then I think this will do the trick. It creates a list of entries for which arraycheck doesn't return True (making sure to exclude checking terms against themselves).
moves = [[3, 0, 5, 2, 3, 4], [5, 0, 3, 2, 5, 4], [1, 0, 2, 1], [1, 2, 2, 3], [1, 2, 2, 1], [1, 4, 2, 3], [2, 5, 3, 6], [2, 5, 3, 4], [2, 7, 3, 6], [3, 0, 5, 2], [5, 0, 3, 2]]
filtered_moves = [move1 for move1 in moves if all([not check_for_sublist(move1, move2) for move2 in moves if move1 != move2])]
Here's a solution using a list comprehension, which is often a useful approach when you want to make things as Pythonic as possible
def is_sublist( seq, lists ):
for candidate in lists:
if len( candidate ) > len( seq ) and candidate[ :len( seq ) ] == seq:
return True
return False
moves = [[3, 0, 5, 2, 3, 4], [5, 0, 3, 2, 5, 4], [1, 0, 2, 1], [1, 2, 2, 3], [1, 2, 2, 1], [1, 4, 2, 3], [2, 5, 3, 6], [2, 5, 3, 4], [2, 7, 3, 6], [3, 0, 5, 2], [5, 0, 3, 2]]
# here comes the list comprehension:
pruned = [ eachMove for eachMove in moves if not is_sublist( eachMove, moves ) ]
To modify the sequence moves in-place, you would assign to moves[:] instead of to a new variable pruned.
However, the solution above is not the most efficient, which may be a concern if the number of potential moves is large. A less-elegant-looking approach like the following may be advisable because, as candidate moves get rejected, we reduce the number of potential superlists against which each future potential sublist must be checked:
accepted = []
while moves:
eachMove = moves.pop( 0 )
if not is_sublist( eachMove, moves ) and not is_sublist( eachMove, accepted ):
accepted.append( eachMove )
moves[:] = accepted
If the ordering of the list of moves is not important, performing moves.sort() at the top of this routine will make it more efficient (in fact, then we could optimize the code even further, because each move only ever has to be compared against the next move in the list).

Python - Generate list of lists from other list values

I need to generate a list of lists in that special way:
[3, 1, 4] -> [[1, 2, 3], [1], [1, 2, 3, 4]]
That means that every list in a list of lists must be in range of the given list values. I've tried smth like:
L = [3, 1, 4]
q = [i for i in L]
print(list([x] for x in range(y for y in q)))
But it return a TypeError: generator cannot be interpreted as an integer
That all has to be a single generator expression.
Using a list comprehension.
Try:
L = [3, 1, 4]
print([list(range(1, i+1)) for i in L])
Output:
[[1, 2, 3], [1], [1, 2, 3, 4]]

Resources