Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Эффективный метод определения местоположения в сетке (массиве)

Автор вопроса: NopeОтветов: 5

Я представляю сетку с 2D-списком в 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 26.09.2009
  • @whatnick, так лучше? Nope 26.09.2009
  • форматирование в порядке, но вы имеете в виду левый столбец и правый столбец, а не левую строку и правую строку. John Machin 26.09.2009
  • И ваш верхний левый угол - это x == 0 и y == 0 ... это было бы нижним левым в соглашении большинства людей. Даже антиподы так не делают :-) John Machin 26.09.2009
  • @John Machin: координаты верхнего левого пикселя на вашем экране x=0, y=0. Внизу слева x=0, y=screen_height. jfs 26.09.2009
  • у = высота_экрана?? Это будет либо проигнорировано, либо обработано по модулю screen_height, т.е. ноль :-) Возможно, вы имели в виду (screen_height - 1). В ОП не упоминались ни экран, ни пиксели. Обычное соглашение с графиками на бумаге или на экране состоит в том, что (0, 0) - это нижний левый угол. John Machin 26.09.2009

pythonlistarraysperformance

Ответы

Ответ 1 · рейтинг 3 · John Machin

# 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): сохраните цепочку тестов и выполняйте обработку каждого случая встроенно вместо вызова функции.

Можно ли использовать психо? Также рассмотрите возможность использования Cython.

Ответ 2 · рейтинг 1 · Escualo

Если я правильно понимаю, у вас есть набор координат (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 · Johannes Charra

Я думаю, если вы действительно хотите рассматривать все эти случаи совершенно по-другому, ваше решение в порядке, поскольку оно очень явное. Компактное решение может выглядеть более элегантно, но, вероятно, его будет сложнее поддерживать. Это действительно зависит от того, что происходит внутри блоков if.

Как только появится обычная обработка, скажем, углов, можно было бы предпочесть ловить эти случаи с помощью одного умного оператора if.

Ответ 4 · Nick Craig-Wood

Что-то вроде этого может быть более читабельным/обслуживаемым. Это, вероятно, будет намного быстрее, чем ваши вложенные операторы 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 · tom10

Для быстрой функции внутреннего цикла вы можете просто стиснуть зубы и сделать уродливое: вложить операторы if else с повторяющимися условиями, чтобы каждое сравнение выполнялось только один раз и выполнялось примерно в два раза быстрее. в качестве примера более чистый ответ (по мобруле):

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")

который так же быстр, как и другие по моему времени.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы