简体   繁体   中英

Fastest way to search if a coordinate is inside a cube

In my code, I am constantly receiving a stream of 3D integer coordinates (x,y,z). Each coordinate has to be checked against a list of regions, to see if it is in those regions. Each region is defined by the opposing high and low coordinates. If the code finds the coordinate is in a region, it takes further action, otherwise it simply exits to continue looking for a match to another region or exits entirely if no region matched.

Since this is happening so fast, I want to get through the whole list of regions as quickly as possible, create a match, or determine it's not a match for any, and move on to the next coordinate. What i am doing presently 'works' and is nice and readable (to me), but needs some optimization:

firstcorner = self.regions["regions"][name]["pos1"]
secondcorner = self.regions["regions"][name]["pos2"]
Xmin = firstcorner[0] - 1  # XXXXXXX
Ymin = firstcorner[1] - 1
Zmin = firstcorner[2] - 1
Xmax = secondcorner[0] + 1  # XXXXXXXX
Ymax = secondcorner[1] + 1
Zmax = secondcorner[2] + 1
BX = position[0]  # XXXXXXX
BY = position[1]
BZ = position[2]
inX = (BX > Xmin) and (BX < Xmax)  # XXXXXXXX
inZ = (BZ > Zmin) and (BZ < Zmax)
inY = (BY > Ymin) and (BY < Ymax)
if inX and inY and inZ: 

I thought about nesting this such that it would match the X items first; if the X falls inside the coordinates, only then try to see if Z, and finally Y...

What can I do to create the fastest Python 2.7 code?

You can use all to chain together (and properly short-circuit) conditionals.

def point_inside(rectangle, point):
    firstcorner, secondcorner = rectangle
    xmin, xmax = firstcorner[0]-1, secondcorner[0]+1
    yield xmin < point[0] < xmax
    ymin, ymax = firstcorner[1]-1, secondcorner[1]+1
    yield ymin < point[1] < ymax
    zmin, zmax = firstcorner[2]-1, secondcorner[2]+1
    yield zmin < point[2] < zmax

rect = (firstcorner, secondcorner)

if all(point_inside(rect, position)):
    # it's inside the cube

However this is far more intelligible if you just write up some class definitions and call them objects.

class Rectangle(object):
    def __init__(self, xrange, yrange, zrange):
        self.xrange = xrange  # (xmin, xmax)
        self.yrange = yrange
        self.zrange = zrange

    def contains_point(self, p):
        if not all(hasattr(p, loc) for loc in 'xyz'):
            raise TypeError("Can only check if 3D points are in the rect")
        return all([self.xrange[0] <= p.x <= self.xrange[1],
                    self.yrange[0] <= p.y <= self.yrange[1]
                    self.zrange[0] <= p.z <= self.zrange[1]])

    # BONUS!
    @classmethod
    def from_points(cls, firstcorner, secondcorner):
        """Builds a rectangle from the bounding points

        Rectangle.from_points(Point(0, 10, -10),
                              Point(10, 20, 0)) == \
                Rectangle((0, 10), (10, 20), (-10, 0))

        This also works with sets of tuples, e.g.:
        corners = [(0, 10, -10), (10, 20, 0)]
        Rectangle.from_points(*corners) == \
                Rectangle((0, 10), (10, 20), (-10, 0))
        """
        return cls(*zip(firstcorner, secondcorner))

class Point(object):
    def __init__(self, x, y z):
        self.x = x
        self.y = y
        self.z = z

    def __iter__(self): 
        yield from (self.x, self.y, self.z)

rect = Rectangle((0, 10), (10, 20), (-10, 0))
# firstpoint, secondpoint in this analogy would be:
# # (0, 10, -10), (10, 20, 0)
inside_point = Point(3, 15, -8)
outside_point = Point(11, 15, -8)

rect.contains_point(inside_point)  # True
rect.contains_point(outside_point)  # False

The simplest approach I've found is to ask if the point is outside of the cube by checking if any coordinate is greater than both of any of the corners:

import numpy as np
def inCube(X, corners):
    """
    Check if a point `X` is inside of rectangular prison with `corners` (two points)
    """
    # Where is X > corners?
    greater = X > corners
    # If X is greater than both corners of any axis, it is outside
    inside = ~np.any(np.equal(*greater))

corners = [[0, 0, 0], [1, 1, 1]]
Xs = np.array([[.5, .5, .5], 
              [-.5, .5, .5]])
[inCube(X, corners) for X in Xs] # [True, False]

1.1)When you check if the coordinate is in a region you need to compute six comparisons (one for each bound). An idea that might help is to nest said comparisons so that if one of them is false then you don't bother checking the others. On average you will compute less than six comparison per region (may be three?). Here's an example in pseudo code:

# for a region that has the following bounds: ((X1,X2),(Y1,Y2),(Z1,Z2))
if(check X1):
  if(check X2):
    if(check Y1):
      if(check Y2):
        if(check Z1):
          if(check Z2):
            return "found it!!"

Consider the case of a unidimensional coordinate space (only x axis) divided in N regions. In the method posted in the question you would need to make 2 comparison per region, adding up to 2N comparisons. With the method proposed it comes to an average of 1.5N comparisons. For the case of N³ identical cubic regions forming a bigger cube of side N regions, originally you would have needed 6N³ comparisons, and now you would need 2N³+3N²+3N+1 comparisons.

1.2)You can evaluate less comparisons if regions that share the same bounds are nested under the same conditional. For example:

# regions A and B share bounds (X1,X2)
if(check X1):
  if(check X2):
    if(check Y1):
      if(check Y2):
        if(check Z1):
          if(check Z2):
            return "found it in region A!!"
    if(check Y3):
      if(check Y4):
        if(check Z3):
          if(check Z4):
            return "found it in region B!!"

This should reduce significantly the computational cost in the case of cubic regions forming a bigger cube, but I didn't bother to go through the calculations.

2)You can definitely speed up the searching through the regions list by using a search tree (specially useful if regions not highly organized as in the cube of cubic regions case). What I propose is that you index the regions as belonging to parent regions. Check if a coordinate is in the parent regions; if it is, only then check if it belongs to the regions indexed under said parent. In a way this is conceptually similar to what is done in (1.2)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM