questions about the nested loop on array [duplicate] - python

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 5 years ago.
s = [[0]*3]*3
i = 0
while i < 3:
j = 0
while j < 3:
print(s[i][j])
s[i][j] += 1
j += 1
i += 1
The printed result of the above code really confused me, Why the second and third column of the array become [1,1,1] and [2,2,2] but not [0,0,0]?

Because when you create a list of lists using [[0]*3]*3, you're creating one list [0,0,0] three times, so you'll have [[0,0,0],[0,0,0],[0,0,0]], but all of the sub lists ([0,0,0]) are really refrencing the same list, since you just created one and multiplied it by 3 to create the others, so changing one list will make changes to the other lists.
To prevent this, create independent zero-ed lists using a list comprehension:
s = [[0]*3 for i in range(3)]
i = 0
while i < 3:
j = 0
while j < 3:
print(s[i][j])
s[i][j] += 1
j += 1
i += 1
print(s) # just to see the final result
Which outputs:
0
0
0
0
0
0
0
0
0
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]

because the way you create array is
s = [[0]*3]*3
which means all elements of s is the same list and once you change one of them, the rest of them will be changed too
so if you want to get [[0,0,0],[1,1,1],[2,2,2]] as you said,try this:
import numpy as np
s = np.zeros((3,3)) #in this way you can get a 3*3 array
i = 0
while i < 3:
j = 0
while j < 3
s[i][j] += i #not plus 1,otherwise you will get [[1,1,1],[1,1,1],[1,1,1]]
j += 1
i += 1

Related

How could you randomly exclude/delete elements from an array that satisfy some condition?

For example, if I had a 1x100 array, and it contained 90 0's, I'd like to somehow exclude 80 of those 0's chosen at random. I've been struggling with this problem for a while and I've made very little progress unfortunately.
Since you have a numpy tag:
import numpy as np
def solution(arr, value, size):
return np.delete(arr, np.random.choice(np.flatnonzero(arr==value), size, False))
arr = np.array([0]*90 + [1]*10)
np.random.shuffle(arr)
print(solution(arr, 0, 80)) # [1 0 1 0 0 1 0 0 1 0 1 1 1 0 0 0 0 1 1 1]
print(solution(arr, 0, 90)) # [1 1 1 1 1 1 1 1 1 1]
print(solution(arr, 0, 100)) # Raises error (expected behavior)
Vanilla Python approach.
Determine indices of the elements meeting the criteria:
candidate_indices = [i for i, x in enumerate(data) if data == 0]
Choose indices to remove:
removed_indices = random.sample(candidate_indices, 80)
Remove them, by building a list without the corresponding elements:
result = [x for i, x in enumerate(data) if i not in removed_indices]
This is one approach, it is not very efficient, but it should be robust.
def progressivelyExcludeItem(item, arr, maxExclusions):
"""
#param item the item value that will be progressively excluded
#param arr the list from which we will exclude
#param maxExclusions the maximum number of excluded items
"""
excludeIndices = [i for i, x in enumerate(arr) if x == item]
excludeIndices = random.shuffle(excludeIndices)[:maxExclusions]
for i in excludeIndices.sort().reversed():
arr.pop(i)
Time complexity is O(n^2).

Python: nested for loop

Hello am a beginner to python and I have been stuck at this problem for awhile now. I want to start with 2 lists:
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
list2 = [a, b, c]
And write a function that will give me this output:
a
1
2
3
b
4
5
6
c
7
8
9
I have tried using nested for loops and a counter but I am unsure how to obtain the above result.
counter = 0
for i in list2:
print(i)
for j in list1:
print(j)
counter += 1
if counter == 3:
counter = 0
break
Any help would be appreciated!
Here's one possible way, closer to what you intended to write:
j = 0
for i in list2:
print(i)
for _ in range(3):
print(list1[j])
j += 1
Here's an option:
for i, v1 in enumerate(list2):
print(v1)
for v2 in list1[i*3:(i+1)*3]:
print(v2)
You don't need to make and update your own counters here. The built-in enumerate() function generates a counter for you and automatically updates it for each step of the loop. Then you can use list slicing to get the right three values of the inner list.
Try this:
for i in list2:
print(i)
# here you don't have to take it back to 0
counter = 0
for j in range(len(list1)):
# Use range to loop the list so you can cut it off later
# from the position you reach the third element
print(list1[j])
counter += 1
if counter == 3:
list1 = list1[j + 1:]
break
It outputs what you expect:
a
1
2
3
b
4
5
6
c
7
8
9

