Find sum of elements within the area of 45 degree rotated rectangle - python

Given a matrix of integers, we'd like to consider the sum of the elements within the area of a 45° rotated rectangle.
More formally, the area is bounded by two diagonals parallel to the main diagonal and two diagonals parallel to the secondary diagonal.
The dimensions of the rotated rectangle are defined by the number of elements along the borders of the rectangle. Given integers a and b representing the dimensions of the rotated rectangle, and matrix (a matrix of integers), your task is to find the greatest sum of integers contained within an a x b rotated rectangle.
Note: The order of the dimensions is not important - consider all a x b and b x a rectangles.
matrix = [[1, 2, 3, 4, 0],
[5, 6, 7, 8, 1],
[3, 2, 4, 1, 4],
[4, 3, 5, 1, 6]]
a = 2, and b = 3, the output should be rotatedRectSum(matrix, a, b) = 36.
I need help to understand how range(w - 1, rows - h + 1) and range(0, cols - (h + w - 1) + 1) are calculated?
def rotatedRectSum(matrix, a, b):
rows, cols = len(matrix), len(matrix[0])
maxArea = float("-inf")
# go through possible rectangles along both diagonals
for w, h in [(a, b), (b, a)]:
# go through possible "anchors", which is the left top coordinate of the rectangle candidate
for i in range(w - 1, rows - h + 1):
for j in range(0, cols - (h + w - 1) + 1):
area = 0
# sum up the long diagonals
for p in range(w): # go to next long diagonal
for q in range(h): # go down current diagonal
area += matrix[i - p + q][j + p + q]
# sum up the short diagonals
k, l = i, j + 1 # note that short diagonals have one less element than long diagonals
for p in range(w - 1):
for q in range(h - 1):
area += matrix[k- p + q][l + p + q]
if (area > maxArea): maxArea = area
return maxArea
More Explanation: Click Here

I need help to understand how range(w - 1, rows - h + 1) and range(0, cols - (h + w - 1) + 1) are calculated?
What the author calls anchor is the leftmost cell in the rotated rectangle that is considered. Below an orange-marked anchor and the corresponding rectangle that goes with it (width is 2, height is 3):
i is the row in which the anchor can be. It is clear that when the width is 2, we cannot have the anchor in the top row as we need at least one row above it to have the necessary space for the top-corner of the rectangle. As the row in which the anchor resides already counts as 1, we need w - 1 more rows above it so to have enough room for the upper part of the rotated rectangle. This explains why the first range starts with w - 1. It means that the anchor cannot be in the first w - 1 rows, and the least possible row where the anchor can be placed is the one with index w - 1.
With a similar reasoning we can deduce how many rows there should at least be below the anchor's row. There should be at least h - 1 rows below it, so to have enough room for the bottom part of the rotated rectangle. This means the greatest possible row index for the anchor's row is rows - h. That means we should have a range(w - 1, rows - h + 1) to iterate all possible row indices for the anchor (remember that the second value given to range is not included in the values it yields).
The reasoning for the second range is the same, but now it concerns the columns where the anchor could possibly be (i.e. the value for j). The anchor can always be in the very first column, as there is no part of the rectangle that comes at the left of it. So that means that valid column numbers for the anchor start at column index 0, hence range(0, ...)
Finally, the number of columns that follows after the anchor's column is w - 1 (for reaching the column with the top peek of the rectangle) and another h - 1 (for reaching from there on the right most cell in the rectangle). So that gives a total of w + h - 2 columns that need to be available at the right of the anchor's column. This means the last possible column index for an anchor is cols - (w + h - 1), and so the range for possible column indices should be defined as range(0, cols - (w + h - 1) + 1).
I hope this clarifies it.

def rotatedRectSum(matrix, a, b):
rows, cols = len(matrix), len(matrix[0])
maxArea = float("-inf")
# go through possible rectangles along both diagonals
for w, h in [(a, b), (b, a)]:
# go through possible "anchors", which is the left top coordinate of the rectangle candidate
for i in range(w - 1, rows - h + 1):
for j in range(0, cols - (h + w - 1) + 1):
area = 0
# sum up the long diagonals
for p in range(w): # go to next long diagonal
for q in range(h): # go down current diagonal
# print(matrix[i - p + q][j + p + q])
area += matrix[i - p + q][j + p + q]
# sum up the short diagonals
k, l = i, j + 1 # note that short diagonals have one less element than long diagonals
for p in range(w - 1):
for q in range(h - 1):
area += matrix[k- p + q][l + p + q]
if (area > maxArea): maxArea = area
return maxArea

Related

How can I speed up/fix this shortest path grid traversal?

