python random shuffle while loop - python

I'm trying to fill lists with permutations of the same initial list. I don't understand why the following is not working.
parts = [[],[]]
while len(parts[-1]) < 2:
newval = random.choice([[1,2,3,4],[5,6,7,8]])
for part in parts:
random.shuffle(newval)
part.append(newval)
Expected result would be something like:
[[[6,7,8,5],[1,3,4,2]],[[5,8,6,7],[4,2,3,1]]]

random.shuffle works in-place and consequently modifies newval. You have to make a copy when appending to part otherwise the same list (or list reference) is shuffled and stored in part.
import random
parts = [[],[]]
while len(parts[-1]) < 2:
newval = random.choice([[1,2,3,4],[5,6,7,8]])
for part in parts:
random.shuffle(newval)
part.append(newval[:])
print(parts)
possible outputs:
[[[3, 1, 2, 4], [5, 7, 6, 8]], [[1, 2, 4, 3], [6, 7, 5, 8]]]
[[[1, 3, 2, 4], [4, 2, 1, 3]], [[2, 4, 3, 1], [4, 3, 2, 1]]]
[[[7, 5, 6, 8], [3, 2, 4, 1]], [[8, 5, 6, 7], [1, 4, 3, 2]]]

Because in Python everything is reference. When you append the value to the array, in fact you add the reference to the place in memory where the value is stored.
Say, you have assigned the list to the first element. When on the next iteration you re-shuffle this list, you change the value in the memory. Thus, the value you will when accessing the element you appended on previous step is also changed.
To fix this, try appending copy.copy(newval) instead of just newval (do not forget to import copy)
Here is your code changed accordingly:
import copy
parts = [[],[]]
while len(parts[-1]) < 2:
newval = random.choice([[1,2,3,4],[5,6,7,8]])
for part in parts:
random.shuffle(newval)
part.append(copy.copy(newval))

Related

Iterate through a nested list and pick certain elements and create a new list

An example:
list = [[2, 1, 2, 3, 4],
[0, 4, 5],
[1, 8, 9]]
So the first index inside a nested list decides which following numbers will be put into an unnested list.
[2, 1, 2, 3, 4] -> 2: so 1 and 2 gets picked up
[0, 4, 5] -> 0: no number gets picked up
[1, 8, 9] -> 1; number 8 gets picked up
Output would be:
[1, 2, 8]
This is what I have so far:
def nested_list(numbers):
if isinstance(numbers[0], list):
if numbers[0][0] > 0:
nested_list(numbers[0][1:numbers[0][0] + 1])
else:
numbers = list(numbers[0])
return numbers + nested_list(numbers[1:])
I try to get the list through recursion but something is wrong. What am I missing or could this be done even without recursion ?
You try using list comprehension with tuple unpacking here.
[val for idx, *rem in lst for val in rem[:idx]]
# [1, 2, 8]
NB This solution assumes you would always have a sub-list of size 1 or greater. We can filter out empty sub-lists using filter(None, lst)
list1=[[2, 1, 2, 3, 4],
[0, 4, 5],
[1, 8, 9]]
list2= []
for nested_list in list1:
for i in range(nested_list[0]):
list2.append(nested_list[i+1])
You can try List-comprehension:
>>> [sub[i] for sub in lst for i in range(1, sub[0]+1) ]
[1, 2, 8]
PS: The solution expects each sublist to be a non-empty list, else it will throw IndexError exception due to sub[0].
Another list comprehension
sum([x[1:x[0] + 1] for x in arr], [])
# [1, 2, 8]
Using builtin function map to apply the picking function, and using itertools.chain to flatten the resulting list of list:
def pick(l):
return l[1:1+l[0]]
ll = [[2, 1, 2, 3, 4], [0, 4, 5], [1, 8, 9]]
print( list(map(pick, ll)) )
# [[1, 2], [], [8]]
print( list(itertools.chain.from_iterable((map(pick, ll)))) )
# [1, 2, 8]
Or alternatively, with a list comprehension:
ll = [[2, 1, 2, 3, 4], [0, 4, 5], [1, 8, 9]]
print( [x for l in ll for x in l[1:1+l[0]]] )
# [1, 2, 8]
Two important notes:
I've renamed your list of lists ll rather than list. This is because list is already the name of the builtin class list in python. Shadowing the name of a builtin is very dangerous and can have unexpected consequences. I strongly advise you never to use the name of a builtin, when naming your own variables.
For both solutions above, the error-handling behaves the same: exception IndexError will be raised if one of the sublists is empty (because we need to access the first element to know how many elements to pick, so an error is raised if there is no first element). However, no exception will be raised if there are not enough elements in one of the sublists. For instance, if one of the sublists is [12, 3, 4], then both solutions above will silently pick the two elements 3 and 4, even though they were asked to pick 12 elements and not just 2. If you want an exception to be raised for this situation, you can modify function pick in the first solution:
def pick(l):
if len(l) == 0 or len(l) <= l[0]:
raise ValueError('in function pick: two few elements in sublist {}'.format(l))
return l[1:1+l[0]]
ll = [[2, 1, 2, 3, 4], [0, 4, 5], [1, 8, 9], [12, 3, 4]]
print( [x for l in ll for x in l[1:1+l[0]]] )
# [1, 2, 8, 3, 4]
print( [x for l in ll for x in pick(l)] )
# ValueError: in function pick: two few elements in sublist [12, 3, 4]

Why is a different outcome occurring on the following use of sort

