Related
I have a simple exam to pass where I have to write a program where I create a binary matrix and a pattern and I have to find the pattern inside the matrix and to highlight it if found. I can't use any external library - specially numpy.
What I have atm are just some highlighted numbers that don't match my pattern. I don't know how to continue. Can someone help me please?
My code is this:
pattern.py:
# to create a matrix from an array of arrays
def create(n, m, data):
matrix = []
for i in range(n): #n=rows number
row = []
for j in range(m): #m=columns number
if data[j] not in matrix:
row.append(data[m * i + j])
matrix.append(row)
return matrix
# to colour the elements in 1 inside the pattern
def colora(element):
if element == 1:
element = '\u001b[36m1'
elif element == 0:
element = '\u001b[00m0'
return element
# my pattern forms an I vertically - I should also find it if horizontal
def patternI():
array = [1, 0, 0, 1, 0, 0, 1, 0, 0]
pattern = create(3, 3, array)
for i in range(3):
for j in range(3):
col_element = colora(pattern[i][j])
print(col_element, end=" ")
# print(pattern[i][j], end=" ")
print()
return pattern
main.py:
import patterns
import ricerca
# to create and print the matrix
def matrix():
m = 5
n = 5
array = [0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0]
matrix = patterns.create(n, m, array)
for i in range(n):
for j in range(m):
print(matrix[i][j], end=" ")
print()
return matrix
def main():
print("pattern:")
pattern = patterns.patternI()
print("\r")
print("matrix:")
matrice = matrix()
print("\r")
print("risult:")
newArray=ricerca.ricerca(pattern, matrice)
main()
ricerca.py:
import patterns
# to search the pattern (m_piccola) inside the (m_grande)
def ricerca(m_piccola, m_grande) :
daricordare = [] #an array to fill with the position of the matrix which combines a little with the pattern
for jR in range(len(m_piccola) - 1) :
for iR in range(len(m_grande)) :
for iC in range(len(m_grande[0])) :
for jC in range(len(m_piccola[jR])) :
if m_piccola[jR][jC] == 0 :
jC += 1
break
if m_piccola[jR][jC] == 1 :
if m_grande[iR][iC] == m_piccola[jR][jC] :
daricordare.append((iR, iC))
else :
iC += 1
data = coloraQuelliDelPattern(daricordare, m_grande)
for i in range(5):
for j in range(5):
print(data[i][j], end = " ")
print()
return data
# to highlight the numbers in the array which are inside "daricordare"
def coloraQuelliDelPattern(daricordare, array) :
for i in range(len(daricordare) - 2) :
a = daricordare[i]
b = daricordare[i + 1]
c = daricordare[i + 2]
if b[1] == a[1] + 1 and c[1]==a[1] + 2:
array[a[0]][a[1]] = "\u001b[36m1"
array[b[0]][b[1]] = "\u001b[36m1"
array[c[0]][c[1]] = "\u001b[36m1"
if b[1] != a[1] + 1 or c[1] != a[1] + 2 :
array[a[0]][a[1]] = "\u001b[00m1"
array[b[0]][b[1]] = "\u001b[00m1"
array[c[0]][c[1]] = "\u001b[00m1"
return array
import numpy as np
group = np.array([0,0,0,0,1,1,1,1,1,1,2,2,2,2])
array = np.array([1,2,3,0,0,2,0,3,4,0,0,0,0,1])
targt = np.array([1,1,1,0,0,2,2,2,2,0,0,0,0,1])
def func(group: np.array, array: np.array):
pass
return array
Step 1: Find the indexes of the first & last non-zero elements for
each group, i.e. (0, 2) for group 0, (5, 8) for group 1, (13, 13) for
group 2.
Step 2: Replace the slices between each indexes with the
first nan-zero value within each group, i,e, group 0 [1,2,3,0] ->
[1,1,1,0], group 1 [0,2,0,3,4,0] -> [0,2,2,2,2,0], group 2 no change.
How could I do it without spliting arrays or iteration?
[Solutions]
def first_nonzero_index(arr: np.array, axis: int, mode: str = None, invalid_value: float = -1):
mask = arr != 0
if mode is None or mode == "head":
return np.where(mask.any(axis=axis), mask.argmax(axis=axis), invalid_value)
else:
return np.where(mask.any(axis=axis),
arr.shape[axis] - np.flip(mask, axis=axis).argmax(axis=axis) - 1, invalid_value)
def func1(group: np.array, array: np.array): # my solution
group_size = np.bincount(group)[:-1]
group_idx_end = np.cumsum(group_size)
array_split = np.split(array, group_idx_end)
concat_list = []
for arr in array_split:
idx_start = first_nonzero_index(arr, axis=0, mode="head")
if idx_start != -1:
idx_end = first_nonzero_index(arr, axis=0, mode="tail") + 1
arr_ffill_first_nonzero = np.zeros_like(arr, dtype=float)
arr_ffill_first_nonzero[idx_start:idx_end] = arr[idx_start]
concat_list.append(arr_ffill_first_nonzero)
else:
concat_list.append(arr)
return np.hstack(concat_list)
def fill(val): # contributor #d.b
inds = np.where(val != 0)[0]
start, end = min(inds), max(inds)
fill_val = val[start]
val[start:end + 1] = fill_val
return val
def split(val, grp): # contributor #d.b
arr = []
curr = [val[0]]
for i in range(1, len(val)):
if grp[i] == grp[i - 1]:
curr.append(val[i])
else:
arr.append(np.array(curr))
curr = [val[i]]
if curr:
arr.append(np.array(curr))
return arr
def func2(group, array): # contributor #d.b
return np.concatenate([fill(x) for x in split(array, group)])
def func3(group: np.array, array: np.array): # my solution
group_size = np.bincount(group)[:-1]
group_idx_end = np.cumsum(group_size)
array_split = np.split(array, group_idx_end)
concat_list = []
for arr in array_split:
idx = np.where(arr != 0)[0]
start, end = min(idx), max(idx)
arr[start:end + 1] = arr[start]
concat_list.append(arr)
return np.hstack(concat_list)
def func4(group, array): # contributor #d.b
nonzero = (array != 0)
_, marker_idx = np.unique(group[nonzero], return_index=True)
nonzero_idx = np.arange(len(array))[nonzero]
#STEP 2
starts = np.minimum.reduceat(nonzero_idx, marker_idx)
ends = np.maximum.reduceat(nonzero_idx, marker_idx)
#STEP 3
values = array[starts]
out = np.zeros_like(array)
out[starts] = values
#check the case we can't insert the last negative value
if ends[-1]+1==len(array):
out[ends[:-1]+1] = -values[:-1]
else:
out[ends+1] = -values
return np.cumsum(out)
Output: [1. 1. 1. 0. 0. 2. 2. 2. 2. 0. 0. 0. 0. 1.]
[Test]
n = 2000
group = np.array([0,0,0,0,1,0,0,0,0,1,0,0,0,0,0]*n) # 30000
array = np.array([1,2,3,0,0,2,0,3,0,0,0,0,0,1,0]*n)
group = np.cumsum(group)
Run 100 times:
func1
func2
func3
func4 (Best solution)
9.70s
2.54s
1.99s
0.03s
group = np.array([0,0,0,0,1,1,1,1,1,1,2,2,2,2])
array = np.array([1,2,3,0,0,2,0,3,4,0,0,0,0,1])
targt = np.array([1,1,1,0,0,2,2,2,2,0,0,0,0,1])
You can do the following steps:
STEP 1. Find indices of nonzero items of array and mark the startings of new groups
nonzero_idx -> [*0,1,2,/,*/,5,/,7,8,/,*/,/,/,13] (cross out slashes)
marker_idx -> [0, 4, 10]
STEP 2. Find starting and ending indices for each group, use np.ufunc.reduceat
starts -> [ 0, 5, 13]
ends -> [ 2, 8, 13]
STEP 3. Think of an out array such that np.cumsum(out) collapses into target array. Like so:
[1,0,0,-1,0,2,0,0,0,-2,0,0,0,1] -> [1,1,1,0,0,2,2,2,2,0,0,0,0,1]
Now, code:
#STEP 1
nonzero = (array != 0)
_, marker_idx = np.unique(group[nonzero], return_index=True)
nonzero_idx = np.arange(len(array))[nonzero]
#STEP 2
starts = np.minimum.reduceat(nonzero_idx, marker_idx)
ends = np.maximum.reduceat(nonzero_idx, marker_idx)
#STEP 3
values = array[starts]
out = np.zeros_like(array)
out[starts] = values
#check the case we can't insert the last negative value
if ends[-1]+1==len(array):
out[ends[:-1]+1] = -values[:-1]
else:
out[ends+1] = -values
>>> np.cumsum(out)
array([1, 1, 1, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 1], dtype=int32)
No loops needed!
1
import numpy as np
import pandas as pd
def foo(s):
chk = np.where(s > 0)[0]
start = min(chk)
end = max(chk)
ans = [True if (start <= ind <= end) else False for ind in range(len(s))]
return ans
pd.Series(array).groupby(group).transform(
lambda x: x.mask(foo(x), x[x > 0].iloc[0])).to_numpy()
# array([1, 1, 1, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 1])
2
def split(val, grp):
inds = np.where(np.append(False, grp[1:] != grp[:-1]))[0]
return np.array_split(val, inds)
def fill(val):
inds = np.where(val > 0)[0]
start, end = min(inds), max(inds)
val[start:end + 1] = val[start]
return val
np.concatenate([fill(x) for x in split(array, group)])
# array([1, 1, 1, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 1])
I've tried posting this question onto other websites and I have received, lightly to say, little to no feedback that helps.
This is the question at hand: Image Here
The code is asking to divide the numbers in a 2d array by 3, and if they're not divisible by 3, then return them to a 0 in the array.
This is the current code I have:
a = [[34,38,50,44,39],
[42,36,40,43,44],
[24,31,46,40,45],
[43,47,35,31,26],
[37,28,20,36,50]]
#print it
def PrintIt(x):
for r in range(len(x)):
for c in range(len(x[0])):
print(x[r][c], end = " ")
print("")
#is supposed to divide and return the sum. but does not
def divSUM(x):
sum = 3
for r in range(len(x)):
for c in range(len(x[0])):
sum = x[r][c] % 3
if sum != 0:
return 0
return sum
divSUM(a)
The divide (divSUM) returns as nothing. and I'm unsure if numpy would work, every time I try it says its outdated so I assume it doesn't. Any help would be nice, but I'm mainly having issues with putting the undivisble by 3 numbers to a 0.
When return is run, the function is stopped. You can append all the values to a list and return that.
Try this:
a = [[34,38,50,44,39],
[42,36,40,43,44],
[24,31,46,40,45],
[43,47,35,31,26],
[37,28,20,36,50]]
#print it
def PrintIt(x):
for r in x:
for c in r:
print(c, end = " ")
print()
def divSUM(x):
sum = 3
output = []
for r in x:
tmp_lst = []
for c in r:
sum = c % 3
if sum != 0:
tmp_list.append(0)
else:
tmp_list.append(sum)
output.append(tmp_lst)
return output
print(divSUM(a))
(What I understood is that if the number % 3 == 0 you let the same number otherwise you put 0.)
I propose to work with numpy it's easy to do it in a simple line :
import numpy as np
a = np.array([[34,38,50,44,39],
[42,36,40,43,44],
[24,31,46,40,45],
[43,47,35,31,26],
[37,28,20,36,50]])
result = np.where(a%3 ==0, a, 0)
>>>result
array([[ 0, 0, 0, 0, 39],
[42, 36, 0, 0, 0],
[24, 0, 0, 0, 45],
[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 36, 0]])
Otherwise, the problem with your code is that you wrongly choose the place where to put return.
So, you have to do this:
def divSUM(x):
result = []
for r in x:
tmp = []
for c in r:
if c % 3 != 0:
tmp.append(0)
else:
tmp.append(c)
result.append(tmp)
return result
I am currently creating minesweeper, and have got to the part where i have to create a function that once the player clicks on a cell that is empty, it reveals that cell and all other empty cells including the numbered cells that surround the cluster of empty cells ( come on guys, you know minesweeper :) my approach to programming this game, is to have a list of lists which contain values, 0 is empty, 1-8 is how many bombs are touching that cell, and 50 is a bomb. i call this grid. I then use this grid to map out the whole game. That grid is then hidden with a use of another list of lists, which all contain booleans. True is hidden, False is revealed. I call this boolgrid. for example when a player clicks on cell[2][10] the boolgrid[2][10] turns False, revealing that cell.
When an empty cell is selected, the surrounding cells have to be revealed, which in some cases, are also empty, so the surrounding cells of THAT cell need to be revealed, and so on. My problem is writing a function that supports that, i have tried many things, like creating a list of tuples of cell coordinates where the cell == 0, and then a new list to hold all of the new tuples which can eventually be used to turn all of the corresponding boolgrid cells to False. it doesnt work very well, is messy, unpythonic, takes up alot of memory.
I would be most grateful to anyone who can help me with a function that gives me some pythonic way of achieving this.
below is some stripped down code, which contains the bare elements. the code contains all 0's in the grid so every bool should turn False
# used to hold values to denote what the cell contains,
grid = [[0 for x in range(30)] for x in range(15)]
# used to check what cell is hidden, true is hidden, false is revealed,
booleangrid = [[True for x in range(30)] for x in range(15)]
list_to_hold_tuples = []
def find_connected_0_value_cells(cell):
i = 5 # used as an example in the real program these are whatever cell the player selects
j = 10 # as above
# this function should be given an argument of a cell that the player selected.
# reveal the surrounding cells, AND THEN take each surrounding cell, and go through
# the same process again, until every surrounding cell containing 0, and its surrounding
# cells containing zero have been revealed.
# i know i need some kind of loop, but cannot seem to build it.
# currently this function just reveals the cell and its immediate surrounding cells
if cell[i][j] == 0:
s = i, j + 1
t = i, j - 1
u = i - 1, j - 1
v = i - 1, j
w = i - 1, j + 1
x = i + 1, j - 1
y = i + 1, j
z = i + 1, j + 1
tempholding = [s, t, u, v, w, x, y, z]
for i in tempholding:
list_to_hold_tuples.append(i)
def reveal_empty_cells():
for a,b in list_to_hold_tuples:
booleangrid[a][b] = False
print(booleangrid)
find_connected_0_value_cells(grid)
reveal_empty_cells()
I have refactored and got the rest of it to work.
thanks #zettatekdev
grid = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 2, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 3, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 5, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 5, 0, 0, 0, 0, 0, 0]]
list_to_hold_tuples = []
list_change_boolgrid =[]
row = 6
cell = 10
booleangrid = [[True for x in range(cell)] for x in range(row)]
def find_connected_0_value_cells(a, b):
list_to_hold_tuples.append((a, b))
if grid[a][b] == 0:
coord_list = get_surrounding_coords(a, b)
for a,b in coord_list:
if check_coord_values(a, b):
if grid[a][b] != 0:
c = a,b
if c not in list_to_hold_tuples:
list_to_hold_tuples.append(c)
else:
c = a,b
if c not in list_to_hold_tuples:
find_connected_0_value_cells(a,b)
def add_surrounding_cells():
extra_coord = True
for a,b in list_to_hold_tuples:
if grid[a][b] == 0:
coord_list = get_surrounding_coords(a,b, extra_coord)
for i in coord_list:
if i not in list_change_boolgrid:
list_change_boolgrid.append(i)
else:
c = a,b
if c not in list_change_boolgrid:
list_change_boolgrid.append(c)
def reveal_empty_cells():
global booleangrid
for a, b in list_change_boolgrid:
if check_coord_values(a,b):
booleangrid[a][b] = False
def check_coord_values(a,b):
if a == -1 or a >= row:
return False
if b == -1 or b >= cell:
return False
else:
return True
def get_surrounding_coords(a, b, *extra_coord):
c = (a, b + 1)
d = (a, b - 1)
e = (a - 1, b - 1)
f = (a - 1, b)
g = (a - 1, b + 1)
h = (a + 1, b - 1)
i = (a + 1, b)
j = (a + 1, b + 1)
if extra_coord:
k = (a, b)
return [c, d, e, f, g, h, i, j, k]
return [c, d, e, f, g, h, i, j]
find_connected_0_value_cells(3,5)
add_surrounding_cells()
reveal_empty_cells()
print(booleangrid)
Ok, so I worked with your code for a bit, and I was able to make a code that works. I did adjust the grid size just to make it easier to figure out, but it should work for your original size. Just make sure you don't test with a grid of all 0s like in your posted code because it will return an error for too many recursions. Here's the code.
EDIT: I changed the code so now it reveals the surrounding numbers as well
# used to hold values to denote what the cell contains,
grid = [[0,1,2,5,4,6,0,0,0,0],
[0,1,2,5,4,6,0,0,0,0],
[0,1,2,5,4,6,0,0,5,0],
[0,1,2,5,4,6,0,0,0,0],
[0,1,2,5,4,6,0,0,0,0],
[0,1,2,5,4,6,6,0,0,0]]
# used to check what cell is hidden, true is hidden, false is revealed,
booleangrid = [[True for x in range(10)] for x in range(6)]
list_to_hold_tuples = []
def find_connected_0_value_cells(i,j):
list_to_hold_tuples.append((i,j))
if grid[i][j] == 0:
s = (i, j + 1)
t = (i, j - 1)
u = (i - 1, j - 1)
v = (i - 1, j)
w = (i - 1, j + 1)
x = (i + 1, j - 1)
y = (i + 1, j)
z = (i + 1, j + 1)
tempholding = [s, t, u, v, w, x, y, z]
for a in tempholding:
if a[0]>=0 and a[1]>=0 and a[0]<=len(grid)-1 and a[1]<=len(grid[i])-1 and grid[a[0]][a[1]]==0:
if (a[0]!=i and a[1]!=j) and (a not in list_to_hold_tuples):
find_connected_0_value_cells(a[0],a[1])
list_to_hold_tuples.append(a)
elif a[0]>=0 and a[1]>=0 and a[0]<=len(grid)-1 and a[1]<=len(grid[i])-1:
list_to_hold_tuples.append(a) #this part adds surrounding non-zero numbers
def reveal_empty_cells():
global booleangrid
for a,b in list_to_hold_tuples:
if a>=0 and a<len(booleangrid) and b>=0 and b<len(booleangrid[a]):
booleangrid[a][b] = False
for i in booleangrid:
print(i)
find_connected_0_value_cells(5,8)#Just using coordinates of 5,8 for testing, this would be click spot
reveal_empty_cells()
This function is intended to recursively navigate a maze and find the length of the shortest path. The path itself is not necessary, only the length. The maze is represented by a 2d list with values such as
0 1 0 0 0
0 0 0 1 0
0 0 0 1 0
The user starts at (0,0) and must end up at the end of the maze as defined (in my case it is the bottom right cell). 1's represent walls.
def maze(x,y,array,length):
m = len(array)
n = len(array[0])
if x < 0 or y < 0 or x == m or y == n or array[x][y] == 1:
return float("inf")
elif x == m - 1 and y == n - 1:
return length
else:
array[x][y] = 1
up = maze(x - 1,y,array,length + 1)
right = maze(x,y + 1,array,length + 1)
down = maze(x + 1,y,array,length + 1)
left = maze(x,y - 1,array,length + 1)
return min(up,down,left,right)
array = [[0,1,0,0,0],[0,0,0,1,0],[0,0,0,1,0]]
minLength = maze(0,0,array,1)
print(minLength)
I designed it so that it recursively finds all possible paths from each direction (up, down, left and right), and returns the lowest value from all these paths with each step of the way. It returns inf for any path that is not valid.
For this specific array, it returns 11, which is false, it should be 9. I do not believe it is merely a mathematical error, as I tried printing each step of the way and it is not recognizing certain paths (it returns inf for paths that most definitely have options).
I can't seem to find where my code is going wrong, it seems like it should properly return the value, but in practice it does not.
array is a reference to the original array, not a local copy. See any of the on-line tutorials on how Python passes function arguments, or how it handles lists. You can see the effect by printing array in your main program after the call to maze:
Final Maze [
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 0]
]
Fixing this is relatively easy: copy the nested list and use that copy locally.
from copy import deepcopy
def maze(x,y,array,length):
m = len(array)
n = len(array[0])
if x < 0 or y < 0 or x == m or y == n or array[x][y] == 1:
return float("inf")
elif x == m - 1 and y == n - 1:
return length
else:
new_maze = deepcopy(array)
new_maze[x][y] = 1
up = maze(x - 1,y,new_maze,length + 1)
right = maze(x,y + 1,new_maze,length + 1)
down = maze(x + 1,y,new_maze,length + 1)
left = maze(x,y - 1,new_maze,length + 1)
return min(up,down,left,right)
array = [[0,1,0,0,0],[0,0,0,1,0],[0,0,0,1,0]]
minLength = maze(0,0,array,1)
print("Final Maze", array)
print(minLength)
The output from this is (edited for readability again)
Final Maze [
[0, 1, 0, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 1, 0]
]
9