Finding area of subspace of 2d array? - python

I am trying to make a function that given a 2d array of either 0's or 1's and a set of coordinates, returns the area of the selected region with the same value as the given coordinates.
For example given the array:
[[0, 1, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 1, 0, 0, 0]]
and the coordinates [0,0], it would return 5.
I have tried a simple DFS but am running into issues where it runs over the same spots multiple times, returning an abnormally large area like 330.

I actually figured this one out!
Basically it follows a BFS search looking only up, down, left, and right.
The part on finding the neighbours was taken from this answer to another question.
def find_area(arr, x, y):
queue = [(x,y)]
visited = []
w = len(arr[0])
h = len(arr)
area = 0
while len(queue) != 0:
curr = queue.pop(0)
print(curr)
x = curr[0]
y = curr[1]
if arr[curr[0]][curr[1]] == 0 and curr not in visited:
area += 1
visited.append(curr)
neighbors = [(x+a[0], y+a[1])
for a in [(-1,0), (1,0), (0,-1), (0,1)]
if ((0 <= x+a[0] < w) and (0 <= y+a[1] < h))]
for i in neighbors:
if i not in visited:
queue.append(i)
return area

Related

python function to count nonzero patches in array

For a given array (1 or 2-dimensional) I would like to know, how many "patches" there are of nonzero elements. For example, in the array [0, 0, 1, 1, 0, 1, 0, 0] there are two patches.
I came up with a function for the 1-dimensional case, where I first assume the maximal number of patches and then decrease that number if a neighbor of a nonzero element is nonzero, too.
def count_patches_1D(array):
patches = np.count_nonzero(array)
for i in np.nonzero(array)[0][:-1]:
if (array[i+1] != 0):
patches -= 1
return patches
I'm not sure if that method works for two dimensions as well. I haven't come up with a function for that case and I need some help for that.
Edit for clarification:
I would like to count connected patches in the 2-dimensional case, including diagonals. So an array [[1, 0], [1, 1]] would have one patch as well as [[1, 0], [0, 1]].
Also, I am wondering if there is a build-in python function for this.
The following should work:
import numpy as np
import copy
# create an array
A = np.array(
[
[0, 1, 1, 1, 0, 1],
[0, 0, 1, 0, 0, 0],
[1, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 1],
[0, 0, 1, 0, 0, 1]
]
)
def isadjacent(pos, newpos):
"""
Check whether two coordinates are adjacent
"""
# check for adjacent columns and rows
return np.all(np.abs(np.array(newpos) - np.array(pos)) < 2):
def count_patches(A):
"""
Count the number of non-zero patches in an array.
"""
# get non-zero coordinates
coords = np.nonzero(A)
# add them to a list
inipatches = list(zip(*coords))
# list to contain all patches
allpatches = []
while len(inipatches) > 0:
patch = [inipatches.pop(0)]
i = 0
# check for all points adjacent to the points within the current patch
while True:
plen = len(patch)
curpatch = patch[i]
remaining = copy.deepcopy(inipatches)
for j in range(len(remaining)):
if isadjacent(curpatch, remaining[j]):
patch.append(remaining[j])
inipatches.remove(remaining[j])
if len(inipatches) == 0:
break
if len(inipatches) == 0 or plen == len(patch):
# nothing added to patch or no points remaining
break
i += 1
allpatches.append(patch)
return len(allpatches)
print(f"Number of patches is {count_patches(A)}")
Number of patches is 5
This should work for arrays with any number of dimensions.

Google ORTools CP-SAT | Get list of index of 1's from a list of ortools-variables