I was doing a puzzle and where i had to add 2 lists having same length to a new list and sort the list by the second element of the list.
for x in range(n):
tmp.append([start[x],end[x]])
where start and end are lists containing equal elements and n is the length of start and end.
Now, idk why a difference / error occurs between the use of following code.
end.sort()
for x in range(n):
tmp.append([start[x],end[x]])
and
for x in range(n):
tmp.append([start[x],end[x]])
tmp.sort(key=lambda x:x[1])
EDIT:-
Input list
start=[1, 3, 0, 5, 8, 5]
end=[2, 4, 6, 7, 9, 9]
output by sorting first
[[1, 2], [3, 4], [0, 6], [5, 7], [8, 9], [5, 9]]
output by sorting later
[[1, 2], [3, 4], [0, 6], [5, 7], [8, 9], [5, 9]]
works fine for this list but doesn't work for a bigger array
(array contains 80 elements thats why not uploading here)
If you sort end first, you combine the original order of start with the sorted order of end.
If you combine the two lists first and then sort by the end element, the start elements will get reordered, too, as they "tag along" with their end partner. Consider
start = [1, 2, 3]
end = [3, 2, 1]
Now, sorting end and combining, you'll end up with:
start = [1, 2, 3]
end = [1, 2, 3]
# =>
tmp = [[1, 1], [2, 2], [3, 3]]
Combining first, however, produces:
tmp = [[1, 3], [2, 2], [3, 1]]
And sorting this by the second element, will shuffle the old start elements as well:
tmp.sort(key=lambda x:x[1])
# [[3, 1], [2, 2], [1, 3]]
Side note: Check out zip:
tmp = list(zip(start, end))

In the for-loop, when I use 'append' to add a new element to the list, the one that has added before also change

I want to get transpose of matrix B without using Numpy. When I use 'append' to add a new element to the list, the one that has added before also change. How can I fix it?
from decimal import *
B = [[1,2,3,5],
[2,3,3,5],
[1,2,5,1]]
def shape(M):
r = len(M)
c = len(M[0])
return r,c
def matxRound(M, decPts=4):
for p in M:
for index in range(len(M[0])):
p[index] = round(p[index], decPts)
def transpose(M):
c_trans, r_trans = shape(M)
new_row = [0]*c_trans
trans_M = []
for i in range(r_trans):
for j in range(c_trans):
new_row[j] = M[j][i]
print 'new_row',new_row
print 'trans_M before append',trans_M
trans_M.append(new_row)
print 'trans_M after append',trans_M
return trans_M
print transpose(B)
The output is here:
new_row [1, 2, 1]
trans_M before append []
trans_M after append [[1, 2, 1]]
new_row [2, 3, 2]
trans_M before append [[2, 3, 2]]
trans_M after append [[2, 3, 2], [2, 3, 2]]
new_row [3, 3, 5]
trans_M before append [[3, 3, 5], [3, 3, 5]]
trans_M after append [[3, 3, 5], [3, 3, 5], [3, 3, 5]]
new_row [5, 5, 1]
trans_M before append [[5, 5, 1], [5, 5, 1], [5, 5, 1]]
trans_M after append [[5, 5, 1], [5, 5, 1], [5, 5, 1], [5, 5, 1]]
[[5, 5, 1], [5, 5, 1], [5, 5, 1], [5, 5, 1]]
I will complete #glibdud comment's answer :
What you are doing now is creating a list that fits your needs for your Transpose.
You are creating your new matrix.
You are, then, appending your transposed value into your new matrix... without creating a new Transpose list.
What happens then is that you modify the last list you just appended, and try to append it again.
So in the end, you added the 4 same lists to your new matrix. As the 4 lists point to the same address in memory as they are the same object, your new matrix have 4 identical rows.
The most pythonic way I know to perform matrix transposition without using Numpy (that should be the preferred way), is by using list unpacking (list expansion) and the builtin zip function transposed = list(zip(*B)).
However, zip() return tuples while your original matrix is a list of lists. So, if you want to keep your structure, you can use transposed = [list(i) for i in zip(*B)]

Finding the index of the minimum of each list in a list of lists

I'm trying to create a list of the index's of the minimums of each list in a list of list. I have only been able to find an answer for a simple list.
data = [[9 ,5, 2, 8, 6], [3, 5, 1, 9, 2], [2, 9, 3, 0, 5]]
My first idea was to use
.index(min(n))
but it doesn't work for a list of lists.
Expected result:
new_list = [2, 2, 3]
use a list comprehension:
[x.index(min(x)) for x in data]
>>>data = [[9 ,5, 2, 8, 6], [3, 5, 1, 9, 2], [2, 9, 3, 0, 5]]
>>>[x.index(min(x))+1 for x in data]
[3, 3, 4] //actual index (Your required output)
Try it:
result = []
for list in data:
result.append(list.index(min(list)))
At the same time, the answer what you want to get is [2,2,3], not [3,3,4]. Because the list's index start from 0. I hope this can help you.

How to set the list element at guess_row, guess_col to "X"?

I might be having some trouble understanding arrays.
This is how I did it. It is incorrect I know but why?
guest_row = [X]
guest_column = [X]
How should I set a list element at guess_row, guess_column to X?
If you have some nested list, like
array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
then you need
array[guest_row][guest_column] = 9
Say guest_row = 1 and guest_column = 0. Then array[guest_row] is the second item in array, [4, 5, 6]. That makes array[guest_row][guest_column] the first element of the second element of array, currently occupied by 4. The above assignment changes that value, so that array now looks like
[[1, 2, 3], [9, 5, 6], [7, 8, 9]]

Categories

Resources