Context: I am trying to write an A* search in an unknown environment. To do this, I am maintaining a representation of the environment in a 2-D or 3-D list (depending on the environment), and another n-D list that represents the agent's knowledge of the environment.
When the agent moves, I check the area around them with the actual environment. If there is a discrepancy, their map gets updated, and I run A* again.
Problem: What is the fastest method to check if there is a difference between the ranges of these two lists?
Naive Solution:
from itertools import product
from random import randint
width, height = 10, 10
known_environment = [[0 for x in range(width)] for y in range(height)]
actual_environment = [[0 for x in range(width)] for y in range(height)]
# Populate with obstacles
for i in xrange(10):
x = randint(0, len(actual_environment) - 1)
y = randint(0, len(actual_environment[x]) - 1)
actual_environment[x][y] += 1
# Run A* and get a path
path = [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4),
(5, 5), (6, 6), (7, 7), (8, 8), (9, 9)] # dummy path
# Traverse path, checking for "new" obstacles
for step in path:
x, y = step[0], step[1]
# check area around agent
for (i, j) in product([-1, 0, 1], [-1, 0, 1]):
# don't bother checking out-of-bounds
if not 0 <= x + i < width:
continue
if not 0 <= y + j < height:
continue
# internal map doesn't match real world, update
if not known_environment[x + i][ y + j] == actual_environment[x + i][ y + j]:
known_environment[x + i][ y + j] = actual_environment[x + i][ y + j]
# Re-run A*
This works, but it feels inefficient. I'm thinking I could replace the loop with something like set(known_environment).intersection(actual_environment) to check if there is a discrepancy, and then update if needed; but this can probably be improved upon as well.
Thoughts?
Edit: I've switched over to numpy slicing, and use array_equal instead of sets.
# check area around agent
left = x - sight if x - sight >= 0 else 0
right = x + sight if x + sight < width else width - 1
top = y - sight if y - sight >= 0 else 0
bottom = y + sight if y + sight < height else height - 1
known_section = known_environment[left:right + 1, top:bottom + 1]
actual_section = actual_environment[left:right + 1, top:bottom + 1]
if not np.array_equal(known_section, actual_section):
known_environment[left:right + 1, top:bottom + 1] = actual_section
It should already be a tad faster, when you employ the solution concept from the link given in my comment to the question.
I modified / hacked up a bit the given code and tried:
#! /usr/bin/env python
from __future__ import print_function
from itertools import product
from random import randint
width, height = 10, 10
known_env = [[0 for x in range(width)] for y in range(height)]
actual_env = [[0 for x in range(width)] for y in range(height)]
# Populate with obstacles
for i in xrange(10):
x = randint(0, len(actual_env) - 1)
y = randint(0, len(actual_env[x]) - 1)
actual_env[x][y] += 1
# Run A* and get a path
path = [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4),
(5, 5), (6, 6), (7, 7), (8, 8), (9, 9)] # dummy path
def effective_slices(i_w, j_h):
"""Note: Depends on globals width and height."""
w, h = width - 1, height - 1
i_w_p, j_h_p = max(0, i_w - 1), max(0, j_h - 1)
i_w_s, j_h_s = min(w, i_w + 1), min(h, j_h + 1)
return slice(i_w_p, i_w_s), slice(j_h_p, j_h_s)
# Traverse path, checking for "new" obstacles
for step in path:
x, y = step[0], step[1]
# check area around agent
dim_w, dim_h = effective_slices(x, y)
actual_set = set(map(tuple, actual_env[dim_w][dim_h]))
known_set = set(map(tuple, known_env[dim_w][dim_h]))
sym_diff = actual_set.symmetric_difference(known_set)
if sym_diff: # internal map doesn't match real world, update
for (i, j) in product(range(dim_w.start, dim_w.stop + 1),
range(dim_h.start, dim_h.stop + 1)):
if known_env[i][j] != actual_env[i][j]:
known_env[i][j] = actual_env[i][j]
# Re-run A*
(Edited): Added some kind of reuse of indexing above in eager update loop.
2nd Edit to accommodate for comment w.r.t. updated question (cf. comment below):
Looking at the amended question i.e. the snippet now relating to a numpy based implementation, I'd suggest two changes that would make the code clearer to me at least:
To avoid the literal + 1 clutter address the issue of slices excluding the stop by introducing supreme values for right and bottom
Define the section boundary box with minand max to make the relation clear.
Like so:
# ... 8< - -
# check area around agent (note: uses numpy)
sight = 1
left, right_sup = max(0, x - sight), min(x + sight + 1, width)
top, bottom_sup = max(0, y - sight), min(y + sight + 1, height)
known_section = known_environment[left:right_sup, top:bottom_sup]
actual_section = actual_environment[left:right_sup, top:bottom_sup]
if not np.array_equal(known_section, actual_section):
known_environment[left:right_sup, top:bottom_sup] = actual_section
# - - >8 ...
... or getting rid of colon-itis (sorry):
# ... 8< - -
h_slice = slice(max(0, x - sight), min(x + sight + 1, width))
v_slice = slice(max(0, y - sight), min(y + sight + 1, height))
known_section = known_environment[h_slice, v_slice]
actual_section = actual_environment[h_slice, v_slice]
if not np.array_equal(known_section, actual_section):
known_environment[h_slice, v_slice] = actual_section
# - - >8 ...
Should be concise read, easy on the run time and nice playground.
... but image processing (e.g. with fixed masks) and staggered grid processing algorithms should be abound to offer ready made solutions
Related
I need a fast way to create a list of tuples representing image pixel coordinates (X, Y).
Where X is from 0 to size and Y is from 0 to size.
A step value of 1 results in X and Y values of (0, 1, 2, 3...) which is too many tuples. Using a step value greater than 1 will reduce processing time. For example, if the step value is 2 the values would be (0, 2, 4, 6...). If the step value is 4 the values would be (0, 4, 8, 12...).
In pure python range command might be used. However, NumPy is installed by default in my Linux distribution. In NumPy the arrange command might be used but I'm having a hard time wrapping my mind around NumPy array syntax.
PS: After a list of tuples is created it will be randomly shuffled and then read in the loop.
Edit 1
Using this answer below:
Instead of the image fading in it's doing some kind of weird wipe left to right. Using the code from the answer with a slight modification:
step = 4
size = self.play_rotated_art.size[0] - step
self.xy_list = [
(x, y)
for x in range(0, size - step, step)
for y in range(0, size - step, step)
]
Bug Update
There was an error in my code, it's working fine now:
The updated code is:
self.step = 4
size = self.play_rotated_art.size[0] - self.step
self.xy_list = [
(x, y)
for x in range(0, size - self.step, self.step)
for y in range(0, size - self.step, self.step)
]
shuffle(self.xy_list)
# Convert numpy array into python list & calculate chunk size
self.current_chunk = 0
self.chunk_size = int(len(self.xy_list) / 100)
# Where we stop copying pixels for current 1% chunck
end = self.current_chunk + self.chunk_size
if end > len(self.xy_list) - 1:
end = len(self.xy_list) - 1
while self.current_chunk < end:
x0, y0 = self.xy_list[self.current_chunk]
x1 = x0 + self.step
y1 = y0 + self.step
box = (x0, y0, x1, y1)
region = self.play_rotated_art.crop(box)
self.fade.paste(region, box)
self.current_chunk += 1
self.play_artfade_count += 1
return self.fade
TL;DR
I already have code with step value 1 but this code is overly complex and inefficient to request a modification. The above generic question would help others more and, still help me, if it were answered.
Existing code with step value 1:
def play_artfade2(self):
''' PILLOW VERSION:
Fade in artwork in 100 chunks leaving loop after chunk and
reentering after Tkinter updates screen and pauses.
'''
if self.play_artfade_count == 100:
# We'have completed a full cycle. Force graphical effects exit
self.play_artfade_count = 0 # Reset art fade count
self.play_rotated_value = -361 # Force Spin Art
return None
# Initialize numpy arrays first time through
if self.play_artfade_count == 0:
# Create black image to fade into
self.fade = Image.new('RGBA', self.play_rotated_art.size, \
color='black')
# Generate a randomly shuffled array of the coordinates
im = np.array(self.play_rotated_art)
X,Y = np.where(im[...,0]>=0)
coords = np.column_stack((X,Y))
np.random.shuffle(coords)
# Convert numpy array into python list & calculate chunk size
self.xy_list = list(coords)
self.current_chunk = 0
self.chunk_size = int(len(self.xy_list) / 100)
# Where we stop copying pixels for current 1% chunck
end = self.current_chunk + self.chunk_size
if end > len(self.xy_list) - 1:
end = len(self.xy_list) - 1
while self.current_chunk < end:
x0, y0 = self.xy_list[self.current_chunk]
x1 = x0 + 1
y1 = y0 + 1
box = (x0, y0, x1, y1)
region = self.play_rotated_art.crop(box)
self.fade.paste(region, box)
self.current_chunk += 1
self.play_artfade_count += 1
return self.fade
Using Pillow's Image.crop() and Image.paste() is overkill for a single pixel but the initial working design was future focused to utilize "super pixels" with box size of 2x2, 3x3, 5x5, etc as image is resized from 200x200 to 333x333 to 512x512, etc.
I need fast way to create a list of tuples representing image pixel coordinates (X, Y).
Where X is from 0 to size and Y is from 0 to size
A list comprehension with range will work:
xsize = 10
ysize = 10
coords = [(x, y) for x in range(xsize) for y in range(ysize)]
# this verifies the shape is correct
assert len(coords) == xsize * ysize
If you wanted a step other than 1, this is setting the step argument:
coords = [(x, y) for x in range(0, xsize, 2) for y in range(0, ysize, 2)]
You can use a generator expression:
size = 16
step = 4
coords = (
(x, y)
for x in range(0, size, step)
for y in range(0, size, step)
)
Then you can iterate on that like you would do with a list
for coord in coords:
print(coord)
Using a generator instead of a list or tuple has the advantage of being more memory efficient.
I currently have a function that, given a list of x,y pairs, will calculate the minimum distance between any of the two pairs in the list and return that distance. I would like to modify the code below so that instead of returning the distance itself, it returns the two pairs that yielded that minimum distance in the same order as they were in the input list. For example, if the given input list was [(1, 2), (4, 5), (5, 5), (4, 1)] the resulting output would be ((4, 5), (5, 5)).
import math
#distance function
def distance(p1, p2):
return math.sqrt((p1[0] - p2[0])**2+(p1[0] - p2[0])**2)
def closest_neighbor(point_list):
if(len(point_list) < 2):
return None
else:
dist = []
for i in range(len(point_list) - 1):
for j in range(i +1, len(point_list)):
x = point_list[i]
y = point_list[j]
dist += [distance(point_list[i], point_list[j])]
return dist
Assuming you fix your currently incorrect distance function, then it is as simple as:
>>> min( (distance(x, y), (x, y)) for x, y in itertools.combinations(point_list, 2))[1]
((4, 5), (5, 5))
Here we generate all combinations of two points from the list, calculate their distance, and take advantage of the fact that tuples sort element-wise.
(You will need to add a check for the length of point_list if that is important to you)
import math
#distance function
def distance(p1, p2):
return math.sqrt((p1[0] - p2[0])**2+(p1[1] - p2[1])**2) # the original function was wrong
def closest_neighbor(point_list):
if(len(point_list) < 2):
return None
else:
min = -1
pair = []
for i in range(len(point_list) - 1):
for j in range(i +1, len(point_list)):
x = point_list[i]
y = point_list[j]
dist = distance(point_list[i], point_list[j]) # this could be changed to distance(x,y)
if dist < min or min == -1:
pair = [x,y] # store the pair with min distance
min = dist # store the min distance to later comparison
return pair
print(closest_neighbor( [(1, 2), (4, 5), (5, 5), (4, 1)]))
I am following a short youtube video tutorial on Monte Carlo problems with python (https://www.youtube.com/watch?v=BfS2H1y6tzQ) and the code isn't working. The goal is to see how many times I will have to take transport to get back home, considering you take transport if the distance is greater than 4.
So I assumed the issue was that every time random_walk was called, the x,y variables are being reset to zero so the distance is never always within a 0-1 range and isn't incrementing as expected.
import random
def random_walk(n):
x, y = 0, 0
for i in range(n):
(dx, dy) = random.choice([(0, 1), (0, -1), (1, 0), (-1, 0)])
x += dx
y += dy
return (x, y)
number_of_walks = 10000
no_transport = 0
for walk_length in range(1, 31):
for i in range(number_of_walks):
(x, y) = random_walk(walk_length)
distance = abs(x) + abs(y)
if distance <= 4:
no_transport += 1
transported_percentage = float(no_transport) / number_of_walks
print("Walk Size = ", walk_length, " / % transported = ", 100 * transported_percentage)
I expect results to show what % of the times I transported did I have to take transport home, instead, I get inaccurate numbers like 100, 200, 300%. Could the video tutorial have incorrect code?
You need to reset the no_transport inside the main loop, because it's cumulative over all your tests instead of for each walk length.
for walk_length in range(1, 31):
no_transport = 0
Also the percentage is calculating the number for no_transport walks, not the percentage of transport walks: This is percentage of transported.
transported_percentage = (number_of_walks - float(no_transport)) / number_of_walks
I'm trying to figure out how to shrink a polygon using only the coordinates of its corners. For example, if I have the following shape with corners at [(0, 0), (0, 100), (20, 100), (30, 60), (40, 100), (60, 100), (60, 0), (40, 10), (40, 40), (20, 40), (20, 10)] so the shape looks like this:
And I want to find the corner coordinates for if I shrink this polygon by some width and height factor. For example, If I want to shrink its width by 10% and height by 20% then this could be shown as something like this:
I was trying to do this using cv2.resize() by could not get the corners after resizing. I have been trying to find an algorithm for polygon resizing or polygon shrinking, but cannot find anything about how to do this. Do any algorithms or packages for doing something like this exist?
I have tested this solution on more than 1200 polygons of real life building in california and it works like charm.
One more thing is that this same approach works find for enlarging polygons as well equally well.
This below method can be used as it is:
def shrink_or_swell_shapely_polygon(my_polygon, factor=0.10, swell=False):
''' returns the shapely polygon which is smaller or bigger by passed factor.
If swell = True , then it returns bigger polygon, else smaller '''
from shapely import geometry
#my_polygon = mask2poly['geometry'][120]
shrink_factor = 0.10 #Shrink by 10%
xs = list(my_polygon.exterior.coords.xy[0])
ys = list(my_polygon.exterior.coords.xy[1])
x_center = 0.5 * min(xs) + 0.5 * max(xs)
y_center = 0.5 * min(ys) + 0.5 * max(ys)
min_corner = geometry.Point(min(xs), min(ys))
max_corner = geometry.Point(max(xs), max(ys))
center = geometry.Point(x_center, y_center)
shrink_distance = center.distance(min_corner)*0.10
if swell:
my_polygon_resized = my_polygon.buffer(shrink_distance) #expand
else:
my_polygon_resized = my_polygon.buffer(-shrink_distance) #shrink
#visualize for debugging
#x, y = my_polygon.exterior.xy
#plt.plot(x,y)
#x, y = my_polygon_shrunken.exterior.xy
#plt.plot(x,y)
## to net let the image be distorted along the axis
#plt.axis('equal')
#plt.show()
return my_polygon_resized
As far as I have understood you are searching the functionality of ST_Buffer from postgis, but with separated factors.
This is unfortunately not easy to achieve (see one question in the qgis-stack for more on it).
But if it already helps to do it with the same factor of x and y (or as a start for a more elaborate algorithm) here you go:
One library that makes ST_Buffer function accessible within python is shapely.
(If you need more geo-data specific power geoalchemy2 might be the better option. Beware the crs/srid changes in that case)
from shapely import geometry
import matplotlib.pyplot as plt
# your variables
coords = [(0, 0), (0, 100), (20, 100), (30, 60), (40, 100), (60, 100), (60, 0), (40, 10), (40, 40), (20, 40), (20, 10)]
lines = [[coords[i-1], coords[i]] for i in range(len(coords))]
# your factor of 10%
# Note: with 20% the polygon becomes a multi-polygon, so a loop for plotting would be needed.
factor = 0.1
# code from nathan
xs = [i[0] for i in coords]
ys = [i[1] for i in coords]
x_center = 0.5 * min(xs) + 0.5 * max(xs)
y_center = 0.5 * min(ys) + 0.5 * max(ys)
min_corner = geometry.Point(min(xs), min(ys))
max_corner = geometry.Point(max(xs), max(ys))
center = geometry.Point(x_center, y_center)
shrink_distance = center.distance(min_corner)*factor
assert abs(shrink_distance - center.distance(max_corner)) < 0.0001
my_polygon = geometry.Polygon(coords)
my_polygon_shrunken = my_polygon.buffer(-shrink_distance)
x, y = my_polygon.exterior.xy
plt.plot(x,y)
x, y = my_polygon_shrunken.exterior.xy
plt.plot(x,y)
# to net let the image be distorted along the axis
plt.axis('equal')
plt.show()
I misread the question, I'm leaving up the anwer because it might help someone, but I realize the final output is not the desired one
To get the new coordinates of the polygon after shrinking you can multiply all coordinates (position vectors) with the shrinkage factor like this:
x_shrink = 0.1
y_shrink = 0.2
coords = [(0, 0), (0, 100), (20, 100), (30, 60), (40, 100), (60, 100), (60, 0), (40, 10), (40, 40), (20, 40), (20, 10)]
xs = [i[0] for i in coords]
ys = [i[1] for i in coords]
# simplistic way of calculating a center of the graph, you can choose your own system
x_center = 0.5 * min(xs) + 0.5 * max(xs)
y_center = 0.5 * min(ys) + 0.5 * max(ys)
# shrink figure
new_xs = [(i - x_center) * (1 - x_shrink) + x_center for i in xs]
new_ys = [(i - y_center) * (1 - y_shrink) + y_center for i in ys]
# create list of new coordinates
new_coords = zip(new_xs, new_ys)
This outputs the following (blue is original, green is the shrunk polygon)
I do not think it is mathematically possible to have a percentage shrink in x, a percentage shrink in y and never have the layout move outside the original layout. That is however just a hunch.
This code shifts all the lines a certain amount closer to the center and then finds the new intersection points of all the lines:
import matplotlib.pyplot as plt
def det(a, b):
return a[0] * b[1] - a[1] * b[0]
def line_intersection(line1, line2):
xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0])
ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1]) #Typo was here
div = det(xdiff, ydiff)
if div == 0:
raise Exception('lines do not intersect')
d = (det(*line1), det(*line2))
x = det(d, xdiff) / div
y = det(d, ydiff) / div
return x, y
# how much the coordinates are moved as an absolute value
shrink_value_x = 3
shrink_value_y = 1.5
# coords must be clockwise
coords = [(0, 0), (0, 100), (20, 100), (30, 60), (40, 100), (60, 100), (60, 0), (40, 10), (40, 40), (20, 40), (20, 10)]
lines = [[coords[i-1], coords[i]] for i in range(len(coords))]
new_lines = []
for i in lines:
dx = i[1][0] - i[0][0]
dy = i[1][1] - i[0][1]
# this is to take into account slopes
factor = 1 / (dx*dx + dy*dy)**0.5
new_dx = dy*shrink_value_x * factor
new_dy = dx*shrink_value_y * factor
new_lines.append([(i[0][0] + new_dx, i[0][1] - new_dy),
(i[1][0] + new_dx, i[1][1] - new_dy)])
# find position of intersection of all the lines
new_coords = []
for i in range(len(new_lines)):
new_coords.append((line_intersection(new_lines[i-1], new_lines[i])))
I got the line intersection code from this answer #Paul Draper.
This outputs
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