Эффективный метод определения местоположения в сетке (массиве)
Автор вопроса: 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
# 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, поскольку оно проверяет каждое условие только один раз и отправляет через словарь, который удобен и быстр.
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%.) Скорость превыше красоты?
Если вместо этого вам нужно краткое и легко читаемое решение, я предлагаю: