I need to find the biggest sequence of zeros next to each other (up down left right).
for example in this example the function should return 6
mat = [[1,**0**,**0**,3,0],
[**0**,**0**,2,3,0],
[2,**0**,**0**,2,0],
[0,1,2,3,3],]
the zeros that i marked as bold should be the answer (6)
the solution should be implemented without any loop (using recursion)
this is what i tried so far
def question_3_b(some_list,index_cord):
y = index_cord[0]
x = index_cord[1]
list_of_nums = []
def main(some_list,index_cord):
y = index_cord[0]
x = index_cord[1]
def check_right(x,y):
if x + 1 < 0:
return 0
if some_list[y][x+1] == 0:
main(some_list,(y,x+1))
else:
return 0
def check_left(x,y):
if x -1 < 0:
return 0
if some_list[y][x - 1] == 0:
main(some_list,(y, x - 1))
def check_down(x,y):
if y + 1 < 0:
return 0
try:
if some_list[y + 1][x] == 0:
main(some_list,(y + 1, x))
except:
print("out of range")
def check_up(x,y):
counter_up = 0
if y - 1 < 0:
return 0
if some_list[y - 1][x] == 0:
counter_up += 1
main(some_list,(y - 1, x))
list_of_nums.append((x,y))
right = check_right(x,y)
down = check_down(x,y)
left = check_left(x,y)
up = check_up(x, y)
main(some_list,index_cord)
print(list_of_nums)
question_3_b(mat,(0,1))
Solution #1: classic BFS
As I mention in a comment, you can tackle this problem using BFS (Breadth First Search), it will be something like this:
# This function will give the valid adjacent positions
# of a given position according the matrix size (NxM)
def valid_adj(i, j, N, M):
adjs = [[i + 1, j], [i - 1, j], [i, j + 1], [i, j - 1]]
for a_i, a_j in adjs:
if 0 <= a_i < N and 0 <= a_j < M:
yield a_i, a_j
def biggest_zero_chunk(mat):
answer = 0
N, M = len(mat), len(mat[0])
# Mark all non zero position as visited (we are not instrested in them)
mask = [[mat[i][j] != 0 for j in range(M)] for i in range(N)]
queue = []
for i in range(N):
for j in range(M):
if mask[i][j]: # You have visited this position
continue
# Here comes the BFS
# It visits all the adjacent zeros recursively,
# count them and mark them as visited
current_ans = 1
queue = [[i,j]]
while queue:
pos_i, pos_j = queue.pop(0)
mask[pos_i][pos_j] = True
for a_i, a_j in valid_adj(pos_i, pos_j, N, M):
if mat[a_i][a_j] == 0 and not mask[a_i][a_j]:
queue.append([a_i, a_j])
current_ans += 1
answer = max(answer, current_ans)
return answer
mat = [[1,0,0,3,0],
[0,0,2,3,0],
[2,0,0,2,0],
[0,1,2,3,3],]
mat2 = [[1,0,0,3,0],
[0,0,2,3,0],
[2,0,0,0,0], # A slight modification in this row to connect two chunks
[0,1,2,3,3],]
print(biggest_zero_chunk(mat))
print(biggest_zero_chunk(mat2))
Output:
6
10
Solution #2: using only recursion (no for statements)
def count_zeros(mat, i, j, N, M):
# Base case
# Don't search zero chunks if invalid position or non zero values
if i < 0 or i >= N or j < 0 or j >= M or mat[i][j] != 0:
return 0
ans = 1 # To count the current zero we start at 1
mat[i][j] = 1 # To erase the current zero and don't count it again
ans += count_zeros(mat, i - 1, j, N, M) # Up
ans += count_zeros(mat, i + 1, j, N, M) # Down
ans += count_zeros(mat, i, j - 1, N, M) # Left
ans += count_zeros(mat, i, j + 1, N, M) # Right
return ans
def biggest_zero_chunk(mat, i = 0, j = 0, current_ans = 0):
N, M = len(mat), len(mat[0])
# Base case (last position of mat)
if i == N - 1 and j == M - 1:
return current_ans
next_j = (j + 1) % M # Move to next column, 0 if j is the last one
next_i = i + 1 if next_j == 0 else i # Move to next row if j is 0
ans = count_zeros(mat, i, j, N, M) # Count zeros from this position
current_ans = max(ans, current_ans) # Update the current answer
return biggest_zero_chunk(mat, next_i, next_j, current_ans) # Check the rest of mat
mat = [[1,0,0,3,0],
[0,0,2,3,0],
[2,0,0,2,0],
[0,1,2,3,3],]
mat2 = [[1,0,0,3,0],
[0,0,2,3,0],
[2,0,0,0,0], # A slight modification in this row to connect two chunks
[0,1,2,3,3],]
print(biggest_zero_chunk(mat.copy()))
print(biggest_zero_chunk(mat2.copy()))
Output:
6
10
Notes:
The idea behind this solution is still BFS (represented mainly in the count_zeros function). Also, if you are interested in using the matrix values after this you should call the biggest_zero_chunk with a copy of the matrix (because it is modified in the algorithm)
So I'm trying to get the positions of pixels that surround a pixel :
for example :
0 0 0 0
x x x 0
x 1 x 0
x x x 0
I need to get the positions of all those Xs.
The problem for me isn't the general case I can do a double-loop to get them, the problem is the conditions. Because what came to my mind is to manually code every case, but it isn't efficient and takes too many lines.
Therefore, I'm asking if there is an easier way to do so
I can program it manually by doing several if statement and assign their values to an array
Here is what i wrote for the moment it takes to many lines and isn't efficient
def cond_i_zero(pos_array,i,j):
pos_array[0] = i
pos_array[1] = j-1
pos_array[2] = i
pos_array[3] = j+1
pos_array[4] = i+1
pos_array[5] = j-1
pos_array[6] = i+1
pos_array[7] = j
pos_array[8] = i+1
pos_array[9] = j+1
return pos_array
def cond_j_zero(pos_array,i,j):
pos_array[0] = i-1
pos_array[1] = j
pos_array[2] = i-1
pos_array[3] = j+1
pos_array[4] = i
pos_array[5] = j+1
pos_array[6] = i+1
pos_array[7] = j
pos_array[8] = i+1
pos_array[9] = j+1
return pos_array
"""
i,j : represent the position of the pixel that is equivalent to 1 in the
example above
total_img_nb : 16 for the example
output expected : array of positions so for example since the maximum of pixels that suround a pixel is 8 the output will be an array of size 16
where every pair number represent the i(row) pos and every odd number represent the j (columns) pos
"""
def pos_in_array(total_imgs_nb,i,j):
x = 2
y = 2
size = int(math.sqrt(total_imgs_nb))-1
if ( i == 0 ):
x = 1
if ( j == 0 ):
y = 1
if ( i == size ):
x = size - 1
if ( j == size ):
y = size - 1
pos_array = np.zeros(( total_imgs_nb ))
pos_array += 999
if((i==0 and j == 0) or (i==size and j == size)):
pos_array[0] = i
pos_array[1] = y
pos_array[2] = x
pos_array[3] = j
pos_array[4] = x
pos_array[5] = y
elif (i==0):
pos_array=cond_i_zero(pos_array,i,j)
elif (j==0):
pos_array=cond_j_zero(pos_array,i,j)
elif (i==size):
pos_array[0] = i
pos_array[1] = y
pos_array[2] = x
pos_array[3] = j
pos_array[4] = x
pos_array[5] = y
pos_array[6] = y
pos_array[7] = y
pos_array[8] = y
pos_array[9] = y
else:
count = 0
for w in range(i-1,i+2):
for v in range(j-1,j+2):
pos_array[count] = w
count = count +1
pos_array[count] = v
count = count +1
return pos_array
def main():
pos_array = pos_in_array(16,1,1)
# this usually return
[0. 0. 0. 1. 0. 2. 1. 0. 1. 2. 2. 0. 2. 1. 2. 2.]
Here is how i did it .
first i get the number of neighbours for the pixel
then i create a 1-D array of double the size of the neighbours and then i bound the loop and add the position to the array .
size = int(math.sqrt(nb_total))
if((i==0 and (j ==0 or j==size-1))or (i==size-1 and (j == size -1 or j==0))):
neighbors = 3
elif(i==0 or j==0 or i==size-1 or j==size-1):
neighbors = 5
else:
neighbors = 8
array_pos = np.zeros((neighbors*2))
count = 0
for x in range(size):
for y in range(size):
if(x == i and y == j ):
continue
if((x < i+2 and y <j+2)and (x> i-2 and y > j-2 )):
array_pos[count] = x
count = count + 1
array_pos[count] = y
count = count + 1
if(count == neighbors*2):
break
if(count == neighbors*2):
break
return array_pos
When I first starting trying the question, my code would take over a minute to even finish running and give me the answer. I have already tried dynamic programming and storing previous numbers so it doesn't have to run the same number multiple times. I have also tried compacting (n3)+1 and n / 2 into a single line with ((n3)+1) but both of these has only managed to cut my code to 10 seconds. Is there anything else I can try to speed up my code?
def Collatz(n):
dic = {a: 0 for a in range(1,1000000)}
dic[1] = 0
dic[2] = 1
number,length = 1,1
for i in range(3,n,1):
z = i
testlength = 0
loop = "T"
while loop == "T":
if z % 2 == 0:
z = z / 2
testlength += 1
else:
z = ((z*3)+1) / 2
testlength += 2
if z < i:
testlength += dic[z]
loop = "F"
dic[i] = testlength
if testlength > length:
print(i,testlength)
number,length = i,testlength
return number,length
print(Collatz(1000000))
When you calculate the sequence for one input, you find out the sequence length for all the intermediate values. It helps to remember all of these in the dictionary so you never have to calculate a sequence twice of any number < n.
I also started at (n-1)//2, since there's no point testing any number x if 2x is going to be tested later, because 2x will certainly have a longer sequence:
def Collatz(n):
dic = [-1]*n
dic[1] = 0
bestlen = 0
bestval = 1
q=[]
for i in range((n-1)//2,n,1):
q.clear()
z = i
while z >= n or dic[z] < 0:
q.append(z)
if z % 2 == 0:
z = z//2
else:
z = z*3+1
testlen = len(q)+dic[z]
if testlen > bestlen:
bestlen = testlen
bestval = i
print (bestval, bestlen)
for j in range(0,len(q)):
z = q[j]
if z < n:
dic[z] = testlen-j
return bestval, bestlen
print(Collatz(1000000))
Although the answer from Matt Timmermanns is fast, it is not quite as easy to understand as a recursive function. Here is my attempt that is actually faster for n = 10*million and perhaps easier to understand...
f = 10000000
def collatz(n):
if n>=collatz.bounds:
if (n % 4) == 0:
return collatz(n//4)+2
if (n % 2) == 0:
return collatz(n//2)+1
return collatz((3*n+1)//2)+2
if collatz.memory[n]>=0:
return collatz.memory[n]
if (n % 2) == 0:
count = collatz(n//2)+1
else:
count = collatz((3*n+1)//2)+2
collatz.memory[n] = count
return count
collatz.memory = [-1]*f
collatz.memory[1] = 0
collatz.bounds = f
highest = max(collatz(i) for i in range(f//2, f+1))
highest_n = collatz.memory.index(highest)
print(f"collatz({highest_n}) is {highest}")
My results:
$ time /usr/bin/python3 collatz.py
collatz(8400511) is 685
real 0m9.445s
user 0m9.375s
sys 0m0.060s
Compared to
$ time /usr/bin/python3 mattsCollatz.py
(8400511, 685)
real 0m10.672s
user 0m10.599s
sys 0m0.066s
i'm trying to find the start and stop of a matrix inside a matrix
For example theres a matrix of W and B, B is a square inside the whole matrix:
5 6
WWBBBW
WWBBBW
WWBBBW
WWWWWW
WWWWWW
i want to get the middle of:
BBB
BBB
BBB
which should output me:
2 4
because the matrix is in another matrix
My idea was to take the square and make the middle B1
then index it inside of the whole matrix.
I've tried the code as following which i made.
def middle(m):
# for getting the middle of BlackPart
n = len(m)//2
n1 = len(m[n])//2
m[n][n1] = "B1"
return m
def find(matrix, list1):
# for getting B list
for i in range(len(matrix)):
for j in range(len(matrix[i])):
if matrix[i][j] == "B":
list1.append("B")
return list1
def q2():
# for getting matrix
nope = input("").split(" ")
counter1 = 0
m = []
while counter1 < int(nope[0]):
a = input("")
m.append(list(a))
counter1 += 1
# for getting matrix
# below is for getting a square matrix
l = []
find(m, l)
n = int(len(l) ** (1 / 2))
counter = 0
for i in range(len(l) // 3):
l += [l[counter * n: counter * n + n]]
counter += 1
middle(m)
q2()
But it was too complicated and confused me, then i ended up noting it as a failure.
Please tell me if there is an easier way to do this.
Thanks
There you go, I figured it out:
matrix = """WWBBBW
WWBBBW
WWBBBW
WWWWWW
WWWWWW"""
bstart = 0
bend = 0
bfound = False
bposition_x = 0
bposition_y = 0
bstartline = 0
bfoundinlines = []
matrix = matrix.split("\n")
for linenum, line in enumerate(matrix):
bfound = False
for index, x in enumerate(line):
if x == "B" and bfound == False:
bstart = index
bfound = True
bfoundinlines.append(linenum)
elif x == "W" and bfound == True:
bend = index - 1
bstartline += 1
bposition_x = int((bstart + bend)/2 + 1)
bposition_y = int(round(len(bfoundinlines)/2)) # 1 already added (counting from 1)
if len(bfoundinlines) == 1: bposition_y = 2 #python rounds 0.5 to 0
print("from top:",bposition_y,"from left",bposition_x)
Below is the code I have written. It says the list assingment index is out of range. I can't seem to figure out where I've gone wrong here. Maybe someone could elaborate as to where the error is.
def genarator(x, y):
square = x * y
matrix = [[]]
matrix2 = []
i = 0
while square > 0:
j = 0
while x > j:
matrix[i][j] = int(raw_input("Enter the matrix number"))
j = 1
i = 1
square = -square
matrix2 = matrix2 + matrix
return matrix2
def main():
matrix3 = []
x = int(raw_input("Enter the width of your matrix"))
y = int(raw_input("Enter the Length of your matrix"))
matrix3 = genarator(x, y)
print(matrix3)
return 0
main()
###EDIT###########I SOLVED THE PROBLEM
def generator(x, y):
matrix = [[0 for i in range(x)] for j in range(y)]
turns = x
i = 0
j = 0
while turns > 0:
while i < x:
while j < y:
matrix[i][j] = int(raw_input("Enter Array values"))
j += 1
j = 0
i += 1
turns -= 1
print(matrix)
def main():
x = int(raw_input("Enter array length"))
y = int(raw_input("Enter array width"))
print(generator(x, y))
return 0
main()
If you try the following snippet:
x = []
x[0] = 4
You will see that there is an error. The reason for this is that x[0] has not yet been defined, so it can not be modified. To fix your error, you will have to append something to your list before modifying it:
def genarator(x, y):
square = x * y
matrix = [[]]
matrix2 = []
i = 0
while square > 0:
matrix.append([])
j = 0
while x > j:
matrix[-1].append(int(raw_input("Enter the matrix number")))
j = 1
i = 1
square = -square
matrix2 = matrix2 + matrix
return matrix2
I also modified the indexing because -1 will give you the last item, which is what you want.