I met this question in a coding interview.
You are in an infinite 2D grid where you can move in any of the 8 directions :
(x,y) to
(x+1, y),
(x - 1, y),
(x, y+1),
(x, y-1),
(x-1, y-1),
(x+1,y+1),
(x-1,y+1),
(x+1,y-1)
You are given a sequence of points you need to cover. Give the minimum number of steps in which you can achieve it. You start from the first point.
Example :
Input : [(0, 0), (1, 1), (1, 2)]
Output : 2
It takes 1 step to move from (0, 0) to (1, 1). It takes one more step to move from (1, 1) to (1, 2).
I was able to come up with a recursive solution with memoization (DP) technic with keeping the list of visited points, but still doesn't seem perfectly optimal. I am still thinking about better solution even after the interview. Can anyone come up with better solution than I did? I need help!
# #param X : list of integers
# #param Y : list of integers
# Points are represented by (X[i], Y[i])
# #return an integer
def coverPoints(self, X, Y):
if len(X) == 1:return 0
def odist(A, B): #to calculate shortest distance between a pair of points
min_d = 0 if abs(A[1]-B[1]) > abs(A[0]-B[0]) else 1
return abs(A[min_d]-B[min_d]) + (abs(A[1-min_d]-B[1-min_d])- abs(A[min_d]-B[min_d]))
D = {}
def rec(curL, last, dist):
if D.get((tuple(curL), dist), False) != False:return D[(tuple(curL),dist)]
if len(curL) == 0:return dist
else:
s = sys.maxsize
for id, i in enumerate(curL):
newL = curL[:id] + curL[id+1:]
s = min(s, rec(newL, id, odist( (X[last], Y[last]), (X[curL[id]], Y[curL[id]]) )))
D[(tuple(curL),dist)] = dist + s
return dist + s
s = rec([i for i in range(len(X))], 0, 0)
return s
Related
I'm trying to understand this Breadth First Search python implementation and I understand most of it shown in my commenting but I don't get this line here:
for dx, dy in [(-1, 0), (0, +1), (+1, 0), (0, -1)]:
a, b = current[0] + dx, current[1] + dy #Start searching in a random direction
if maze.in_maze(a, b) and not maze.is_wall(a, b) and (a, b) not in parent: #Check to see if the coordinates that are searching is inside the wall is not a wall and not inside of parent
parent[(a, b)] = current #?
dist[(a, b)] = dist[current] + 1; #?
queue.append((a, b)) #Add the coordinates to the end of the queue
The whole code can be found here, please feel free to call me out on any commenting error. I'm still new to python so I don't know exactly what every line does but I get a rough idea.
from collections import deque #A double-ended queue, or deque, supports adding and removing elements from either end. Import this from collections
nodes = 0 #Initialise nodes with value 0
def solve(maze, start, end): #Solve function that takes in the maze, start and end points as arguments
global nodes #Declare nodes as a global variable
nodes = 0 #Set nodes value to 0
queue = deque() #Set queue as a double ended queue
parent, dist = dict(), dict() #Set parent and dist
queue.append(start) #Add start point to the queue
parent[start], dist[start] = start, 1
while len(queue): #While there are items in the list
current = queue.popleft() #Set current to the first thing in the queue from the left
nodes += 1 #Increment nodes by 1
if current == end: #If the current place is the end target then solution has been found and we can exit the loop
break #Exit the loop
for dx, dy in [(-1, 0), (0, +1), (+1, 0), (0, -1)]:
a, b = current[0] + dx, current[1] + dy #Start searching in a random direction
if maze.in_maze(a, b) and not maze.is_wall(a, b) and (a, b) not in parent: #Check to see if the coordinates that are searching is inside the wall is not a wall and not inside of parent
parent[(a, b)] = current #Set later
dist[(a, b)] = dist[current] + 1; #set later
queue.append((a, b)) #Add the coordinates to the end of the queue
if end not in parent: #If there is no solution
return [] #Return an empty solution
else: #Otherwise if there is a solution
path = [] #Initialise path as an empty list
while start != end: #While the starting point is not the end point, the solution has not be found so
path.append(end) #Keep appending the end node to the path queue until they meet the condition
end = parent[end] #Set end point to the position it is in the parent dictionary
path.append(start) #Insert the starting point to the end of the queue
path.reverse() #Reverse the path queue since the solution was found back to front
return path #Return the final solution
So, in the above code, after reading, I am assuming, that start and end are represented by coordinates like (x, y).
Also, if you select any coordinate in the "maze", you can only traverse in up, down, left, right directions i.e. if you are on the coordinates (x, y) you can only go to one of the following coordinates:
(x+1, y), (x-1, y), (x, y+1), (x, y-1)
So basically, the for loop is used to select the neighboring coordinates in which you can traverse, one by one.
Then a, b = current[0] + dx, current[1] + dy this line is used to get the absolute coordinates of the neighboring coordinates.
Then we check if the new coordinate exists in the maze or if it is a wall.
If it is in the maze and not a wall and also we have not already traversed through it, we update the parent dict and also update the dist dict (for the distance).
The parent dict stores the parent of the coordinate. So for (x+1, y) the parent will be (x, y) which is current.
parent[(x+1, y)] = (x, y) that means the parent of (x+1, y) is (x, y)
the dist dict stores the distance or non of steps required to reach the new coord.
dist[(x+1, y)] = dist[(x,y)] + 1 that means, distance of new coordinate is equal to distance of parent + 1 new step.
Then we add it to the queue.
parent[(a, b)] = current
is used to store the coordinates (current) of the location from which you came to coordinates (a, b). Oh, and BTW, this comment here is wrong:
for dx, dy in [(-1, 0), (0, +1), (+1, 0), (0, -1)]:
a, b = current[0] + dx, current[1] + dy #Start searching in a random direction
It should be "Search in every direction, one at a time". There is nothing random here.
I'm trying to understand this Breadth First Search python implementation and I understand most of it shown in my commenting but I don't get this line here:
for dx, dy in [(-1, 0), (0, +1), (+1, 0), (0, -1)]:
a, b = current[0] + dx, current[1] + dy #Start searching in a random direction
if maze.in_maze(a, b) and not maze.is_wall(a, b) and (a, b) not in parent: #Check to see if the coordinates that are searching is inside the wall is not a wall and not inside of parent
parent[(a, b)] = current #?
dist[(a, b)] = dist[current] + 1; #?
queue.append((a, b)) #Add the coordinates to the end of the queue
The whole code can be found here, please feel free to call me out on any commenting error. I'm still new to python so I don't know exactly what every line does but I get a rough idea.
from collections import deque #A double-ended queue, or deque, supports adding and removing elements from either end. Import this from collections
nodes = 0 #Initialise nodes with value 0
def solve(maze, start, end): #Solve function that takes in the maze, start and end points as arguments
global nodes #Declare nodes as a global variable
nodes = 0 #Set nodes value to 0
queue = deque() #Set queue as a double ended queue
parent, dist = dict(), dict() #Set parent and dist
queue.append(start) #Add start point to the queue
parent[start], dist[start] = start, 1
while len(queue): #While there are items in the list
current = queue.popleft() #Set current to the first thing in the queue from the left
nodes += 1 #Increment nodes by 1
if current == end: #If the current place is the end target then solution has been found and we can exit the loop
break #Exit the loop
for dx, dy in [(-1, 0), (0, +1), (+1, 0), (0, -1)]:
a, b = current[0] + dx, current[1] + dy #Start searching in a random direction
if maze.in_maze(a, b) and not maze.is_wall(a, b) and (a, b) not in parent: #Check to see if the coordinates that are searching is inside the wall is not a wall and not inside of parent
parent[(a, b)] = current #Set later
dist[(a, b)] = dist[current] + 1; #set later
queue.append((a, b)) #Add the coordinates to the end of the queue
if end not in parent: #If there is no solution
return [] #Return an empty solution
else: #Otherwise if there is a solution
path = [] #Initialise path as an empty list
while start != end: #While the starting point is not the end point, the solution has not be found so
path.append(end) #Keep appending the end node to the path queue until they meet the condition
end = parent[end] #Set end point to the position it is in the parent dictionary
path.append(start) #Insert the starting point to the end of the queue
path.reverse() #Reverse the path queue since the solution was found back to front
return path #Return the final solution
So, in the above code, after reading, I am assuming, that start and end are represented by coordinates like (x, y).
Also, if you select any coordinate in the "maze", you can only traverse in up, down, left, right directions i.e. if you are on the coordinates (x, y) you can only go to one of the following coordinates:
(x+1, y), (x-1, y), (x, y+1), (x, y-1)
So basically, the for loop is used to select the neighboring coordinates in which you can traverse, one by one.
Then a, b = current[0] + dx, current[1] + dy this line is used to get the absolute coordinates of the neighboring coordinates.
Then we check if the new coordinate exists in the maze or if it is a wall.
If it is in the maze and not a wall and also we have not already traversed through it, we update the parent dict and also update the dist dict (for the distance).
The parent dict stores the parent of the coordinate. So for (x+1, y) the parent will be (x, y) which is current.
parent[(x+1, y)] = (x, y) that means the parent of (x+1, y) is (x, y)
the dist dict stores the distance or non of steps required to reach the new coord.
dist[(x+1, y)] = dist[(x,y)] + 1 that means, distance of new coordinate is equal to distance of parent + 1 new step.
Then we add it to the queue.
parent[(a, b)] = current
is used to store the coordinates (current) of the location from which you came to coordinates (a, b). Oh, and BTW, this comment here is wrong:
for dx, dy in [(-1, 0), (0, +1), (+1, 0), (0, -1)]:
a, b = current[0] + dx, current[1] + dy #Start searching in a random direction
It should be "Search in every direction, one at a time". There is nothing random here.
I have a list of objects that I have mapped on an xy-plane based on similarity. The points for these objects are stored in a 2D python list:
>>> points
[[x1, x2, ... , xn],
[y1, y2, ... , yn]]
The coordinates are floating point numbers and somewhat outline a square on a scatter plot. This is an example of 2000 objects mapped on a plane:
example of plotted points
I am trying to "fit" these points into a two-dimensional list in Python. Essentially, I want to keep the relative similarities of the points, but map them to a 2D array. For example, let's say I have these four points that form a perfect square:
(1, 1), (1, 2), (2, 1), and (2, 2)
I want an algorithm that will map them (as best as possible, obviously not perfectly unless the points form a perfectly filled square) to a 2D list like this:
[[(2, 1), (2, 2)],
[(1, 1), (1, 2)]]
If I have n points, then the 2D list generated will have m by m dimensions where m*m >= n. Any unused spots in the list will have "None" inside of it.
Here is some code I wrote attempting a solution. I pass the function a list of objects and a 2D list of their xy-coordinates, respectively. I then calculate the upper and lower bound in the x and y direction, and compute the "grids" that each spot in the list will represent based on the size of the matrix I am trying to fill. Then, I iterate through each point, and try an place it in the closest mapped spot I can find.
I try the point itself, and if it is already full, I search the perimeter around the spot in the matrix. If all those are full, I increase the "radius" of the perimeter to check until I find a spot.
This has two issues:
1) I know this is not as accurate of a mapping I could create, but I searched online and cannot find any other ideas.
2) Sometimes, this code never finishes and can't find the proper open spot. I know I can fix this bug somewhere in the calculation of the perimeter, but I figured I best spend my time looking for a more optimal solution. Hence, here I am!
This is my first SO post as well, so apologies if I didn't follow proper procedure.
Here is the code. Any assistance would be greatly appreciated.
from math import ceil, sqrt
def insert_nearest(lst, obj, i, j, N):
if lst[i][j] == None: # see if "ideal" spot is empty
lst[i][j] = obj
return
yu = i - 1 # top of box
yd = i + 1 # bottom of box
xl = j - 1 # left of box
xr = j + 1 # right of bog
while True:
to_try = [(yu, x) for x in range(xl, xr+1)] + \
[(yd, x) for x in range(xl, xr+1)] + \
[(y, xl) for y in range(yu+1, yd)] + \
[(y, xr) for y in range(yu+1, yd)] # "perimeter" points to check
for coord in to_try: # check around perimeter for any empty spots
try: # in case index is out of range
if lst[coord[0]][coord[1]] == None:
lst[coord[0]][coord[1]] = obj
return
except:
continue
# if perimeter is full, increase "raduis" of box and try again
yu -= 1
yd += 1
xl -= 1
xr += 1
def build_matrix(objList, points):
N = ceil(sqrt(len(objList)))
matrix = [[None]*N]*N
xinc = (max(points[0]) - min(points[0])) / N # grid lines in x direction
yinc = (max(points[1]) - min(points[1])) / N # grid lines in y direction
for i in range(len(points[0])):
x = min(N-1, int(points[0][i] // xinc)) # map to integer spot in list
y = min(N-1, int(points[1][i] // yinc)) # map to integer spot in list
insert_nearest(matrix, objList[i], x, y, N)
return matrix
I have to write a code to calculate route and length of a drunkard's walk.
Excersise:
A drunkard begins walking aimlessly, starting at a lamp post. At each time step he takes one step at random, either north, east, south, or west. How far will the drunkard be
from the lamp post after N steps?
In order to emulate drunkard's steps we can encode each direction with the number so that when the random variable is equal to 0 the drunkard moves north, if random variable is equal to 1 the drunkard moves east and so on.
Write a program that takes an integer argument N and simulates the motion of a random walker for N steps. After each step, print the location of the random walker, treating the lamp post as the origin (0, 0). Also, print the final squared distance from the origin.
So far I've come up with:
import random
x = 0
y = 0
def randomWalk(N):
for i in range (1, (N)):
a = random.randint
if a == 0:
x = x+0
y = y+1
return (x, y)
print (x, y)
if a == 1:
x = x+1
y = y+0
return (x, y)
print (x, y)
if a == 3:
x = x+0
y = y-1
return (x, y)
print (x, y)
if a == 3:
x = x-1
y = y+0
return (x, y)
print (x, y)
print(randomWalk(input()))
But I get None as output, when I test this code.
I would be thankful for any help with this excersise.
It's a good start.
The main problem is that you are failing to call randint:
a = random.randint
This just turns a into an alias for random.randint. Instead, it should be
a = random.randint(0, 3)
Also, you repeat if a == 3: twice.
Also, setting x and y to zero should be done inside the function, not outside.
Finally, your loop (which, by the way, is one iteration too short) doesn't really function as a loop, since you always return during the first iteration.
P.S. Here is a little parting puzzle for you. Figure out how the following works:
dx, dy = random.choice([(-1, 0), (1, 0), (0, -1), (0, 1)])
x += dx
y += dy
def randomWalk(steps):
x = 0 # Make sure you initialize the position to 0,0 each time the function is called
y = 0
directions = ['N', 'E', 'S', 'W'] # To keep track of directions, you could use strings instead of 0, 1, 2, 3.
for i in range(steps):
a = random.choice(directions) # You can use random.choice to choose a dir
if a == 'N':
y += 1
print('Current position: ({},{})'.format(x,y)) # You can print the position using format
elif a == 'S':
y -= 1
print('Current position: ({},{})'.format(x,y))
elif a == 'E':
x += 1
print('Current position: ({},{})'.format(x,y))
else:
x -= 1
print('Current position: ({},{})'.format(x,y))
Testing
>>> randomWalk(8)
Current position: (0,-1)
Current position: (1,-1)
Current position: (1,0)
Current position: (1,-1)
Current position: (0,-1)
Current position: (-1,-1)
Current position: (-1,0)
Current position: (0,0)
I have a 570 x 800 matrix with id values. What I would like to do if find the adjacent neighbors for each item. The max number of neighbors would be 8 unless the cell is along a boundary. In that case, there would be three neighbors. I want to append the neighbors to a list. I saw the posting for finding neighbors when each cell has x and y coordinates which was very helpful, but how would modify the code with no coordinates. The ids come in as a string which is fine because I use it as a key in a dictionary. Any help would be appreciated.
Assuming that what you're trying to do is construct an eight-connected grid on the matrix, and that the position of item in the the matrix defines an x- and y- co-ordinate, you can use something like this:
def eight_connected_neighbours( xmax, ymax, x, y ):
"""The x- and y- components for a single cell in an eight connected grid
Parameters
----------
xmax : int
The width of the grid
ymax: int
The height of the grid
x : int
The x- position of cell to find neighbours of
y : int
The y- position of cell to find neighbours of
Returns
-------
results : list of tuple
A list of (x, y) indices for the neighbours
"""
results = []
for dx in [-1,0,1]:
for dy in [-1,0,1]:
newx = x+dx
newy = y+dy
if (dx == 0 and dy == 0):
continue
if (newx>=0 and newx<xmax and newy >=0 and newy<ymax):
results.append( (newx, newy) )
return results
Let me give an alternate answer with numpy, which is a library you might want to consider if you're doing anything a bit more heavy duty with your data. The advantage with this method is the extensibility to the number of nearest neighbors with the parameter k. The setup:
from numpy import *
k = 1
# Create the nearest neighbors
Xidx, Yidx = mgrid[-k:k+1,-k:k+1]
# Remove the center (0,0) index
center = (Xidx==0) & (Yidx==0)
Xidx = Xidx[~center]
Yidx = Yidx[~center]
Now you can access the nearest neighbors with A[Xidx+dx, Yidx+dy] where dx and dy are the offsets for the x and y coordinates.
Example
Let's take a random matrix:
A = random.random((5,5))
print A
which for me looks like:
[[ 0.90779297 0.91195651 0.32751438 0.44830373 0.2528675 ]
[ 0.02542108 0.52542962 0.28203009 0.35606998 0.88076027]
[ 0.08955781 0.98903843 0.86881875 0.21246095 0.92005691]
[ 0.57253561 0.08830487 0.06418296 0.59632344 0.53604546]
[ 0.7646322 0.50869651 0.00229266 0.26363367 0.64899637]]
Now we can view the nearest neighbors with
dx, dy = 2,1
print "Cell value A[%i,%i] = %f " % (dx, dy, A[dx,dy])
print "k=%i nearest neighbors: "%k, A[Xidx+dx, Yidx+dy]
Giving:
Cell value A[2,1] = 0.989038
k=1 nearest neighbors: [ 0.02542108 0.52542962 0.28203009 0.08955781 0.86881875 0.57253561 0.08830487 0.06418296]
Bonus
As mentioned, by changing k you can easily get the next nearest neighbors, and next-next neighbors, etc... In addition, the ability to index a higher order array (say a tensor of rank 3) is now possible by adding an additional variable Zidx in a similar way.
Caveats
This works nicely when you go to the rightmost and bottom of your matrix - you'll get smaller lists (as you specified you wanted). However, numpy indexing (and Python) as well, wraps around, so an index of -1 will give you the last element. Thus asking for the offset at 0,0 will still give you eight entries by wrapping around. The other answers here show a good way to check for this.
If you want to grab something on the left side edge (and you really don't want to use an if statement), you might change the index as such (making sure to remove the center element as above):
# Create the nearest neighbors (ON THE LEFT EDGE)
Xidx_left, Yidx_left = mgrid[0:k+1,-k:k+1]
code with no coordinates? Do you mean like this:
XMAX = 800
YMAX = 570
NEIGHBOURS = [(-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1)]
matrix = range(XMAX * YMAX)
def all_neighbours(m):
for i in xrange(len(m)):
ns = []
y, x = divmod(i, XMAX)
for u, v in NEIGHBOURS:
ux = u + x
vy = v + y
if 0 <= ux < XMAX and 0 <= vy < YMAX:
ns.append(ux + vy * YMAX)
yield i, ns
if __name__ == '__main__':
for field, neighbours in all_neighbours(matrix):
print field, neighbours