确定网格(阵列)位置的有效方法

时间:2009-09-26 03:58:51

标签: python arrays list performance

我在python中表示一个带有2D列表的网格。我想在列表中选择一个点(x,y)并确定它的位置......右边缘,左上角,中间的某个位置......

目前我正在检查:

         # left column, not a corner
         if x == 0 and y != 0 and y != self.dim_y - 1:
             pass
         # right column, not a corner
         elif x == self.dim_x - 1 and y != 0 and y != self.dim_y - 1:
             pass
         # top row, not a corner
         elif y == 0 and x != 0 and x != self.dim_x - 1:
             pass
         # bottom row, not a corner
         elif y == self.dim_y - 1 and x != 0 and x != self.dim_x - 1:
             pass
         # top left corner
         elif x == 0 and y == 0:
             pass
         # top right corner
         elif x == self.dim_x - 1 and y == 0:
             pass
         # bottom left corner
         elif x == 0 and y == self.dim_y - 1:
             pass
         # bottom right corner
         elif x == self.dim_x - 1 and y == self.dim_y - 1:
             pass
         # somewhere in middle; not an edge
         else:
             pass

在确定位置后我有一些功能做某事

dim_x和dim_y是列表的维度。

如果没有那么多if-else语句,有没有更好的方法呢?有效的东西会很好,因为这部分逻辑被称为几百万次...它是模拟退火。

提前致谢。另外,什么是更好的措辞标题呢?

6 个答案:

答案 0 :(得分:7)

def location(x,y,dim_x,dim_y):
    index = 1*(y==0) + 2*(y==dim_y-1) + 3*(x==0) + 6*(x==dim_x-1)
    return ["interior","top","bottom","left","top-left",
            "bottom-left","right","top-right","bottom-right"][index]

答案 1 :(得分:3)

# initially:
method_list = [
    bottom_left, bottom, bottom_right,
    left, middle, right,
    top_left, top, top_right,
    ]

# each time:
keyx = 0 if not x else (2 if x == self.dim_x - 1 else 1)
keyy = 0 if not y else (2 if y == self.dim_y - 1 else 1)
key = keyy * 3 + keyx
method_list[key](self, x, y, other_args)

未经测试......但总的想法应该透过。

在目标职位大幅度重新定位之后

更新“有效的东西会很好,因为这部分逻辑被称为几百万次......这是模拟退火”:

最初你不喜欢测试链,并说你正在调用一个函数来处理8个案例中的每一个。如果你想快速(在Python中):保留测试链,并且内联处理每个案例而不是调用函数。

你能用psyco吗?另外,请考虑使用Cython。

答案 2 :(得分:1)

如果我理解正确,你有一个生成在网格中的坐标(x,y)的集合,并且你想知道,给定任何坐标,无论它是在网格内还是在边缘上。

我将采取的方法是在进行比较之前对网格进行规范化,使其原点为(0,0),右上角为(1,1),然后我只需要知道用于确定其位置的坐标。让我解释一下。

0)设_max表示最大值和_min,例如,x_min是坐标x的最小值; let _new代表标准化值。

1) Given (x,y), compute: x_new = (x_max-x)/(x_max-x_min) and y_new=(y_max-y)/(y_max-y_min).

2) [this is pseudo code]
switch y_new:
  case y_new==0: pos_y='bottom'
  case y_new==1: pos_y='top'
  otherwise: pos_y='%2.2f \% on y', 100*y_new
switch x_new:
  case x_new==0: pos_x='left'
  case x_new==1: pos_x='right'
  otherwise: pos_x='%2.2f \% on x', 100*x_new

print pos_y, pos_x

It would print stuff like "bottom left" or "top right" or "32.58% on y 15.43% on x"

Hope that helps.

答案 3 :(得分:0)

我想如果你真的想以完全不同的方式对待所有这些案例,你的解决方案是可以的,因为它非常明确。紧凑的解决方案可能看起来更优雅,但可能更难维护。这实际上取决于if-blocks中发生的事情。

只要对角落进行共同处理,人们可能更愿意用一个聪明的if语句来抓住这些案例。

答案 4 :(得分:0)