I'm working on writing a program to traverse a grid from the bottom left to the top right where valid moves are moving to the right by 1, moving up by 1, or jumping up or right by an amount designated in the jump array. The inputs are the grid in a 2D array format, dimensions of the grid: X and Y, and an array of jumps where the jumps have to be completed in order. The output is the shortest path where the length of the path is the sum of all numbers touched including bottom left and top right.
Example input:
Grid:
[9 9 1]
[9 9 1]
[3 9 1]
Jumps: [1 1] X:3, Y:3
The output for this would be 5 because we start at (0,0) which is 3 and then use the first 1 block jump to (2, 0) which is 1 and then the second one block jump to (2, 2) which is 1 so 3+1+1 = 5. If the jumps array was only [1] then the output would be 6 since we would have to move from (2,0) to (2,1) to (2,2).
This is my first solution, which seems to work for smaller inputs but just runs forever on larger inputs:
def get(grid, x, y, endX, endY):
if (x > endX or y > endY):
return False
return grid[y][x]
def fly(x, y, grid, jumps, endX, endY):
if x > endX or y > endY:
return float('inf')
if (x == endX and y == endY):
return get(grid, endX, endY, endX, endY)
flyup = fly(x, y+1, grid, jumps, endX, endY)
flyright = fly(x+1, y, grid, jumps, endX, endY)
if (len(jumps) > 0):
jumpup = fly(x, y+jumps[0]+1, grid, jumps[1:], endX, endY)
jumpright = fly(x+jumps[0]+1, y, grid, jumps[1:], endX, endY)
temp = min(flyup, flyright, jumpup, jumpright)
return get(grid, x, y, endX, endY) + temp
else:
temp = min(flyup, flyright)
return get(grid, x, y, endX, endY) + temp
fly(0, 0, grid, jumps, X-1, Y-1)
This is another solution I wrote using DP (or whatever my understanding of DP is since I just learned it) but this one seems to only work on certain inputs and I can't identify a pattern.
def fly2(x, y, grid, jumps):
dp = [[0 for i in range(len(grid[0]))] for j in range(len(grid))]
for row in range(len(grid)):
for col in range(len(grid[0])):
if row == 0 and col == 0:
dp[row][col] += get2(grid, col, row)
else:
flyup = float('inf') if row==0 else dp[row-1][col]
flyright = float('inf') if col==0 else dp[row][col-1]
jumpup = float('inf') if row < jumps[0]+1 else dp[row-jumps[0]-1][col]
jumpright = float('inf') if col < jumps[0]+1 else dp[row][col-jumps[0]-1]
shortest = min(flyup, flyright, jumpup, jumpright)
if min == jumpup or min == jumpright:
jumps = jumps[1:]
dp[row][col] += get2(grid, col, row) + shortest
return dp[len(grid)-1][len(grid[0])-1]
I need some help speeding up the first one or figuring out what's wrong with the second one or maybe get some other ideas on how I can write this efficiently. Thanks
It seems to me that the dp should have another dimension for the current jump index. Generally,
dp[y][x][j] = Grid[y][x] + min(
dp[y + 1][x][j],
dp[y][x - 1][j],
dp[y + jump[j-1] + 1][x][j-1],
dp[y][x - jump[j-1] - 1][j-1]
)
where j is the current index in the jump array.
(I think the question description uses (x, y) coordinate notation in the example. I used [y][x] as [row][column], common for programming two dimension array access.)
The following implementation uses the grid and the remaining jumps as the state variables:
import numpy as np
def shortest(grid, jumps):
rows, cols = grid.shape
if (rows, cols) == (1, 1): # if grid is just a single number
return float('inf') if jumps else grid[0, 0]
candidates = [] # store results from deeper calls
if rows > 1:
candidates.append(shortest(grid[:-1, :], jumps)) # up by one
if jumps and rows > jumps[0] + 1: # jump to the above if possible
candidates.append(shortest(grid[:-(jumps[0] + 1), :], jumps[1:]))
if cols > 1:
candidates.append(shortest(grid[:, 1:], jumps)) # right by one
if jumps and cols > jumps[0] + 1: # jump to the right if possible
candidates.append(shortest(grid[:, (jumps[0] + 1):], jumps[1:]))
return grid[-1, 0] + min(candidates)
grid = np.array([[9, 9, 1], [9, 9, 1], [3, 9, 1]])
jumps = [1, 1]
print(shortest(grid, jumps)) # 5
Usage of np.array is just for simplicity of slicing.

Speeding up the following function

