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))
So I am trying to create a sudoku solver, however, I am stuck.
When the number reaches 10, it should go back one square and try again. However, when it reaches number 10, it suddenly goes to a negative row.
I have tried rewriting my code to see if anything would change due to a semantic error, however, it still doesn't work.
I would really appreciate if you could help me out.
Edited question / problem:
When it reaches number 10, it should change the variable direct to "back". Then when new_square is called, it should go one position back. But then for some reason, it infinitely goes back, until the row goes to the negative (position was 1 7, but it almost like recursion it goes back one column, until it goes into the minus row).
The code:
sudoku = [
# 0 1 2 3 4 5 6 7 8
[8, 0, 1, 0, 0, 0, 0, 4, 5], # 0
[0, 0, 0, 0, 0, 0, 7, 0, 6], # 1
[0, 5, 6, 0, 0, 0, 8, 0, 0], # 2
[0, 9, 0, 7, 0, 0, 1, 0, 0], # 3
[0, 0, 0, 0, 8, 0, 0, 0, 0], # 4
[0, 0, 0, 2, 0, 0, 5, 3, 8], # 5
[0, 0, 0, 0, 4, 0, 0, 8, 0], # 6
[4, 2, 7, 0, 0, 0, 0, 1, 0], # 7
[0, 0, 0, 0, 9, 0, 0, 0, 4] # 8
]
original = tuple(sudoku)
row = 0
column = 0
direct = "forw"
number = 0
steps = 0
game_change = True
def start():
global row, column, number, direct, steps
steps += 1
if not ((sudoku[row][column] == original[row][column]) and sudoku[row][column] != 0):
direct = "forw"
if number == 0:
number = 1
if number != 10:
direct = "forw"
rowc = row_check()
colc = column_check()
boxc = box_check()
if rowc and colc and boxc:
sudoku[row][column] = number
new_square()
number = sudoku[row][column]
else:
number += 1
else:
direct = "back"
new_square()
number = sudoku[row][column] + 1
return
else:
new_square()
number = sudoku[row][column] + 1
return
def row_check():
global row, number
if number not in sudoku[row]:
return True
else:
return False
def column_check():
global column, number
new_col = []
for i in range(9):
new_col.append(sudoku[i][column])
if number not in new_col:
return True
else:
return False
def box_check():
global row, column, number
srow = 0
scol = 0
if row in [0, 1, 2]:
srow = 0
elif row in [3, 4, 5]:
srow = 3
elif row in [6, 7, 8]:
srow = 6
if column in [0, 1, 2]:
scol = 0
elif column in [3, 4, 5]:
scol = 3
elif column in [6, 7, 8]:
scol = 6
new_box = []
for i in range(srow, (srow + 3)):
for j in range(scol, (scol + 3)):
new_box.append(sudoku[i][j])
if number not in new_box:
return True
else:
return False
def new_square():
global row, column, direct
if direct == "forw":
if column == 8:
column = 0
row += 1
else:
column += 1
else:
if column != 0:
column -= 1
else:
column = 8
row -= 1
while game_change:
start()
if steps % 1000 == 0:
print("It is done")
Answering the question
deepcopy
First up I noticed original = tuple(sudoku). This won't do. You are creating shallow copy but you want deepcopy. In short: if you edit a row in sudoku, the corresponding row in original will change as well.
To prevent that, use deep copy:
from copy import deepcopy
original = deepcopy(sudoku)
What now?
I tried to debug your code further. Mainly I added a few prints and a nice print_with_cursor function. I wasn't very successful, there is my code for future reference.
from copy import deepcopy
sudoku = [
# 0 1 2 3 4 5 6 7 8
[8, 0, 1, 0, 0, 0, 0, 4, 5], # 0
[0, 0, 0, 0, 0, 0, 7, 0, 6], # 1
[0, 5, 6, 0, 0, 0, 8, 0, 0], # 2
[0, 9, 0, 7, 0, 0, 1, 0, 0], # 3
[0, 0, 0, 0, 8, 0, 0, 0, 0], # 4
[0, 0, 0, 2, 0, 0, 5, 3, 8], # 5
[0, 0, 0, 0, 4, 0, 0, 8, 0], # 6
[4, 2, 7, 0, 0, 0, 0, 1, 0], # 7
[0, 0, 0, 0, 9, 0, 0, 0, 4] # 8
]
original = deepcopy(sudoku)
row = 0
column = 0
direct = "forw"
number = 0
steps = 0
game_change = True
def start(row, column, number, direct, steps):
steps += 1
print(f"row {row}, column {column}, number {number}", end="")
if not ((sudoku[row][column] == original[row][column]) and sudoku[row][column] != 0):
direct = "forw"
if number == 0:
number = 1
if number != 10:
direct = "forw"
rowc = number not in sudoku[row]
colc = column_check(column, number)
boxc = box_check(row, column, number)
if rowc and colc and boxc:
sudoku[row][column] = number
print(" set")
print_with_cursor(sudoku, row, column)
row, column = new_square(row, column, direct)
number = sudoku[row][column]
else:
if sudoku[row][column] != 0:
sudoku[row][column] = 0 # added
print(" reset")
print_with_cursor(sudoku, row, column)
number += 1
else:
direct = "back"
print(" unfillable")
print_with_cursor(sudoku, row, column)
row, column = new_square(row, column, direct)
number = sudoku[row][column] + 1
else:
row, column = new_square(row, column, direct)
number = sudoku[row][column] + 1
print()
return row, column, number, direct, steps
def print_with_cursor(sudoku, row, column):
print(f'{" "*column}v')
for irow, prow in enumerate(sudoku):
print(f'{"".join(str(p) for p in prow)}{"<" if irow == row else ""}')
def column_check(column, number):
new_col = []
for i in range(9):
new_col.append(sudoku[i][column])
return number not in new_col
def box_check(row, column, number):
if row in [0, 1, 2]:
srow = 0
elif row in [3, 4, 5]:
srow = 3
elif row in [6, 7, 8]:
srow = 6
if column in [0, 1, 2]:
scol = 0
elif column in [3, 4, 5]:
scol = 3
elif column in [6, 7, 8]:
scol = 6
new_box = []
for i in range(srow, (srow + 3)):
for j in range(scol, (scol + 3)):
new_box.append(sudoku[i][j])
return number not in new_box
def new_square(row, column, direct):
if direct == "forw":
if column == 8:
column = 0
row += 1
else:
column += 1
else:
if column != 0:
column -= 1
else:
column = 8
row -= 1
return row, column
while game_change:
row, column, number, direct, steps = start(row, column, number, direct, steps)
print("It is done")
The debug starts like this:
row 0, column 0, number 0
row 0, column 1, number 1
row 0, column 1, number 2
row 0, column 1, number 3 set
v
831000045<
000000706
056000800
090700100
000080000
000200538
000040080
427000010
000090004
row 0, column 2, number 1
row 0, column 3, number 1
row 0, column 3, number 2
row 0, column 3, number 3
row 0, column 3, number 4
row 0, column 3, number 5
row 0, column 3, number 6 set
v
831600045<
000000706
056000800
090700100
000080000
000200538
000040080
427000010
000090004
...
Code improvements
Let's upgrade your code! We can answer your question later, when the code is simpler to understand.
First up: fix SyntaxError
You are missing a code block after if steps % 1000 == 0:
while game_change:
start()
if steps % 1000 == 0:
print("It is done")
Secondly: get rid off global.
Then your code will be much more readable!
instead of
def new_square():
global row, column, direct
# do something
use
def new_square(row, column, direct):
# do something
return row, column
Then to call it, instead of
new_square()
use
row, column = new_square(row, column, direct)
Notice that now you get a much better idea of what the function does simply by looking at the call in the code.
Also: I am not a big fan of abbreviation direct of direction. I would rather see the whole word.
Bonus 1, inline function row_check:
def row_check():
global row, number
if number not in sudoku[row]:
return True
else:
return False
Can be simplified down to
def row_check():
global row, number
return number not in sudoku[row]
Note: use this simplification in all the X_check functions!
Now, this can be inlined, it is even used only in one place:
replace rowc = row_check()
by rowc = number not in sudoku[row]
Bonus 2, simplify X_check:
X_check's end with creating a list, checking if a number is in that list. Instead, we can iterate over what we are creating the list from and check whether the number is there. Maybe it is better understood with the solution:
create list of numbers in a sudoku box, then check if number is there
def box_check(row, column, number):
# setting srow & scol to be one of 0, 3, 6
new_box = []
for i in range(srow, (srow + 3)):
for j in range(scol, (scol + 3)):
new_box.append(sudoku[i][j])
return number not in new_box
check if number is in a sudoku box
def box_check(row, column, number):
# setting srow & scol to be one of 0, 3, 6
for i in range(srow, (srow + 3)):
for j in range(scol, (scol + 3)):
if number == sudoku[i][j]:
return False
return True
These two are the same, but the second doesn't use extra list, is simpler to understand. (And is more optimised, but that's not that important).
Do the same for column_check
Replace
def column_check(column, number):
new_col = []
for i in range(9):
new_col.append(sudoku[i][column])
return number not in new_col
with
def column_check(column, number):
for i in range(9):
if number == sudoku[i][column]:
return False
return True
We can go further there, replacing the range(9) by what is actually happening, we simplify it further to get:
def column_check(column, number):
for row in sudoku:
if number == row[column]:
return False
return True
I'm working on A star algorithm and as my code below shown the gird is written manually and I'm thinking to make a grid with 100* 100 size. So, it will be so awful to write them manually. I need to put my starting point at (0,0) location and my goal point at (99,99) location.
I'm trying to make the grid with this line below
grid1 = [[0 for i in range(100)]for j in range(100)]
But how could I assign obstacles to this grid randomly or not randomly without touching the location of starting point and goal point?
This is below my code:
from __future__ import print_function
import random
grid = [[0, 1, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0],#0 are free path whereas 1's are obstacles
[0, 1, 0, 0, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 0]]
'''
heuristic = [[9, 8, 7, 6, 5, 4],
[8, 7, 6, 5, 4, 3],
[7, 6, 5, 4, 3, 2],
[6, 5, 4, 3, 2, 1],
[5, 4, 3, 2, 1, 0]]'''
init = [0, 0]
goal = [len(grid)-1, len(grid[0])-1] #all coordinates are given in format [y,x]
cost = 1
drone_h = 60
#the cost map which pushes the path closer to the goal
heuristic = [[0 for row in range(len(grid[0]))] for col in range(len(grid))]
for i in range(len(grid)):
for j in range(len(grid[0])):
heuristic[i][j] = abs(i - goal[0]) + abs(j - goal[1])
#if grid[i][j] == 1:
#heuristic[i][j] = 99 #added extra penalty in the heuristic map
print(heuristic)
elevation = [[0 for row in range(len(grid[0]))] for col in range(len(grid))]
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == 1:
elevation[i][j] = random.randint(1,100)
else:
elevation[i][j] = 0
#the actions we can take
delta = [[-1, 0 ], # go up
[ 0, -1], # go left
[ 1, 0 ], # go down
[ 0, 1 ]] # go right
#function to search the path
def search(grid,init,goal,cost,heuristic):
closed = [[0 for col in range(len(grid[0]))] for row in range(len(grid))]# the referrence grid
closed[init[0]][init[1]] = 1
action = [[0 for col in range(len(grid[0]))] for row in range(len(grid))]#the action grid
x = init[0]
y = init[1]
g = 0
f = g + heuristic[init[0]][init[0]] + elevation[init[0]][init[0]]
cell = [[f, g, x, y]]
found = False # flag that is set when search is complete
resign = False # flag set if we can't find expand
while not found and not resign:
if len(cell) == 0:
resign = True
return "FAIL"
else:
cell.sort()#to choose the least costliest action so as to move closer to the goal
cell.reverse()
next = cell.pop()
x = next[2]
y = next[3]
g = next[1]
f = next[0]
if x == goal[0] and y == goal[1]:
found = True
else:
for i in range(len(delta)):#to try out different valid actions
x2 = x + delta[i][0]
y2 = y + delta[i][1]
if x2 >= 0 and x2 < len(grid) and y2 >=0 and y2 < len(grid[0]):
if closed[x2][y2] == 0 and grid[x2][y2] == 0 and elevation[x2][y2] < drone_h :
g2 = g + cost
f2 = g2 + heuristic[x2][y2] + elevation[x2][y2]
cell.append([f2, g2, x2, y2])
closed[x2][y2] = 1
action[x2][y2] = i
invpath = []
x = goal[0]
y = goal[1]
invpath.append([x, y])#we get the reverse path from here
while x != init[0] or y != init[1]:
x2 = x - delta[action[x][y]][0]
y2 = y - delta[action[x][y]][1]
x = x2
y = y2
invpath.append([x, y])
path = []
for i in range(len(invpath)):
path.append(invpath[len(invpath) - 1 - i])
print("ACTION MAP")
for i in range(len(action)):
print(action[i])
return path
a = search(grid,init,goal,cost,heuristic)
for i in range(len(a)):
print(a[i])
You could assign the grid randomly and afterwards make sure both starting and end point don't contain obstacles. For neighboring fields you could just do the same as for those two.
import random
grid1 = [[random.randint(0,1) for i in range(100)]for j in range(100)]
# clear starting and end point of potential obstacles
grid1[0][0] = 0
grid1[99][99] = 0
We have a list
list = [1, 1, 1, 0, 1, 0, 0, 1]
I am trying find a function that would count the number of 0's before each item and then multiply this number by 3.
def formula(n):
for x in list:
if x == 1:
form = n * 3
return form
#If x is 1, count the number of zeros right before x and multiply this by 3,
For example for the list above, the first element is a 1 and there are no numbers right before it, the program should compute 0 * 3 = 0, for the second item, which is also a 1, the number right before it is also not a zero, the program should also compute 0 * 3 = 0. The 4th element is 0 so the program should ignore, For the 5th element which is a 1, the number right before it is a 0, the programme to compute 1 * 3 = 3, for the 6th element the number right before it is a 1, the system should compute 0 * 3 = 0. The 7th element is a 0, since x is not equal to 1 the program should not do anything. For the last element which is a 1, the last two numbers before it are zeros, the program should compute 2 * 3 = 6
I believe you are looking for a generator, with a simple counter:
def get_values (list):
n = 0
for x in list:
if x == 1:
yield n * 3
n = 0 # reset the counter
elif x == 0:
n += 1 # increment the counter
print(list(get_values([1, 1, 1, 0, 1, 0, 0, 1])))
# [0, 0, 0, 3, 6]
Try this,
def formula(l):
count_zero = 0
result = []
for i in l:
if i == 1:
result.append(3*count_zero)
count_zero = 0
elif i == 0:
count_zero += 1
return result
# Test case
l = [1, 1, 1, 0, 1, 0, 0, 1]
result = formula(l)
print(result)
# [0, 0, 0, 3, 6]
Here is my solution for the problem.
test_list = [1, 1, 1, 0, 1, 0, 0, 1]
def formula(list):
track = []
start = 0
for i,n in enumerate(list):
count_list_chunk = list[start:i]
if count_list_chunk.count(0) > 0 and n != 0:
start = i
if n != 0:
track.append( count_list_chunk.count(0)*3 )
return track
print formula(test_list)
#[ 0, 0, 0, 3, 6]