I want to convert [0, 0, 1, 0, 1, 0, 1, 0] to [2, 4, 6] using ortools.
Where "2", "4", "6" in the second list are the index of "1" in the first list.
Using the below code I could get a list [0, 0, 2, 0, 4, 0, 6, 0]. How can I get [2, 4, 6]?
from ortools.sat.python import cp_model
model = cp_model.CpModel()
solver = cp_model.CpSolver()
work = {}
days = 8
horizon = 7
for i in range(days):
work[i] = model.NewBoolVar("work(%i)" % (i))
model.Add(work[0] == 0)
model.Add(work[1] == 0)
model.Add(work[2] == 1)
model.Add(work[3] == 0)
model.Add(work[4] == 1)
model.Add(work[5] == 0)
model.Add(work[6] == 1)
model.Add(work[7] == 0)
v1 = [model.NewIntVar(0, horizon, "") for _ in range(days)]
for d in range(days):
model.Add(v1[d] == d * work[d])
status = solver.Solve(model)
print("status:", status)
vec = []
for i in range(days):
vec.append(solver.Value(work[i]))
print("work",vec)
vec = []
for v in v1:
vec.append(solver.Value(v))
print("vec1",vec)
You should see this output on the console,
status: 4
work [0, 0, 1, 0, 1, 0, 1, 0]
vec1 [0, 0, 2, 0, 4, 0, 6, 0]
Thank you.
Edit:
I also wish to get a result as [4, 6, 2].
For just 3 variables, this is easy. In pseudo code:
The max index is max(work[i] * i)
The min index is min(horizon - (horizon - i) * work[i])
The medium is sum(i * work[i]) - max_index - min_index
But that is cheating.
If you want more that 3 variable, you will need parallel arrays of Boolean variables that indicate the rank of each variable.
Let me sketch the full solution.
You need to build a graph. The X axis are the variables. The why axis are the ranks. You have horizontal arcs going right, and diagonal arcs going right and up. If the variable is selected, you need to use a diagonal arc, otherwise an horizontal arc.
If using a diagonal arc, you will assign the current variable to the rank of the tail of the arc.
Then you need to add constraints to make it a contiguous path:
mass conservation at each node
variable is selected -> one of the diagonal arc must be selected
variable is not selected -> one of the horizontal arc must be selected
bottom left node has one outgoing arc
top right node has one incoming arc

Backtracking pathinding problem in Python

Recently, I've found out about backtracking and without much thinking started on the book from the guy who has shown some Sudoku backtracking tricks (https://www.youtube.com/watch?v=G_UYXzGuqvM&ab_channel=Computerphile. Unfortunately, I'm stuck with the first backtracking problem without the solution.
The problem is formulated accordingly:
Use backtracking to calculate the number of all paths from the bottom left to the top right corner in a
x * y-grid. This includes paths like https://imgur.com/3t3Np4M. Note that every point can only be visited once. Write a function np(x,y) that returns the number of paths in a x*y-grid. E.g. np(2,3) should return 38. Hint: Create a grid of booleans where you mark the positions already visited.
Whatever I change in this short block of code I'm landing nowhere near 38.
```
grid = [[0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0]]
solution = 0
def number_of_paths(x, y):
global solution
global grid
for i in range(0, x):
for j in range(0, y):
if grid[i][j] == 0:
grid[i][j] = 1
number_of_paths(x, y)
grid[i][j] = 0
solution += 1
return
if __name__ == '__main__':
number_of_paths(2, 3)
print(grid)
print(solution)```
That's a sample solution with solution with Sudoku solver.
```
grid = [[5, 3, 0, 0, 7, 0, 0, 0, 0],
[6, 0, 0, 1, 9, 5, 0, 0, 0],
[0, 9, 8, 0, 0, 0, 0, 6, 0],
[8, 0, 0, 0, 6, 0, 0, 0, 3],
[4, 0, 0, 8, 0, 3, 0, 0, 1],
[7, 0, 0, 0, 2, 0, 0, 0, 6],
[0, 6, 0, 0, 0, 0, 2, 8, 0],
[0, 0, 0, 4, 1, 9, 0, 0, 5],
[0, 0, 0, 0, 8, 0, 0, 7, 9]]
import numpy as np
def possible(y, x, n):
global grid
for i in range(0, 9):
if grid[y][i] == n:
return False
for i in range(0, 9):
if grid[i][x] == n:
return False
x0 = (x // 3) * 3
y0 = (y // 3) * 3
for i in range(0, 3):
for j in range(0, 3):
if grid[y0 + i][x0 + j] == n:
return False
return True
def solve():
global grid
for y in range(9):
for x in range(9):
if grid[y][x] == 0:
for n in range(1, 10):
if possible(y, x, n):
grid[y][x] = n
solve()
# backtracking - bad choice
grid[y][x] = 0
return
print(np,matrix(grid))
input("More?")```
A few suggestions:
You might want to use a set for a grid, adding a square as soon as it is visited, if it is not a member of the set yet.
The counter and the grid can be global but it would probably be easier for you to take them as arguments for the function at first. After the solution is clearer you can worry about those details.
You are going about the problem the wrong way. It would be good to have one function calculating the number of paths from the origin to the destination (by calling the function for the neighbors that have not been visited yet. Make sure you update the grid). On top of that you can have a function that calls the path function for every combination of origin and destination. A small tip: You do not have to calculate the same path in reverse direction! You can have a map of calculate sums of paths. If the opposite direction has been calculate, don't bother. Later, double the amount of paths by 2.
Good luck!
I will show you a solution on a coordinate system where (0,0) is the topleft and (maxY,maxX) is the bot right. Going right increases x and going down increases y.
1- If you are trying to solve the exact maze in the image, then your grid array shape is wrong. Notice that you are travelling between corners of the squares, there are 4 points you can be horizontally and 3 points you can be vertically.
2- Hint is telling you about using a boolean mask for visited state, you already have a grid array so a separate array is not necessary.
3- The main problem with your code is how you are progressing in the maze. The loop structure
for i in range(0, x):
for j in range(0, y):
does not make sense because when you are in a position (x, y), you can only move in 4 main directions (right, up, left, down). However this loops make it look like you are trying to branch into all positions behind you, which is not valid. In my code I will explicity show about this traverse stuff.
grid = [[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 0, 0, 0]]
# number of solutions
solution = 0
# maximum values of x and y coordinates
maxX = len(grid[0])-1
maxY = len(grid)-1
# endpoint coordinates, top(y=0) right(x=maxX) of the maze
endX = maxX
endY = 0
# starting point coordinates, bottom(y=maxY) left(x=0) of the maze
mazeStartX = 0
mazeStartY = maxY
def number_of_paths(startX, startY):
global solution
global grid
global mask
# if we reached the goal, return at this point
if (startX == endX and startY == endY):
solution += 1
return
# possible directions are
#RIGHT (+1x, 0y)
#UP (0x, -1y)
#LEFT (-1x, 0y)
#DOWN (0x, +1y)
# I use a direction array like this to avoid nested ifs inside the for loop
dx = [1, 0, -1, 0]
dy = [0, -1, 0, 1]
for d in range(len(dx)):
newX = startX + dx[d]
newY = startY + dy[d]
# out of maze bounds
if (newX < 0 or newY < 0):
continue
# out of maze bounds
if (newX > maxX or newY > maxY):
continue
if (grid[newY][newX] == 1):
# this are is already visited
continue
else:
# branch from this point
grid[newY][newX] = 1
number_of_paths(newX, newY)
grid[newY][newX] = 0
if __name__ == '__main__':
number_of_paths(mazeStartX, mazeStartY)
print(grid)
print(solution)

