There is a task on CodeFights involving building a two-dimensional array:
A spiral matrix is a square matrix of size n × n. It contains all the
integers in range from 1 to n * n so that number 1 is written in the
bottom right corner, and all other numbers are written in increasing
order spirally in the counterclockwise direction.
Given the size of the matrix n, your task is to create a spiral
matrix.
One is supposed to fill only one gap denoted by ... in the following code:
def createSpiralMatrix(n):
dirs = [(-1, 0), (0, -1), (1, 0), (0, 1)]
curDir = 0
curPos = (n - 1, n - 1)
res = ...
for i in range(1, n * n + 1):
res[curPos[0]][curPos[1]] = i
nextPos = curPos[0] + dirs[curDir][0], curPos[1] + dirs[curDir][1]
if not (0 <= nextPos[0] < n and
0 <= nextPos[1] < n and
res[nextPos[0]][nextPos[1]] == 0):
curDir = (curDir + 1) % 4
nextPos = curPos[0] + dirs[curDir][0], curPos[1] + dirs[curDir][1]
curPos = nextPos
return res
When I fill in the following code, all tests are passed:
res = [[0 for item in range(n)] for sublist in range(n)]
However, if I slightly change it to:
res = [[None for item in range(n)] for sublist in range(n)]
I receive the following error message:
Execution error on test 1: Something went wrong when executing the solution - program stopped unexpectedly with an error.
Traceback (most recent call last):
file.py3 on line ?, in getUserOutputs
userOutput = _runiuljw(testInputs[i])
file.py3 on line ?, in _runiuljw
return createSpiralMatrix(*_fArgs_jlosndfelxsr)
file.py3 on line 8, in createSpiralMatrix
res[curPos[0]][curPos[1]] = i
IndexError: list index out of range
Test 1 Input: n: 3 Output: Empty Expected Output: [[5,4,3], [6,9,2],
[7,8,1]] Console Output: Empty
The same result (with the error message) is with the following code:
res = [list(range(n)) for sublist in range(n)]
All three options build arrays of the same size:
n = 3
res = [[0 for item in range(n)] for sublist in range(n)]
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
res = [[None for item in range(n)] for sublist in range(n)]
[[None, None, None], [None, None, None], [None, None, None]]
res = [list(range(n)) for sublist in range(n)]
[[0, 1, 2], [0, 1, 2], [0, 1, 2]]
Am I missing something obvious?
If you want it to work with None, then you simply have to change the third condition of your if not ... statement to agree with how you initialized res. Otherwise, nextPos will incorrectly become out of the range of your 2D array.
def createSpiralMatrix(n):
dirs = [(-1, 0), (0, -1), (1, 0), (0, 1)]
curDir = 0
curPos = (n - 1, n - 1)
res = [[None for item in range(n)] for sublist in range(n)]
for i in range(1, n * n + 1):
res[curPos[0]][curPos[1]] = i
nextPos = curPos[0] + dirs[curDir][0], curPos[1] + dirs[curDir][1]
if not (0 <= nextPos[0] < n and
0 <= nextPos[1] < n and
res[nextPos[0]][nextPos[1]] is None): # Changed this line
curDir = (curDir + 1) % 4
nextPos = curPos[0] + dirs[curDir][0], curPos[1] + dirs[curDir][1]
curPos = nextPos
return res
Related
I have written a program to calculate GCD of all numbers of an array except elements in a given range.
Given an integer array and a q number of queries.
Each query contains the number left and right. I need to find out the greatest common divisor of all the integers except within the given range of the query.
arr = [2, 6, 9]
query = [(0, 0), (1, 1), (1, 2)]
My Output: [3, 1, 1]
Expected Output: [3, 1, 2]
Code:
def computeGCD(a, b):
if b == 0:
return a
else:
return computeGCD(b, a%b)
def getGCD(arr, query):
n = len(arr)
left_gcd = right_gcd = [0 for i in range(n)]
left_gcd[0] = arr[0]
for i in range(1, n):
left_gcd[i] = computeGCD(left_gcd[i-1], arr[i])
print(left_gcd)
right_gcd[n-1] = arr[n-1]
for i in range(n-2, -1, -1):
right_gcd[i] = computeGCD(right_gcd[i+1], arr[i])
print(right_gcd)
res = []
for q in query:
if q[0] == 0:
gcd_outside_q = right_gcd[q[1]+1]
elif q[1] == n-1:
gcd_outside_q = left_gcd[q[0]-1]
else:
gcd_outside_q = computeGCD(left_gcd[q[0]-1], right_gcd[q[1]+1])
res.append(gcd_outside_q)
return res
getGCD(arr, query)
Please let me know what I am missing.
The problem is with line:
left_gcd = right_gcd = [0 for i in range(n)]
This way, left_gcd and right_gcd refer to the same list. If you change one of them, the other changes too. You can check this by printing left_gcd after the right_gcd block:
...
left_gcd[0] = arr[0]
for i in range(1, n):
left_gcd[i] = computeGCD(left_gcd[i-1], arr[i])
print(left_gcd)
right_gcd[n-1] = arr[n-1]
for i in range(n-2, -1, -1):
right_gcd[i] = computeGCD(right_gcd[i+1], arr[i])
print(right_gcd)
print(left_gcd)
...
The output is:
[2, 2, 1]
[1, 3, 9]
[1, 3, 9]
An easy way to fix this is to change it like so:
def getGCD(arr, query):
n = len(arr)
left_gcd = [0 for i in range(n)]
right_gcd = [0 for i in range(n)]
...
I need to find the longest path of 0's in a 2d matrix recursively and can't figure out how to do it.( from a given (i , j) it can only move up, down, right or left)
For example this matrix:
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
print(question3_b(mat))
This should return 6 as there are communities of sizes 1,3,6.
My attempt: I created a few wrapper functions to find the maximum in a list, and a function to find the route at a given (i,j) element and add it to a list, and doing this on every point(i,j) in the matrix.
def question3_b(mat) -> int:
rows: int = len(mat)
cols: int = len(mat[0])
community_lengths = list()
for i in range(rows):
for j in range(cols):
visited = zeros(rows, cols) # create a 2d list of 0's with size rows,cols
a = findcommunity(mat, (i, j), visited)
print("a=", a)
community_lengths.append(a)
print(community_lengths)
return findmax(community_lengths)
def findcommunity(mat: list, indices: tuple, visited: list): # find length of community
#global rec_counter
#rec_counter += 1
i, j = indices
rows = len(mat)
cols = len(mat[0])
if mat[i][j] == 0:
visited[i][j] = 1
if i < rows - 1:
if visited[i + 1][j] == 0:
return 1 + findcommunity(mat, (i + 1, j), visited)
if j < cols - 1:
if visited[i][j + 1] == 0:
return 1 + findcommunity(mat, (i, j + 1), visited)
if i > 0:
if visited[i - 1][j] == 0:
return 1 + findcommunity(mat, (i - 1, j), visited)
if j > 0:
if visited[i][j - 1] == 0:
return 1 + findcommunity(mat, (i, j - 1), visited)
else: # mat[i][j]!=0
return 0
def zeros(rows:int,cols:int)->list: #create a 2d matrix of zeros with size (rows,cols)
if rows==1 and cols==1:return [[0]]
if rows==1 and cols>1:return [[0]*cols]
if rows>1 and cols>1:return zeros(rows-1,cols)+zeros(1,cols)
def findmax(arr:list)->int: # find max in an array using recursion
if len(arr)==2:
if arr[0]>arr[1]:return arr[0]
else:return arr[1]
else:
if arr[0]>arr[1]:
arr[1]=arr[0]
return findmax(arr[1:])
else:
return findmax(arr[1:])
Where did I go wrong? for a matrix of 4X4 zeros, I expect it to run 16*16 times[16 times for each i,j, and there are 16 elements in the matrix]. but it only runs once.
zeros is a recursive function I made that functions like np.zeros, it creates a 2d matrix full of 0's with specified size.
It got really messy but I tried to just change your code instead of writing a new solution. You should have a look at collections deque. Saw this several times where people keep track of visited a lot easier.
I changed visited to outside of the loop and defined it with np.zeros (didn't have your function ;) ). I'm not sure if your recursive function calls at return were wrong but your if-statements were, or at least the logic behind it (or I didn't understand, also possible :) )
I changed that block completly. The first time you come across a 0 in mat the recursive part will dig into the mat as long as it finds another 0 left,right,bottom or top of it (that's the functionality behind dc and dr). That's where the community_counter is increasing. If the function is returning the last time and you jump out to the outerloop in question_3b the counter gets resetted and searchs for the next 0 (next start of another community).
import numpy as np
def question3_b(mat) -> int:
rows: int = len(mat)
cols: int = len(mat[0])
community_lengths = list()
visited = np.zeros((rows, cols)) # create a 2d list of 0's with size rows,cols
community_count = 0
for row in range(rows):
for col in range(cols):
if visited[row][col]==0:
community_count,visited = findcommunity(mat, (row, col), visited, community_count)
if community_count!=0:
community_lengths.append(community_count)
community_count=0
return community_lengths
def findcommunity(mat: list, indices: tuple, visited: list,community_count: int): # find length of community
i, j = indices
rows = len(mat)
cols = len(mat[0])
visited[i][j] = 1
if mat[i][j] == 0:
community_count += 1
dr = [-1, 0, 1, 0]
dc = [0,-1, 0, 1]
for k in range(4):
rr = i + dr[k]
cc = j + dc[k]
if 0<=rr<rows and 0<=cc<cols:
if visited[rr][cc]==0 and mat[rr][cc]==0:
community_count, visited = findcommunity(mat, (rr,cc), visited, community_count)
return community_count,visited
else:
return community_count,visited
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
all_communities = question3_b(mat)
print(all_communities)
# [6, 3, 1]
print(max(all_communities))
# 6
EDIT
Here is the findcommunity function in your way. Tested it and it works aswell.
def findcommunity(mat: list, indices: tuple, visited: list,community_count: int): # find length of community
i, j = indices
rows = len(mat)
cols = len(mat[0])
visited[i][j] = 1
if mat[i][j] == 0:
community_count += 1
if i < rows - 1:
if visited[i + 1][j] == 0:
community_count, visited = findcommunity(mat, (i + 1, j), visited, community_count)
if j < cols - 1:
if visited[i][j + 1] == 0:
community_count, visited = findcommunity(mat, (i, j + 1), visited, community_count)
if i > 0:
if visited[i - 1][j] == 0:
community_count, visited = findcommunity(mat, (i - 1, j), visited, community_count)
if j > 0:
if visited[i][j - 1] == 0:
community_count, visited = findcommunity(mat, (i, j - 1), visited, community_count)
return community_count,visited
else:
return community_count,visited
Here a completely different approach, in case someone is interested.
import numpy as np
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
mat = np.array(mat)
# some padding of -1 to prevent index error
mask = np.full(np.array(mat.shape) + 2, -1)
mask[1:-1, 1:-1 ] = mat
# visiteds are set to -2
def trace(f, pt):
mask[tuple(pt)], pts = -2, [pt - 1]
pts += [trace(f, pt + d) for d in
([0, 1], [1, 0], [0, -1], [-1, 0]) if
mask[tuple(pt + d)] == f]
return pts
clusters = lambda f: {tuple(pt-1): trace(f, pt) for pt in np.argwhere(mask==f) if mask[tuple(pt)]==f}
# now call with value your looking for
print(clusters(0))
there is an issue that i am trying to solve which requires me to generate the indices for an n - dimensional list. Eg: [5, 4, 3] is a 3 dimensional list so valid indices are [0, 0, 0], [0, 0, 1], [0, 1, 0] ... [2, 2, 1] ..... [4, 3, 2]. The best I could come up with a recursive algorithm but this isn't constant space
def f1(dims):
def recur(res, lst, depth, dims):
if depth == len(dims):
res.append(lst[::])
return
curr = dims[depth]
for i in range(curr):
lst[depth] = i
recur(res, lst, depth + 1, dims)
res = []
lst = [0] * len(dims)
recur(res, lst, 0, dims)
return res
the dimensions can be any number , ie: 4D, 5D, 15D etc. Each time it would be given in the form of a list . Eg: 5D would be [3,2,1,5,2] and I would need to generate all the valid indices for these while using constant space ( just while loops and indices processing ) . How would I go about generating these efficiently without the help of any in built python functions ( just while, for loops etc )
This is a working solution in constant space (a single loop variable i, and a vector idx of which modified copies are being yielded, and a single temporary length variable n to avoid calling len() on every iteration):
def all_indices(dimensions):
n = len(dimensions)
idx = [0] * n
while True:
for i in range(n):
yield tuple(idx)
if idx[i] + 1 < dimensions[i]:
idx[i] += 1
break
else:
idx[i] = 0
if not any(idx):
break
print(list(all_indices([3, 2, 1])))
Result:
[(0, 0, 0), (1, 0, 0), (2, 0, 0), (0, 0, 0), (0, 1, 0), (1, 1, 0), (2, 1, 0), (0, 1, 0), (0, 0, 0)]
As pointed out in the comments, there's duplicates there, a bit sloppy, this is cleaner:
def all_indices(dimensions):
n = len(dimensions)
idx = [0] * n
yield tuple(idx) # yield the initial 'all zeroes' state
while True:
for i in range(n):
if idx[i] + 1 < dimensions[i]:
idx[i] += 1
yield tuple(idx) # yield new states
break
else:
idx[i] = 0 # no yield, repeated state
if not any(idx):
break
print(list(all_indices([3, 2, 1])))
Alternatively, you could yield before the break instead of at the start of the loop, but I feel having the 'all zeroes' at the start looks cleaner.
The break is there to force a depth first on running through the indices, which ensures the loop never reaches 'all zeroes' again before having passed all possibilities. Try removing the break and then passing in something like [2, 1, 2] and you'll find it is missing a result.
I think a break is actually the 'clean' way to do it, since it allows using a simple for instead of using a while with a more complicated condition and a separate increment statement. You could do that though:
def all_indices3(dimensions):
n = len(dimensions)
idx = [1] + [0] * (n - 1)
yield tuple([0] * n)
while any(idx):
yield tuple(idx)
i = 0
while i < n and idx[i] + 1 == dimensions[i]:
idx[i] = 0
i += 1 % n
if i < n:
idx[i] += 1
This has the same result, but only uses while, if and yields the results in the same order.
So far I have the following code which I believe selects up to a MAX of 4 (or n) elements in the knapsack (hence the 3rd dimension). However, I want to ensure that the code ALWAYS selects 4 (or n) elements. Can someone please advise as I can't find anything about this anywhere...
def knapsack2(n, weight, count, values, weights):
dp = [[[0] * (weight + 1) for _ in range(n + 1)] for _ in range(count + 1)]
for z in range(1, count + 1):
for y in range(1, n + 1):
for x in range(weight + 1):
if weights[y - 1] <= x:
dp[z][y][x] = max(dp[z][y - 1][x],
dp[z - 1][y - 1][x - weights[y - 1]] + values[y - 1])
else:
dp[z][y][x] = dp[z][y - 1][x]
return dp[-1][-1][-1]
w = 10
k = 4
values = [1, 2, 3, 2, 2]
weights = [4, 5, 1, 1, 1]
n = len(values)
# find elements in
elements=[]
dp=m
while (n> 0):
if dp[k][n][w] - dp[k][n-1][w - weights[n-1]] == values[n-1]:
#the element 'n' is in the knapsack
elements.append(n)
n = n-1 #//only in 0-1 knapsack
w -= weights[n]
else:
n = n-1
I'm trying to write a function that takes a list of integers and finds all arithmetic sequences in it.
A = [-1, 1, 3, 3, 3, 2, 1, 0]
There are five arithmetic sequences in this list: (0, 2), (2,4), (4, 6), (4,7), (5,7) - these are indexes of first and last element of a sequence. A sequence is derived by the difference between elements.
As you see from the example above - the sequence must be longer than 2 elements (otherwise it would find a sequence between every two elements).
The function that I need to write must return the number of sequences it finds on the list - in this case it should return 5.
I'm kind of stuck - tried a few different approaches but failed miserably. The most recent thing I've done is:
def solution(A):
slices = []
x = 0
listlen = len(A)
while x < listlen-1:
print ("Current x:", x)
difference = A[x+1] - A[x]
#print ("1st diff: ", A[x+1], ",", A[x], " = ", difference)
for y in range(x+1, len(A)-1):
difference_2 = A[y+1] - A[y]
#print ("Next Items: ", A[y+1], A[y])
#print ("2nd diff: ", difference_2)
if (difference == difference_2):
#print ("I'm in a sequence, first element at index", x)
else:
#print ("I'm leaving a sequence, last element at index ", y)
slice = str(x) + "," + str(y)
slices.append(slice)
x += 1
#print ("Changing X to find new slice: x:", x)
break
print (slices)
I messed something up with iterating X, at this point in time, it's an endless loop.
Maybe you can use a logic like this -
>>> A = [-1, 1, 3, 3, 3, 2, 1, 0]
>>> def indices(l):
... res = []
... for i in range(0,len(l)-2):
... diff = l[i+1] - l[i]
... for j in range(i+2,len(l)):
... if (l[j] - l[j-1]) == diff:
... res.append((i,j))
... else:
... break;
... return res
...
>>> indices(A)
[(0, 2), (2, 4), (4, 6), (4, 7), (5, 7)]
a brute force approach is to just check each slice > len 3, for each slice you just need to subtract the first and last element to get the difference and see if all a[i+1] - A[i] are equal to the difference:
def is_arith(x):
return all(x[i + 1] - x[i] == x[1] - x[0]
for i in range(len(x) - 1))
def arith_count(A):
return sum(is_arith(A[i:j])for i in range(len(A))
for j in range(i + 3,len(A)+1))
A more efficient version:
def arith_sli(A):
n = len(A)
st,tot = 0, 0
while st < n - 2:
end = st + 1
dif = A[end] - A[st]
while end < n - 1 and A[end + 1] - A[end] == dif:
end += 1
ln = end - st + 1
if ln >= 3:
tot += (ln - 2) * (ln - 1) // 2
st = end
return tot
tot += (ln - 2) * (ln - 1) // 2 is the max number of slices that can be formed for any length progression >= 3, we set st = end because no progressions can overlap.
Both return the correct output, the latter is just considerably more efficient:
In [23]: A = [-1, 1, 3, 3, 3, 2, 1, 0]
In [24]: arith_sli(A)
Out[24]: 5
In [25]: arith_count(A)
Out[25]: 5
In [26]: A = [-1, 1, 3, 3, 4, 2, 1, 0,1,2]
In [27]: arith_sli(A)
Out[27]: 3
In [28]: arith_count(A)
Out[28]: 3