How to use 2 index variable in a single for loop in python

In C language we can use two index variable in a single for loop like below.
for (i = 0, j = 0; i < i_max && j < j_max; i++, j++)
Can some one tell how to do this in Python ?
With zip we can achieve this.
>>> i_max = 5
>>> j_max = 7
>>> for i, j in zip(range(0, i_max), range(0, j_max)):
... print str(i) + ", " + str(j)
...
0, 0
1, 1
2, 2
3, 3
4, 4
If the first answer does not suffice; to account for lists of different sizes, a possible option would be:
a = list(range(5))
b = list(range(15))
for i,j in zip(a+[None]*(len(b)-len(a)),b+[None]*(len(a)-len(b))):
print(i,j)
Or if one wants to cycle around shorter list:
from itertools import cycle
for i,j in zip(range(5),cycle(range(2)):
print(i,j)
One possible way to do that is to iterate over a comprehensive list of lists.
For example, if you want to obtain something like
for r in range(1, 3):
for c in range(5, 7):
print(r, c)
which produces
# 1 5
# 1 6
# 2 5
# 2 6
is by using
for i, j in [[_i, _j] for _i in range(1, 3) for _j in range(5, 7)]:
print(i, j)
Maybe, sometimes one more line is not so bad.
You can do this in Python by using the syntax for i,j in iterable:. Of course in this case the iterable must return a pair of values. So for your example, you have to:
define the list of values for i and for j
build the iterable returning a pair of values from both lists: zip() is your friend in this case (if the lists have different sizes, it stops at the last element of the shortest one)
use the syntax for i,j in iterable:
Here is an example:
i_max = 7
j_max = 9
i_values = range(i_max)
j_values = range(j_max)
for i,j in zip(i_values,j_values):
print(i,j)
# 0 0
# 1 1
# 2 2
# 3 3
# 4 4
# 5 5
# 6 6

Python nested loop miscounting instances of integers in list

I'm banging my head against the wall trying to figure out why this nested loop is miscounting the number of times an integer occurs in a list. I've set up a function to take two lines of input, n and ar, where n is the number of integers in ar and ar is the list of integers. My code is below:
import sys
n = sys.stdin.readline()
n = int(n)
ar = sys.stdin.readline()
ar = ar.split(' ')
ar = [int(i) for i in ar]
def find_mode(n,ar):
# create an empty dict and initially set count of all integers to 1
d = {}
for i in range(n):
d[ar[i]] = 1
for i in range(n):
# hold integer i constant and check subsequent integers against it
# increase count if match
x = ar[i]
for k in range(i+1,n):
if ar[k] == x:
d[ar[k]] += 1
print(d)
The counter seems to be increasing the count by 1 every time, which leads me to believe it's a problem with the nested loop.
>>> 9
>>> 1 2 3 4 4 9 9 0 0
{0: 2, 1: 1, 2: 1, 3: 1, 4: 2, 9: 2}
OK
>>> 10
>>> 1 2 3 4 4 9 9 0 0 0
{0: 4, 1: 1, 2: 1, 3: 1, 4: 2, 9: 2}
Count of 0 increased by +2
>>> 11
>>> 1 2 3 4 4 9 9 0 0 0 0
{0: 7, 1: 1, 2: 1, 3: 1, 4: 2, 9: 2}
Count of 0 increased by +3
I understand there might be more efficient or "pythonic" ways to count the amount of times a number occurs in a list but this was the solution I came up with and as someone still learning Python, it would help to understand why this exact solution is failing. Many thanks in advance.
This is because for each distinct number in the list (call it x) you count the number of subsequent appearances. This is fine if a number only occurs twice but if it occurs multiple times you will over-count for each additional appearance.
For example: [0, 0, 0, 0]. You iterate over the list and then for each item you iterate over the list that follows that item. So for the first 0 you count a total of 3 subsequent 0s. For the second however you will count a total of 2 and for the third a total of 1 which makes 6. This is why you have 3 too much in the end.
You can achieve this task by using collections.Counter:
>>> from collections import Counter
>>> d = Counter(ar)
I'm not exactly sure that I can fix your specific problem, but would something like this work instead?
d={}
for x in ar:
d[x] = d.get(x, 0) + 1
I understand that you want to fix your existing work as a learning exercise, but I'm not sure that that approach is even the right one. As it is, I can't really tell what you're going for, so it's hard for me to offer specific advice. I would recommend that you don't throw good time after bad.
python has a method to do exactly what you're describing.
It's called .count().
If you do ar.count(3), it will return the number of occurences of 3 in the list ar.
** In your case:**
There's no need for a nested loop as you only need one loop.
Try this:
dic = {}
for num in ar:
if num not in dic:
dic[num] = 1
else:
dic[num] += 1
This would produce the dict you want with the numbers and their occurences
You can refer to other answers as to how you should solve this problem more efficiently, but to answer the question you're asking (Why doesn't this nested loop work?):
To visualize what your nested loop is doing consider the following input:
0 0 0 0 0
Your algorithm will count the following:
0 0 0 0 0
^ ^ ^ ^ ^ (5)
then,
0 0 0 0 0
^ ^ ^ ^ (4)
then,
0 0 0 0 0
^ ^ ^ (3)
then,
0 0 0 0 0
^ ^ (2)
and finally,
0 0 0 0 0
^ (1)
What happens is it counts the number of 0's multiple times over. In this instance it will count
15 0's (5+4+3+2+1)
itertools are your friend
from itertools import groupby
def group_by_kv_l(n):
l = []
for k, v in groupby(n):
l.append((len(list(v)),int(k)))
return l
def group_by_kv_d(n):
d = {}
for k, v in groupby(n):
d[(int(k))] = len(list(v))
return d
if __name__ == "__main__":
n = input().split()
n = "".join(n)
print(
"STDIN: {}".format(n)
)
print(
group_by_kv_l(n)
)
print(
group_by_kv_d(n)
)

