Manhattan distance with impassible tiles - python [duplicate] - python

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.

Related

Why does my pygame code become painfully slow after a certain time? Is there a way to make it run faster?

I wrote a program to model a phenomenon called diffusion limited aggregation, using the random motion of squares in pygame. The idea is that there is a stem and every particle (square) that touches it sticks to it and becomes part of the stem.
The code seems to work, but after like 30 seconds to a minute it starts to slow down quite a bit. I cannot figure out why.
import pygame
import random
#changing the row number will change the size of the squares, and bassically the size of the invisible 'array'
width = 1000
rows = 500
d = width//rows
e = {}
squares = []
accreted = []
#note: all positions are noted in array-like notation (a matrix of dimensions rows x rows)
#to convert it back to normal notation, do (square.position[0] * d, square.position[1] * d)
class square:
def __init__(self, position):
self.position = (position)
#method to move a square in a random direction (no diagonal) (brownian motion)
def move(self):
a = random.randint(0, 3)
if a == 0:
new_posi = (self.position[0] + 1)
new_posj = (self.position[1])
elif a == 1:
new_posi = (self.position[0] - 1)
new_posj = (self.position[1])
elif a == 2:
new_posi = (self.position[0])
new_posj = (self.position[1] + 1)
else:
new_posi = (self.position[0])
new_posj = (self.position[1] - 1)
if new_posj<0 or new_posi<0 or new_posi>rows or new_posj>rows:
self.move()
else:
self.position = (new_posi, new_posj)
pygame.draw.rect(win, (255, 255, 255), [new_posi * d, new_posj * d, d, d])
def accrete(square):
accreted.append(square)
if square in squares:
squares.remove(square)
def redrawWindow(win):
win.fill((0, 0, 0))
pygame.draw.rect(win, (255, 255, 255), [stem.position[0] * d, stem.position[1] * d, d, d])
for square in squares:
square.move()
# here we make it so that every square that touches the stem stops moving, then a square that touches this square stops moving, etc.
for accret in accreted:
if square.position[1] == accret.position[1]+1 and square.position[0] == accret.position[0]:
accrete(square)
elif square.position[1] == accret.position[1]-1 and square.position[0] == accret.position[0]:
accrete(square)
elif square.position[1] == accret.position[1] and square.position[0] == accret.position[0]+1:
accrete(square)
elif square.position[1] == accret.position[1] and square.position[0] == accret.position[0]-1:
accrete(square)
for accret in accreted:
pygame.draw.rect(win, (255, 255, 255), [accret.position[0] * d, accret.position[1] * d, d, d])
pygame.display.update()
def main():
global win
win = pygame.display.set_mode((width, width))
clock = pygame.time.Clock()
while True:
# pygame.time.delay(5)
# clock.tick(64)
redrawWindow(win)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
#by changing the range here, we change how many squares are created
for i in range(5000):
e["accreted{0}".format(i)] = square((random.randint(0, rows), random.randint(0, rows)))
squares.append(e["accreted{0}".format(i)])
#a stem to start accretion from
stem = square((rows/2, rows/2))
accrete(stem)
main()
I've watched your simulation run for a while on my MacBook Pro. It seems that on my system, it takes quite a bit longer than a minute...maybe more like 5 or so...before it starts to quite obviously slow down. But it does nonetheless.
I think the problem is that you are building this "accreted" structure during your simulation, and due to this, the number of "accreted" squares (those stored in the accreted list) keeps increasing. For each iteration of your program, your code needs to compare the position of each live square against the position of each "accreted" square. So over time, the number of comparisons you have to do continues to grow.
You likely need to find a way to optimize your algorithm if you want to be able to maintain the update speed (the frame rate) as the sim progresses. You need to figure out how to be smarter about performing your comparisons to somehow avoid this geometric progression in iteration time that occurs as the structure you're building grows.
UPDATE AND POSSIBLE OPTIMIZATION: I see a pretty simple optimization that you could add to your code to greatly speed it up. What you can do is maintain a bounding box around your "accreted" squares. When you add a new square to that list, you increase the size of the bounding box, if necessary, so that it contains the new square. Now, when you first check for a collision between a live square and an the list of accreted squares, you can first check for if the live square is within the bounding box of the accreted squares (with a little extra margin as appropriate) before testing for a collision between that square and any one accreted squares. This would let you immediately rule out collisions between most of the live squares and the accreted squares with just one collision test for each live square. This should have the effect of allowing your code to stay about as fast in later rounds as it is in the early rounds, since most of the live squares will always be trivially rejected as collision candidates regardless who big the accreted structure gets.
UPDATE 2: What I describe is definitely what's going on with your code. I added a little code to count the number of collision tests you perform in each round of your sim. Here's the number of tests being performed at one second intervals, along with how long one iteration of your sim is taking, in seconds:
0 5000 0.023629821000000106
1 9998 0.023406135000000106
2 24980 0.03102543400000002
...
30 99680 0.07482247300000111
31 99680 0.08382184299999551
...
59 114563 0.08984024400000123
60 114563 0.087317634999998
The first iteration of your code does 5000 hit tests, as expected, and a single iteration of your sim takes about .023 seconds. After a minute, your sim is having to do more than 20 times as many tests per iteration, 114563, and now a single iteration is taking .087 seconds. This problem just keeps growing, and your code just keeps slowing down. (It's interesting to note that most of the "progress" of your sim up to one minute has occurred in the first 30 seconds. For this run, only 3 accretes occurred in the second 30 second interval.)
The first and probably best optimization is to keep a list of "forbidden" squares that will trigger the freeze of a particle instead of iterating over all positions in accreted multiple times.
So e.g. when we start with the first stem particle (or square, or whatever you call it), we also store the positions above, beneath, left and right of that particle's position in a set. It's important to use a set because looking up items in a set is much, much faster that using a list.
After moving a square, we now check if that square's new position is in this set. If it is, we add all adjacent positions to that set, also.
The next thing we can improve is the list of squares itself. Instead of removing a freezed square from the list, we create a new list every frame and add all squares that are not freezed this turn. (Your code is actually buggy by not making a copy of the list of squares and iterating over that copy while removing items from the original list) I'll use a deque since adding to it is slightly faster that a regular list.
Another bottleneck is the huge amount of random numbers you create each frame. random.randint() becomes painfully slow. We could create a list of random numbers at the start of the script and use that so we don't have to create new random numbers while running.
We could also change the drawing. Using pygame.draw.rect 5000 times is also quite slow. Let's create a surface and blit it with pygame's new batch function Surface.blits (I guess using pygame.surfarray to manipulate the screen surface directly would be even faster).
In the code below I also implemented the bounding box suggested by CryptoFool, because why not, but the biggest speed up is using a set as I described above.
With these changes, I get ~200 FPS without any slowdown over time:
import pygame
import numpy as np
import random
from collections import deque
def get_rand_factory():
length = 100000000
sample = np.random.randint(1, 5, length).tolist()
index = -1
def inner():
nonlocal index
index += 1
if index == length:
index = 0
return sample[index]
return inner
get_rand = get_rand_factory()
def move(x, y, rows):
dx, dy = x, y
a = get_rand()
if a == 1: dx += 1
elif a == 2: dx -= 1
elif a == 3: dy += 1
else: dy -= 1
if dx<0 or dy<0 or dx>rows or dy>rows:
return move(x, y, rows)
return dx, dy
def get_adjacent(x, y):
for dx, dy in (1, 0), (-1, 0), (0, 1), (0, -1):
yield x + dx, y + dy
def get_bounds(positions):
min_x, min_y, max_x, max_y = 9999, 9999, 0, 0
for x, y in positions:
min_x = min(min_x, x)
min_y = min(min_y, y)
max_x = max(max_x, x)
max_y = max(max_y, y)
return min_x, min_y, max_x, max_y
def main():
width = 1000
rows = 500
d = width//rows
squares = deque()
accreted = set()
adjacent_accreted = set()
win = pygame.display.set_mode((width, width))
clock = pygame.time.Clock()
for i in range(5000):
pos = (random.randint(0, rows), random.randint(0, rows))
squares.append(pos)
stem = (rows/2, rows/2)
accreted.add(stem)
adjacent_accreted.add(stem)
for adj in get_adjacent(*stem):
adjacent_accreted.add(adj)
rect_white = pygame.Surface((d, d))
rect_white.fill('white')
rect_blue = pygame.Surface((d, d))
rect_blue.fill((255, 0, 255))
bounds = get_bounds(adjacent_accreted)
min_x, min_y, max_x, max_y = bounds
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
win.fill((0, 0, 0))
new_state = deque()
for x, y in squares:
die = False
new_pos = move(x, y, rows)
if min_x <= new_pos[0] <= max_x and min_y <= new_pos[1] <= max_y:
if new_pos in adjacent_accreted:
accreted.add(new_pos)
adjacent_accreted.add(new_pos)
for adj in get_adjacent(*new_pos):
adjacent_accreted.add(adj)
die = True
bounds = get_bounds(adjacent_accreted)
min_x, min_y, max_x, max_y = bounds
if not die:
new_state.append(new_pos)
squares = new_state
win.blits(blit_sequence=((rect_blue, (pos[0]*d, pos[1]*d)) for pos in accreted))
win.blits(blit_sequence=((rect_white, (pos[0]*d, pos[1]*d)) for pos in squares))
pygame.draw.rect(win, (0, 255, 255), [bounds[0] * d, bounds[1] * d, (bounds[2]-bounds[0]) * d, (bounds[3]-bounds[1]) * d], 1)
pygame.display.update()
pygame.display.set_caption(f'{clock.get_fps():.2f} {len(squares)=} {len(accreted)=}')
clock.tick()
main()
Have a look at your code:
def accrete(square):
accreted.append(square)
if square in squares:
squares.remove(square)
squares is a list containing up to 5000 items. Searching for any content needs up to 5000 comparisons because there is no index and all items have to be checked until it is found in the list. Use a set instead if the entries are unique and the order does not care. A set in indexed and searching for an item runs very fast.

Understanding a python breadth-first-search algorithm

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.

Seeking better dynamic programming solution to find algorithm with less complexity

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

Python: Drunkard's walk

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)

Listing adjacent cells

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

Categories

Resources