find max square with black border - python
For a square matrix, each cell is either black (1) or white (0), trying to find the max sub-square whose border is black. Here is my code with Python 2.7, wondering if logically correct? And any performance improvements? Thanks.
My major ideas is, keep track of how many black nodes are on top (continuously) and on left (continuously), which is left and top matrix represents. Then, based on the left and top tracking, for any node, I will try to find minimal value of top and left continuous black node, then I will base the minimal value to see if a square could be formed.
A=[[1,1,0,0,0],
[1,1,1,1,1],
[0,0,1,0,1],
[0,0,1,1,1],
[0,0,0,0,0]]
top=[[0] * len(A[0]) for i in range(len(A))]
left=[[0] * len(A[0]) for i in range(len(A))]
result = 0
for i in range(len(A)):
for j in range(len(A[0])):
if A[i][j] == 1:
top[i][j] = top[i-1][j] + 1 if i > 0 else 1
left[i][j] = left[i][j-1] + 1 if j > 0 else 1
print top
print left
for i in range(len(A)):
for j in range(len(A[0])):
if A[i][j] == 1:
top[i][j] = top[i-1][j] + 1 if i > 0 else 1
left[i][j] = left[i][j-1] + 1 if j > 0 else 1
x = min(top[i][j], left[i][j])
if x > 1:
y = min(left[i-x+1][j], top[i][j-x+1])
result = max(result, y)
print result
Edit 1, fix an issue, which is pointed by j_random_hacker.
A = [[1, 1, 0, 0, 0],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 1],
[0, 0, 1, 1, 1],
[0, 0, 0, 0, 0]]
A = [[0,1,1],
[1,0,1],
[1,1,1]]
top = [[0] * len(A[0]) for i in range(len(A))]
left = [[0] * len(A[0]) for i in range(len(A))]
result = 0
for i in range(len(A)):
for j in range(len(A[0])):
if A[i][j] == 1:
top[i][j] = top[i - 1][j] + 1 if i > 0 else 1
left[i][j] = left[i][j - 1] + 1 if j > 0 else 1
print top
print left
for i in range(len(A)):
for j in range(len(A[0])):
if A[i][j] == 1:
top[i][j] = top[i - 1][j] + 1 if i > 0 else 1
left[i][j] = left[i][j - 1] + 1 if j > 0 else 1
x = min(top[i][j], left[i][j])
if x > 1:
y = min(left[i - x + 1][j], top[i][j - x + 1])
if x == y:
result = max(result, y)
print result
Edit 2, address the issue by j_random_hacker.
A = [[0,1,0,0],
[1,1,1,1],
[0,1,0,1],
[0,1,1,1]]
A = [[0,1,1],
[1,0,1],
[1,1,1]]
A = [[1, 1, 0, 0, 0],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 1],
[0, 0, 1, 1, 1],
[0, 0, 0, 0, 0]]
top = [[0] * len(A[0]) for i in range(len(A))]
left = [[0] * len(A[0]) for i in range(len(A))]
result = 0
for i in range(len(A)):
for j in range(len(A[0])):
if A[i][j] == 1:
top[i][j] = top[i - 1][j] + 1 if i > 0 else 1
left[i][j] = left[i][j - 1] + 1 if j > 0 else 1
print top
print left
for i in range(len(A)):
for j in range(len(A[0])):
if A[i][j] == 1:
top[i][j] = top[i - 1][j] + 1 if i > 0 else 1
left[i][j] = left[i][j - 1] + 1 if j > 0 else 1
x = min(top[i][j], left[i][j])
while x > 1:
y = min(left[i - x + 1][j], top[i][j - x + 1])
if x == y:
result = max(result, y)
break
x -= 1
print result
Edit 3, new fix
A = [[0,1,0,0],
[1,1,1,1],
[0,1,0,1],
[0,1,1,1]]
A = [[1, 1, 0, 0, 0],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 1],
[0, 0, 1, 1, 1],
[0, 0, 0, 0, 0]]
A = [[0,1,1],
[1,0,1],
[1,1,1]]
top = [[0] * len(A[0]) for i in range(len(A))]
left = [[0] * len(A[0]) for i in range(len(A))]
result = 0
for i in range(len(A)):
for j in range(len(A[0])):
if A[i][j] == 1:
top[i][j] = top[i - 1][j] + 1 if i > 0 else 1
left[i][j] = left[i][j - 1] + 1 if j > 0 else 1
print top
print left
for i in range(len(A)):
for j in range(len(A[0])):
if A[i][j] == 1:
top[i][j] = top[i - 1][j] + 1 if i > 0 else 1
left[i][j] = left[i][j - 1] + 1 if j > 0 else 1
x = min(top[i][j], left[i][j])
while x > 1:
y = min(left[i - x + 1][j], top[i][j - x + 1])
if x <= y:
result = max(result, x)
break
x -= 1
print result
This isn't logically correct, as the following simple counterexample shows:
011
101
111
This array contains no bordered square, but your code reports that it contains a square of edge length 3. You need either a third nested loop through all candidate square sizes down from x to 1 (making the solution O(n^3)-time) or, possibly, some more sophisticated data structure that enables an equivalent check to be performed faster.
[EDIT to address updated algorithm]
The new algorithm thinks there is no bordered square in either
0100
1111 1111
0101 or 0101
0111 0111
despite there being a 3x3 one in each.
Stop guessing at fixes and think about how to break down the set of all possible locations of bordered squares into sets that you can efficiently (or even not-so-efficiently) check. As I tried to say at the top: For each possible bottom-right corner of a bordered square, your code is currently only checking one rectangle. But many more rectangles could have (i, j) as a bottom-right corner -- where are the tests for them?
Related
How to find the max hotspot of a matrix in python
I was given a matrix question on a technical assessment that revolved around the idea of finding the "Max Hotspot". Areas of connected 1's represent a hotspot, and given a matrix we want to return the max hotspot. Similar to "Number of Islands" or "Word Search. areamap = [[1, 0, 0, 0], [1, 1, 1, 0], [0, 0, 0, 1], [1, 1, 1, 1]] areamap = [[0, 0, 0, 0], [0, 1, 1, 0], [0, 0, 0, 0], [1, 0, 0, 0]] I tried using the 4 way DFS approach but had trouble creating/incrementing a variable that keeps track of the size of each hotspot. Any suggestions/help? Below is my attempt. The idea of my algo is that every time we find a 1, we "evaporate" it to avoid traveling duplicates. For each 1 we evaporate, we incrementing the count of 1's. The count variable and the tmp list variable always print/return as empty. class Solution: def hotSpots(self, area: List[int]): def evap(r, c, cnt): if r < 0 or c < 0 or r >= len(area) or c >= len(area[0]) or area[r][c] == "0": return cnt += 1 area[r][c] = "0" evap(r, c + 1) evap(r, c - 1) evap(r + 1, c) evap(r - 1, c) return tmp = [] for i in range(len(area)): for j in range(len(area[0])): count = 0 if area[i][j] == "1": evap(i, j, count) tmp.append(count) return sum(max(tmp))
There are two issues with the code: evap does not return any result and so count never gets assigned anything other than 0 You have an array of integers, but you check its elements against strings ("0" and "1") Solving those issues yields the following code, which outputs the result you want def hotSpots(area): def evap(r, c): if r < 0 or c < 0 or r >= len(area) or c >= len(area[0]) or area[r][c] == 0: return 0 area[r][c] = 0 return 1 + evap(r, c + 1) + evap(r, c - 1) + evap(r + 1, c) + evap(r - 1, c) tmp = [] for i in range(len(area)): for j in range(len(area[0])): if area[i][j] == 1: count = evap(i, j) tmp.append(count) return sum(max(tmp))
Finding the coordinates of max sum path in matrix
I have this method which returns the max sum of the path from top left to bottom right (can move only to the right or bottom). def max_path(grid): N = len(grid) M = len(grid[0]) sum = [[0 for i in range(M + 1)] for i in range(N + 1)] for i in range(1, N + 1): for j in range(1, M + 1): sum[i][j] = (max(sum[i - 1][j], sum[i][j - 1]) + grid[i - 1][j - 1]) return sum[N][M] matrix = [[1, 2, 3], [3, 4, 5]] print(max_path(matrix)) output : 1 + 3 + 4 + 5 = 13 But what I want to get is also the coordinates of the points of the path: [(0,0) (1,0) (1,1) (1,2)]
You can try the below code to get your job done. from itertools import permutations, product def get_max_sum(table): height, width = len(table), len(table[0]) sum_, *pos = max((sum(table[x][y] for x, y in zip(*pairs)), *zip(*pairs)) for pairs in product( permutations(range(height)), ([*range(i, width), *range(i)] for i in range(width)))) return (sum_, *sorted(pos)) sum_, *pos = get_max_sum( [[1, 2, 3], [2, 3, 5], [4, 9, 16]] ) Output: 20 #maximum sum (0, 1) (1, 0) (2, 2) #co -ordinates
The variable sum after the nested-for loops (as written in your code) is [0, 0, 0, 0], [0, 1, 3, 6], [0, 4, 8, 13] You can work out the coordinates of the max sum by having a initial "pointer" at the bottom right corner (i=1, j =2, ignoring the zeros), and comparing the values that is on the top (i=0, i=2) and on the left (i=1, j=1). Since 8 is larger than 6, we move the "pointer" to the left, and now i=1, j=1. 4 is larger than 3, so we move the pointer to 4 (i=1, j=0) 1 is larger than 0, so we move the pointer to 1 (i=0, j=0) A basic (untested) implementation of the algorithm is as follows: def max_path(grid): N = len(grid) M = len(grid[0]) sum = [[0 for i in range(M + 1)] for i in range(N + 1)] for i in range(1, N + 1): for j in range(1, M + 1): sum[i][j] = (max(sum[i - 1][j], sum[i][j - 1]) + grid[i - 1][j - 1]) j = M i = N path = [] while i > 0 and j > 0: path.append((i-1,j-1)) if sum[i-1][j] <= sum[i][j-1]: j -= 1 else: i -= 1 path.reverse() return sum[N][M],path matrix = [[1, 2, 3], [3, 4, 5]] print(max_path(matrix))
Unable to access float object in a 2D array in Python
I need to return the vector solution x of Ux = b for an upper triangular matrix U and vector b using back substitution, but I'm unable to actually access an element of the matrix U. def BackSub(U, b): n = len(U) x = [0 for i in range(n)] for i in range(n - 1, -1, -1): s = 0 for j in range(n - 1, i, -1): s += (U[i][j])*b[j] b[i] = (b[i] - s)/(U[i][i]) return b b = [5, 6, 3, 2] U = [[ 1, 2, 1, 0], [ 0, 3, -5, 9], [ 0, 0, 0.5, 1], [ 0, 0, 0, 7]] N = GaussElim(U, b) x = BackSub(N, b) It returns TypeError: 'float' object is not subscriptable for U[i][i] The GaussElim function is this import numpy as np def GaussElim(A, b): n = len(b) #n is matrix size #Elimination phase for k in range(0 , n - 1): #k is matrix row for i in range(k + 1, n): #i is matrix col if A[i][k] != 0: factor = A[i][k]/ A[k][k] A[i][k + 1 : n] = A[i][k + 1 : n] - np.multiply(factor, A[k][k + 1 : n]) b[i] = b[i] - np.multiply(factor, b[k]) #Back substitution for k in range(n - 1, -1, -1): b[k] = (b[k] - dot(A[k][k + 1 : n], b[k + 1 : n]))/A[k][k] return b
Erosion operation (python)
I am trying to implement the erosion function for a binary image without using standard libraries (cv2,..). That's what I got at the moment. Is it possible to simplify my code so that there are only vector or matrix operations? Any other suggestions are also welcome. mask = np.array([ [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1] ]) def erosion(image, mask): mask_size = mask.shape[0] left_half = int(mask_size / 2) right_half = int(mask_size / 2) if mask_size % 2 == 0: right_half += 1 result = image.copy() # Идем по изображению for i in range(left_half, result.shape[0] - right_half): for j in range(left_half, result.shape[1] - right_half): left_border = int(mask_size / 2) mask_i = 0 mask_j = 0 flag = False right_border = left_border if mask_size % 2 != 0: right_border += 1 # Идем по маске, перебираем каждый элемент маски for k in range(i - left_border, i + right_border): mask_j = 0 for l in range(j - left_border, j + right_border): if mask[mask_i, mask_j] == 1 and image[k, l] == 0: result[i, j] = 0 flag = True break mask_j += 1 mask_i += 1 if(flag): break if not (flag): result[i, j] = 255 return result
How do I increment the number in a cell of a 2D-list based on the adjacent cell's info in Python
The question may seem complicated, so here is my input and output. Input: [[0, X, 0, 0], [0, 0, 0, 0], [0, 0, 0, X], [0, X, 0, 0]] When a cell is adjacent to 'X', it is worth 1 point. So this will become, [[1, X, 1, 0], [1, 1, 2, 1], [1, 1, 2, X], [1, X, 2, 1]] Considering adjacent also covers diagonals. What I've tried is: def adx(grid, i, j): for move in movable: if not (i + move[0] < 0 or i + move[0] >= len(grid) or j + move[1] < 0 or j + move[1] >= len(grid[0])): if grid[i + move[0]][j + move[1]] == 'X': return True else: return False for i in range(len(grid)): for j in range(len(grid[0])): if adx(grid, i, j) > 0: if grid[i][j] != 'X': grid[i][j] = 1 else: grid[i][j] += 1 The problem with this is that it does not accumulates the score, and just puts 1 regardless. How can I fix this?
You need to calculate all 'X', while you just returned while you find 1 'X'. def adx(grid, i, j): step = 0 for move in movable: if not (i + move[0] < 0 or i + move[0] >= len(grid) or j + move[1] < 0 or j + move[1] >= len(grid[0])): if grid[i + move[0]][j + move[1]] == 'X': step += 1 return step for i in range(len(grid)): for j in range(len(grid[0])): step = adx(grid, i, j) if adx(grid, i, j) > 0: if grid[i][j] != 'X': grid[i][j] = step There is faster way, take a look def adx(grid, i, j): for move in movable: if not (i + move[0] < 0 or i + move[0] >= len(grid) or j + move[1] < 0 or j + move[1] >= len(grid[0])): if grid[i + move[0]][j + move[1]] != 'X': grid[i + move[0]][j + move[1]] += 1 for i in range(len(grid)): for j in range(len(grid[0])): if grid[i][j] == 'X': adx(grid, i, j)