finding shorted path to a position using python

Guys this is a question i got part of google foobar challege
You have maps of parts of the space station, each starting at a prison
exit and ending at the door to an escape pod. The map is represented
as a matrix of 0s and 1s, where 0s are passable space and 1s are
impassable walls. The door out of the prison is at the top left
(0,0)(0,0) and the door into an escape pod is at the bottom right
(w−1,h−1)(w−1,h−1).
Write a function answer(map) that generates the length of the shortest
path from the prison door to the escape pod, where you are allowed to
remove one wall as part of your remodeling plans. The path length is
the total number of nodes you pass through, counting both the entrance
and exit nodes. The starting and ending positions are always passable
(0). The map will always be solvable, though you may or may not need
to remove a wall. The height and width of the map can be from 2 to 20.
Moves can only be made in cardinal directions; no diagonal moves are
allowed.
Test cases
Input:
maze = [[0, 1, 1, 0], [0, 0, 0, 1], [1, 1, 0, 0], [1, 1, 1, 0]]
Output:
7 Input:
maze = [[0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0]] Output:
11
and this is its answer i got online
from collections import deque
class Node:
def __init__(self, x, y, saldo, grid):
self.x = x
self.y = y;
self.saldo = saldo
self.grid = grid
def __hash__(self):
return self.x ^ self.y
def __eq__(self, other):
return self.x == other.x and self.y == other.y and self.saldo == other.saldo
def get_neighbors(self):
neighbors = []
x = self.x
y = self.y
saldo = self.saldo
grid = self.grid
rows = len(grid)
columns = len(grid[0])
if x > 0:
wall = grid[y][x - 1] == 1
if wall:
if saldo > 0:
neighbors.append(Node(x - 1, y, saldo - 1, grid))
else:
neighbors.append(Node(x - 1, y, saldo, grid))
if x < columns - 1:
wall = grid[y][x + 1] == 1
if wall:
if saldo > 0:
neighbors.append(Node(x + 1, y, saldo - 1, grid))
else:
neighbors.append(Node(x + 1, y, saldo, grid))
if y > 0:
wall = grid[y - 1][x] == 1
if wall:
if saldo > 0:
neighbors.append(Node(x, y - 1, saldo - 1, grid))
else:
neighbors.append(Node(x, y - 1, saldo, grid))
if y < rows - 1:
wall = grid[y + 1][x]
if wall:
if saldo > 0:
neighbors.append(Node(x, y + 1, saldo - 1, grid))
else:
neighbors.append(Node(x, y + 1, saldo, grid))
return neighbors
def answer(maze):
rows = len(maze)
columns = len(maze[0])
source = Node(0, 0, 1, maze)
queue = deque([source])
distance_map = {source: 1}
while queue:
current_node = queue.popleft()
if current_node.x == columns - 1 and\
current_node.y == rows - 1:
return distance_map[current_node]
for child_node in current_node.get_neighbors():
if child_node not in distance_map.keys():
distance_map[child_node] = distance_map[current_node] + 1
queue.append(child_node)
and this description was there along with solution Basically you need
a breadth-first search with a minor tweak:
each node represents a cell in the grid (x- and y-coordinates), each
node knows its "saldo" (how many walls it may penetrate). What comes
to that saldo, if a node has zero saldo, it may not generate those its
neighbors that are occupied by wall. If saldo is s>0s>0, and the node
has a wall neighbor node uu, uu is generated with saldo s−1s−1.
The rest is breadth-first search: as soon you remove the exit node
from the queue, just print its distance from the starting node:
But even after a long effort i am unable to understand what "saldo"
means here and how it affects the result
i did not undertand the logic of its use
you are allowed to remove one wall as part of your remodeling plans.
Apparently, the variable saldo represents the number of walls you can remove during your escape.
It is decremented when you remove a wall; tests are made to assert whether you are still allowed to remove a wall.