How to create lists of 3x3 sudoku block in python

I need help creating a list for each of the 9 3x3 blocks in sudoku. so I have a list of lists representing the original sudoku board (zero means empty):
board=[[2,0,0,0,0,0,0,6,0],
[0,0,0,0,7,5,0,3,0],
[0,4,8,0,9,0,1,0,0],
[0,0,0,3,0,0,0,0,0],
[3,0,0,0,1,0,0,0,9],
[0,0,0,0,0,8,0,0,0],
[0,0,1,0,2,0,5,7,0],
[0,8,0,7,3,0,0,0,0],
[0,9,0,0,0,0,0,0,4]]
I need to turn these into a list of lists containing the 3x3 blocks. So for example:
[[2,0,0,0,0,0,0,4,8],[etc]]
i tried creating one list called "blocks" containing 9 other lists with just zeroes in each list. so it looked like:
blocks=[[0,0,0,0,0,0,0,0,0],[etc]
then i used a while loop to change the values in the list:
BLOCK_COUNT=0
BOARD_COUNT=0
while BLOCK_COUNT<len(blocks):
blocks[BLOCK_COUNT][0]=board[BOARD_COUNT][BOARD_COUNT]
blocks[BLOCK_COUNT][1]=board[BOARD_COUNT][BOARD_COUNT+1]
blocks[BLOCK_COUNT][2]=board[BOARD_COUNT][BOARD_COUNT+2]
blocks[BLOCK_COUNT][3]=board[BOARD_COUNT+1][BOARD_COUNT]
blocks[BLOCK_COUNT][4]=board[BOARD_COUNT+1][BOARD_COUNT+1]
blocks[BLOCK_COUNT][5]=board[BOARD_COUNT+1][BOARD_COUNT+2]
blocks[BLOCK_COUNT][6]=board[BOARD_COUNT+2][BOARD_COUNT]
blocks[BLOCK_COUNT][7]=board[BOARD_COUNT+2][BOARD_COUNT+1]
blocks[BLOCK_COUNT][8]=board[BOARD_COUNT+2][BOARD_COUNT+2]
BLOCK_COUNT+=1
BOARD_COUNT+=3
This however gives me an index error. if I create 2 of those while loops with "BLOCK_COUNT" being 3 and 6 respectively then i get a better answer but it still doesn't give me the correct 3x3 block for some. So i'm pretty much at a loss for how to do this. Thanks.
def getBlocks(board):
answer = []
for r,c in itertools.product(range(3), repeat=2):
answer.append([board[r+i][c+j] for i,j in itertools.product(range(0, 9, 3), repeat=2)])
return answer
Of course, you could replace the whole thing with just one list comprehension:
answer = [[board[r+i][c+j] for i,j in itertools.product(range(0, 9, 3), repeat=2)]
for r,c in itertools.product(range(3), repeat=2)]
In case you are interested in a version that doesn't use any built-ins to do any heavy lifting:
def getBlocks(board):
answer = []
for r in range(3):
for c in range(3):
block = []
for i in range(3):
for j in range(3):
block.append(board[3*r + i][3*c + j])
answer.append(block)
return answer
So what's happening here?:
Well, first, we decide to iterate over the 9 blocks that we want. These are governed by the r and c variables. This is also why we multiply them by 3 when we access the numbers on the board (because each block is a square of side 3).
Next, we want to iterate over the elements in each block. Translation: Lookup the numbers within each 3x3 block. The index of each element within the block is governed by i and j. So we have i and j that govern the elements we want to access, along with r and c, which are their offsets from the board itself, determining the location of the "block" we want. Now we're off to the races.
For each r and c (notice that each loops over range(3), so there are 9 (r,c) pairs - the 9 blocks that we are after), loop over the 9 elements in the block (the 9 (i,j) pairs). Now, simply access the elements based on their relative locations from the (r,c) offsets (3*r gives the first row of the relevant block, and adding i gives the row of the required element. Similarly, 3*c gives the first column of the relevant block, and adding j gives the column of the required element. Thus, we have the coordinates of the element we want). Now, we add the element to block.
Once we've looped over all the elements in the block, we add the block itself to the answer, and presto! we're done
You can do this with a combination of reshape and transpose when you use numpy.
edit - sorry - hit enter too soon:
import numpy as np
board=[[2,0,0,0,0,0,0,6,0],
[0,0,0,0,7,5,0,3,0],
[0,4,8,0,9,0,1,0,0],
[0,0,0,3,0,0,0,0,0],
[3,0,0,0,1,0,0,0,9],
[0,0,0,0,0,8,0,0,0],
[0,0,1,0,2,0,5,7,0],
[0,8,0,7,3,0,0,0,0],
[0,9,0,0,0,0,0,0,4]]
t = np.array(board).reshape((3,3,3,3)).transpose((0,2,1,3)).reshape((9,9));
print t
Output:
[[2 0 0 0 0 0 0 4 8]
[0 0 0 0 7 5 0 9 0]
[0 6 0 0 3 0 1 0 0]
[0 0 0 3 0 0 0 0 0]
[3 0 0 0 1 0 0 0 8]
[0 0 0 0 0 9 0 0 0]
[0 0 1 0 8 0 0 9 0]
[0 2 0 7 3 0 0 0 0]
[5 7 0 0 0 0 0 0 4]]
should work, in python3 you might replace "(m/3)*3" with "int(m/3)*3"
[[board[(m/3)*3+i][(m%3)*3+j] for i in range(3) for j in range(3)] for m in range(9)]
this uses no builtins and is faster 3 nested for loops
def get_boxes(board):
boxes = []
for i in range(9):
if i == 0 or i % 3 == 0:
box_set_1 = board[i][:3] + board[i + 1][:3] + board[i + 2][:3]
boxes.append(box_set_1)
box_set_2 = board[i][3:6] + board[i + 1][3:6] + board[i + 2][3:6]
boxes.append(box_set_2)
box_set_3 = board[i][6:] + board[i + 1][6:] + board[i + 2][6:]
boxes.append(box_set_3)
def get_boxes(board):
boxes = []
for i in range(9):
if i == 0 or i % 3 == 0:
box_set_1 = board[i][:3] + board[i + 1][:3] + board[i + 2][:3]
boxes.append(box_set_1)
box_set_2 = board[i][3:6] + board[i + 1][3:6] + board[i + 2][3:6]
boxes.append(box_set_2)
box_set_3 = board[i][6:] + board[i + 1][6:] + board[i + 2][6:]
boxes.append(box_set_3)
return boxes

Categories