【问题标题】:Efficient method to determine location on a grid(array)确定网格(数组)上位置的有效方法
【发布时间】:2010-12-01 14:41:18
【问题描述】:

我在 python 中用二维列表表示一个网格。我想在列表中选择一个点 (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 语句,有没有更好的方法来做到这一点?有效率的东西会很好,因为这部分逻辑被调用了几百万次......它用于模拟退火。

提前致谢。另外,标题的措辞有什么更好的方式?

【问题讨论】:

  • 您的格式丢失,请编辑并重新发布。
  • @whatnick,好点了吗?
  • 格式没问题——但你的意思是左列和右列,而不是左行和右行。
  • 您的“左上角”是 x == 0 和 y == 0 ...在大多数人的惯例中,这将是“左下角”。甚至 Antipodeans 也不会这样做 :-)
  • @John Machin:屏幕左上角像素的坐标是x=0, y=0。左下角是x=0, y=screen_height

标签: python arrays list performance


【解决方案1】:
# 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】:

    如果我理解正确,您有一组坐标 (x,y) 位于网格中,并且您想知道给定任何坐标,它是在网格内还是在边缘上。

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

    0) 设_max代表最大值,_min,例如x_min是坐标x的最小值;让 _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】:
      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]
      

      【讨论】:

        【解决方案4】:

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

        一旦有一个共同的处理,比如说,角落,人们可能更愿意用一个聪明的 if 语句来捕捉这些情况。

        【讨论】:

          【解决方案5】:

          这样的东西可能更具可读性/可维护性。它可能会比您的嵌套 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
          

          【讨论】:

            【解决方案6】:

            对于一个快速的内循环函数,你可以咬紧牙关做丑陋的:嵌套 if else 重复项的语句,这样每次比较只进行一次,它的运行速度大约是 两倍 作为一个更简洁的答案(通过 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")
            

            按照我的时间,这和其他人一样快。

            【讨论】:

            • (1) 您没有始终如一地使用 max/dim。即使在调整了顶部/底部约定差异之后,这两个函数也不会产生相同的结果。 (2)最快的/looking/代码不一定是最快的/running/代码。尝试通过 (1 if not y else (2 if y == max_y else 0)) + (3 if not x else (6 if x == max_x else 0)) 之类的方式计算索引...更具竞争力 (3) 使用 python -mtimeit -s"initialisation code" "loop code" 而不是 DIY 变体
            • 3) 我认为我正在以可接受的方式使用 timeit - 如果不是,请发布一个链接到它说这是错误的方式,因为这是 Python 文档中的内容。我不是自己动手,只是用几个不同的点独立运行 timeit 。 2)如果你认为你的更快,很好,运行测试并证明它;如果有比我丑陋的答案更好的方法,我很高兴。 1)如果我输入错误,很好,关键是这个结构显然有效并且速度很快。
            • (3) 参见 Python 文档——命令行界面运行 3 次(默认)测试并报告 3 次中最好的。(2)我不是断言索引计算 I上面提到的比 if 测试方式更快;它几乎一样好。我的观点是它比 mobrule 的索引计算快得多。什么更好取决于 OP 真正想要什么,速度或美观。 (1) 这是一个脑筋急转弯,而不是一个错字。首先正确,其次快速。 (4) 您的 dict.get() 努力:OP 希望为 8 个案例中的每一个执行一些未指定的代码;此代码不太可能产生字符串标签。
            • 我正在按照 Python 文档运行 timeit。至于我的代码和我可能的错误,我会让它保持不变,因为我认为它回答了任何诚实想要答案的人的问题。我很高兴纠正向我指出的错误,但是“有一个错误,我不会告诉你在哪里”发布“未经测试”和不定时代码的人不值得我花时间,或者其他任何人都为此问题。
            猜你喜欢
            • 2010-12-02
            • 2021-11-05
            • 2021-09-24
            • 1970-01-01
            • 1970-01-01
            • 2017-11-22
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多