Why my list is not changing the way it's supposed to?

I'm trying to build a method in my class Circle which gets a matrix (represented by a list of lists, each sublist represents a row) as an input.
The matrix has zero in every cell, and I'm supposed to place my circle in the center of the matrix and check if the (i,j) cell which represents the (i,j) point is contained in the circle, but for some reason I get a different output.
Here is an example:
mat = [[0 for j in range(5)] for i in range(7)]
Circle(40, 10, 1).draw(mat)
The output I expect is:
[[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]]
But the output I get is:
[[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 1],
[0, 0, 0, 1, 1],
[0, 0, 0, 0, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]]
Here's my code:
class Point():
""" Holds data on a point (x,y) in the plane """
def __init__(self, x=0, y=0):
assert isinstance(x,(int, float)) and isinstance(y,(int, float))
self.x = x
self.y = y
class Circle():
""" Holds data on a circle in the plane """
def __init__(self,*args):
if len(args)==2:
if isinstance(args[0],Point) and isinstance(args[1],(float,int)):
assert args[1]>0
self.center= args[0]
self.radius= args[1]
if len(args)==3:
assert args[2]>0
self.a=args[0]
self.b=args[1]
self.center= Point(self.a,self.b)
self.radius= args[2]
def contains(self,check):
if isinstance(check,(Point)):
if math.sqrt((self.center.x-check.x)**2 + (self.center.y-check.y)**2) <= self.radius:
return True
if isinstance(check,Circle):
test= math.sqrt((self.center.x-check.center.x)**2 + (self.center.x-check.center.x)**2)
if test < (abs((self.radius)-(check.radius))):
return True
else:
return False
def draw(self,mat):
n=len(mat)
m=len(mat[0])
newcircle=Circle((int(m/2)+1),(int(n/2)+1),self.radius)
for i,lst in enumerate(mat):
for j,val in enumerate(lst):
if newcircle.contains(Point(i,j)):
mat[i][j]=1
You're not placing your circle in the middle of the matrix.
newcircle=Circle((int(m/2)+1),(int(n/2)+1),self.radius)
should be
newcircle=Circle((int(n/2)),(int(m/2)),self.radius)
or possibly, since there is no need to use just integers here.
newcircle=Circle((n-1)/2.0,(m-1)/2.0,self.radius)
To draw a circle, you create a new circle in the center of the matrix, you check the cells that are inside and then you turn the value to 1 for those inside.
1) First, there is a problem with the function contains :
def contains(...):
if (cond1):
if (cond11)
return True
if (cond2):
if (cond21)
return True
else:
return False
If cond2 is true and cond21 is false, you will get a None.
To be more Pythonic, try:
def contains(...):
if (cond1) and (cond11):
return True
elif (cond2) and (cond21):
return True
else:
return False
You are sure in this case to have a True or a False.
2) Copy / Paste mistake
There is a copy / paste mistake in the function contains when instance is Circle.
You have forgotten to turn y into x.
3) Function draw
All you need for this function is the radius.
Be careful with the use of integers and floats; we have:
(int(5 / 2)) == (5 / 2) != (5 / 2.)
To be sure to have a float, write the divisor as a float 2. instead of 2
Your circle is created on a matrix which rows and columns indices start with 1 if you define the center using int(len(mat) / 2.) + 1.
Don't forget that enumerate indices starts at 0, not 1.
So, int((len(mat) - 1) / 2.) + 1 (which is the same as len(mat) / 2) would be more accurate.
Seriously, Taha!

Categories

Resources