I am implementing a code to find all the paths from top left to bottom right in a n*m matrix.
Here is my code:
# Python3 program to Print all possible paths from
# top left to bottom right of a mXn matrix
'''
/* mat: Pointer to the starting of mXn matrix
i, j: Current position of the robot
(For the first call use 0, 0)
m, n: Dimentions of given the matrix
pi: Next index to be filed in path array
*path[0..pi-1]: The path traversed by robot till now
(Array to hold the path need to have
space for at least m+n elements) */
'''
def printAllPathsUtil(mat, i, j, m, n, path, pi):
# Reached the bottom of the matrix
# so we are left with only option to move right
if (i == m - 1):
for k in range(j, n):
path[pi + k - j] = mat[i][k]
for l in range(pi + n - j):
print(path[l], end = " ")
print()
return
# Reached the right corner of the matrix
# we are left with only the downward movement.
if (j == n - 1):
for k in range(i, m):
path[pi + k - i] = mat[k][j]
for l in range(pi + m - i):
print(path[l], end = " ")
print()
return
# Add the current cell
# to the path being generated
path[pi] = mat[i][j]
# Print all the paths
# that are possible after moving down
printAllPathsUtil(mat, i + 1, j, m, n, path, pi + 1)
# Print all the paths
# that are possible after moving right
printAllPathsUtil(mat, i, j + 1, m, n, path, pi + 1)
# Print all the paths
# that are possible after moving diagonal
# printAllPathsUtil(mat, i+1, j+1, m, n, path, pi + 1);
# The main function that prints all paths
# from top left to bottom right
# in a matrix 'mat' of size mXn
def printAllPaths(mat, m, n):
path = [0 for i in range(m + n)]
printAllPathsUtil(mat, 0, 0, m, n, path, 0)
def printAllPaths(mat, m, n):
path = [0 for i in range(m + n)]
printAllPathsUtil(mat, 0, 0, m, n, path, 0)
matrix = np.random.rand(150, 150)
printAllPaths(matrix, 150, 150)
I would like to find all the paths for a 150 by 150 matrix. But this takes a lot of time. Is there a good way to make it faster? If there are also any suggestions to speed up the algorithm that would be great`.
I think that when you talk of path a graph is a good solution, my idea is to build a graph with all paths and ask to him the solution, this print out all paths, each node is the couple of coordinates (x,y):
import networkx as nx
X = Y = 150
G = nx.DiGraph()
edges = []
for x in range(X):
for y in range(Y):
if x<X-1:
edges.append(((x,y),(x+1,y)))
if y<Y-1:
edges.append(((x,y),(x,y+1)))
G.add_edges_from(edges)
print(len(G.nodes()))
print(len(G.edges()))
for path in nx.all_simple_paths(G,(0,0),(X-1,Y-1)):
print(path)

Find how many random points lie inside ellipse centered at a point

The below code generates set of random x,y coordinates and uses the equation of an ellipse to compare how many of those points lie inside ellipse centered at (1,1) and a rectangle of area 2a*2b constructed around the ellipse whose semimajor and semiminor axis are a and b but b is variable and takes a value from the list b every single time. I want to have all the values of b for which the ratio of all the points lying inside the ellipse to the points lying inside the rectangle is greater than 0.5.
The problem I'm facing is If I check for a single value of b = 0.63. the condition ellipse_points/rectangle_points is approximately equal to 0.5 but when I loop throught the list b and use the If statement to get all the points for which ellipse_points/rectangle_points > 0.5, I do not see any value close to 0.63 instead I see values from 1.2 till 1.9, I do not understand why when I loop through a list of values for b the if statement seems to give faulty values. please refer to the next set of code where I set value of b = 0.63 and find ratio ellipse_points/rectangle_points
import numpy as np
x = np.random.uniform(0, 2, 10000) #generates random x coordinates
y = np.random.uniform(0, 2, 10000) #generates random y coordinates
ellipse_points, rectangle_points = 0, 0
a = 1
b = []
for i in range(1, 200):
b.append(i/100)
#print(b)
for p in b:
for i, j in zip(x, y):
if (((i - 1) ** 2) / a ** 2 + ((j - 1) ** 2) / p ** 2) < 1:
ellipse_points += 1
rectangle_points += 1
if ellipse_points/rectangle_point > 0.5:
print(p)
OUTPUT: 1.2, 1.21.............1.9
#
x = np.random.uniform(0, 2, 10000) #generates random x coordinates
y = np.random.uniform(0, 2, 10000) #generates random y coordinates
ellipse_points, rectangle_points = 0, 0
a = 1
b = 0.63
for i, j in zip(x, y):
if (((i - 1) ** 2) / a ** 2 + ((j - 1) ** 2) / b ** 2) < 1:
ellipse_points += 1
rectangle_points += 1
print(ellipse_points/rectangle_points)
OUTPUT 0.5001
If I understood your problem correctly, here's a vectorized solution.
It creates a binary mask for points inside the ellipse, counts where the mask is True and divides it by the total number of points.
# np.random.seed(42)
N = 10000
x = np.random.uniform(0, 2, N) #generates random x coordinates
y = np.random.uniform(0, 2, N) #generates random y coordinates
a = 1
b = 0.63
ratio = ((((x - 1)/a)**2 + ((y - 1)/b)**2) < 1).sum()/N
>>> print(ratio)
0.4954

numpy: get the indices within a polygon without creating a mask

I have a polygon vector formatted as follows (x1,y1,x2,y2, ...., xn,yn). As an example, consider this polygon array:
polyPoints = [3,5,7,8,9,5]
How do I get all the indices (or the coordinates) that are within the polygon generated from these points ?
The answers I looked so far requires you to create the 2D mask before you can get the indices within the polygon.
You can use scikit-image:
import numpy as np
from skimage.draw import polygon
points = [3,5,7,8,9,5]
r, c = polygon(points[1::2], points[::2])
print(r, c)
the output is:
[5 5 5 5 5 5 6 6 6 6 7 7] [3 4 5 6 7 8 5 6 7 8 6 7]
Using a mask is probably as efficient as you can get. This is some algorithm you which is rather inefficient, but probably can be optimized to be close to the mask approach. This essentially does a mask but on lines.
The approach is:
Find equations of lines of all edges
Find bounding box
For each y within bounding box (or x, whichever is smaller), compute the edges which intersect with the horizontal line (y=yi) at that y, and find at which x they intersect.
For each x within the bounding box, find which number of edges to the right of x which the line y=yi intersect. If the number of edges is odd, then the point (x,y) is inside the polygon.
It does work on a simple square geometry.
import numpy as np
# taken from: https://stackoverflow.com/questions/20677795/how-do-i-compute-the-intersection-point-of-two-lines-in-python
def line(p1, p2):
A = (p1[1] - p2[1])
B = (p2[0] - p1[0])
C = (p1[0]*p2[1] - p2[0]*p1[1])
return A, B, -C
def intersection(L1, L2):
D = L1[0] * L2[1] - L1[1] * L2[0]
Dx = L1[2] * L2[1] - L1[1] * L2[2]
Dy = L1[0] * L2[2] - L1[2] * L2[0]
if D != 0:
x = Dx / D
y = Dy / D
return x,y
else:
return False
# polyPoints = np.array([0, 0, 4, 0,4, 4, 0, 4])
polyPoints = np.array([[3,5,7,8,9,5]])
polyPoints = polyPoints.reshape(-1, 2)
npoints = polyPoints.shape[0]
polyEgdes = []
for i in range(npoints):
point1, point2 = polyPoints[i, :], polyPoints[(i+1) % npoints, :]
polyEgdes.append(line(point1, point2))
# bounding box
boundingBox = np.vstack((polyPoints.min(axis=0), polyPoints.max(axis=0)))
inside_points = []
for y in range(boundingBox[0, 1], boundingBox[1, 1]):
x_intersect = []
for l in polyEgdes:
# y_ins should be same as y
insect_point = intersection(l, [0, y, 0])
if insect_point:
x_intersect.append(insect_point[0])
x_intersect = np.array(x_intersect)
for x in range(boundingBox[0, 0]+1, boundingBox[1, 0]-1):
x_int_points = x_intersect[(x_intersect - x) >= 0]
if len(x_int_points) % 2 == 1:
inside_points.append((x, y))
print(inside_points)

Values from elements that creates concentric square on distance "k" in NumPy ndarray

What is the fastest way to get values from elements that creates concentric square on some distance K from the center. I wrote code where I access every element value with numpy.ndarray.item, but it has shown poor results. To help you visualize concentric squares here is picture of it
Note:
If center of sqaures is on that position that not all of square elements are in matrix then only visible elements shoud count. Example
Even better solution will be if I can get sum of elements after numpy.isclose method on array of elements from concentric square.
Here is current code:
def valid_pixel(image, pixel):
"""
Returns True if pixel is inside the image boundaries.
"""
width, height = image.shape
x, y = pixel
return 0 <= x < width and 0 <= y < height
def calculate_delta(indexed_img, C, k, pixel, h):
"""
Returns sum of elements all over the distance k that have the same value as parameter C
"""
x, y = pixel
c_sum = 0
v = 1 - h
for i in range(0, k + 1):
new_pixel = (x + h * i, y + v * i)
if valid_pixel(indexed_img, new_pixel) and C == indexed_img.item(new_pixel):
c_sum += 1
return c_sum
def color_correlation(indexed_img, colors_map, Ci, Cj, k):
correl_sum = 0
for x, y in colors_map.get(Ci, iter(())):
# this part of code returns sum of elements which create square with center in (x,y) and have the same value as parameter Cj
correl_sum += calculate_delta(indexed_img, Cj, 2 * k, (x - k, y + k), 1) + \
calculate_delta(indexed_img, Cj, 2 * k, (x - k, y - k), 1) + \
calculate_delta(indexed_img, Cj, 2 * k - 2, (x - k, y - k + 1), 0) + \
calculate_delta(indexed_img, Cj, 2 * k - 2, (x + k, y - k + 1), 0)
return correl_sum

Categories