这样的东西可能更具可读性/可维护性。它可能比嵌套的if语句快得多,因为它只测试每个条件一次并通过一个很好而快速的字典发送。

class LocationThing:

    def __init__(self, x, y):
        self.dim_x = x
        self.dim_y = y

    def interior(self):
        print "interior"
    def left(self):
        print "left"
    def right(self):
        print "right"
    def top(self):
        print "top"
    def bottom(self):
        print "bottom"
    def top_left(self):
        print "top_left"
    def top_right(self):
        print "top_right"
    def bottom_left(self):
        print "bottom_left"
    def bottom_right(self):
        print "bottom_right"

    location_map = {
        # (left, right,   top, bottom)
        ( False, False, False, False ) : interior,
        (  True, False, False, False ) : left,
        ( False,  True, False, False ) : right,
        ( False, False,  True, False ) : top,
        ( False, False, False,  True ) : bottom,
        (  True, False,  True, False ) : top_left,
        ( False,  True,  True, False ) : top_right,
        (  True, False, False,  True ) : bottom_left,
        ( False,  True, False,  True ) : bottom_right,
        }


    def location(self, x,y):
        method = self.location_map[(x==0, x==self.dim_x-1, y==0, y==self.dim_y-1)]
        return method(self)

l = LocationThing(10,10)
l.location(0,0)
l.location(0,1)
l.location(1,1)
l.location(9,9)
l.location(9,1)
l.location(1,9)
l.location(0,9)
l.location(9,0)

当您运行上述内容时,它会打印

top_left
left
interior
bottom_right
right
bottom
bottom_left
top_right

答案 5 :(得分:0)

对于一个快速的内循环函数,你可以咬住子弹并执行丑陋的:使用重复的术语嵌套if else语句,这样每次比较只进行一次,并且运行速度快两倍< / strong>作为一个更清洁的答案(通过mobrule):

import timeit

def f0(x, y, x_dim, y_dim):
    if x!=0:
        if x!=x_dim: # in the x interior
            if y!=0:
                if y!=y_dim: # y interior
                    return "interior"
                else: # y==y_dim edge 'top'
                    return "interior-top"
            else:
                return "interior-bottom"
        else: # x = x_dim, "right"
            if y!=0:
                if y!=y_dim: # 
                    return "right-interior"
                else: # y==y_dim edge 'top'
                    return "right-top"
            else:
                return "right-bottom"
    else: # x=0 'left'
        if y!=0:
            if y!=y_dim: # y interior
                return "left-interior"
            else: # y==y_dim edge 'top'
                return "left-top"
        else:
            return "left-bottom"

r_list = ["interior","top","bottom","left","top-left",
            "bottom-left","right","top-right","bottom-right"]                 
def f1(x,y,dim_x,dim_y):
    index = 1*(y==0) + 2*(y==dim_y-1) + 3*(x==0) + 6*(x==dim_x-1)
    return r_list[index]

for x, y, x_dim, y_dim in [(4, 4, 5, 6), (0, 0, 5, 6)]:
    t = timeit.Timer("f0(x, y, x_dim, y_dim)", "from __main__ import f0, f1, x, y, x_dim, y_dim, r_list")
    print "f0", t.timeit(number=1000000)
    t = timeit.Timer("f1(x, y, x_dim, y_dim)", "from __main__ import f0, f1, x, y, x_dim, y_dim, r_list")
    print "f1", t.timeit(number=1000000)

给出了:

f0 0.729887008667  # nested if-else for interior point (no "else"s)
f1 1.4765329361
f0 0.622623920441  # nested if-else for left-bottom (all "else"s)
f1 1.49259114265

所以它比mobrule的答案要快两倍,这是我发布的最快的代码。 (此外,我将mobrule的字符串列表移出功能,因为结果加快了50%。)速度超过美丽?

如果您想要一个简洁易读的解决方案,我建议:

def f1(x, y, x_dim, y_dim):
    d_x = {0:"left", x_dim:"right"}
    d_y = {0:"bottom", y_dim:"top"}
    return d_x.get(x, "interior")+"-"+d_y.get(y, "interior")

和我的时间一样快。