Определить соседние ячейки в двумерной сетке с условиями периода i c - PullRequest
2 голосов
/ 06 мая 2020

Предположим, у нас есть следующая двумерная сеть, индексы ячеек которой мы помечаем целыми числами:

20  21  22  23  24
15  16  17  18  19
10  11  12  13  14
5   6   7   8   9
0   1   2   3   4

Мне нужна функция, которая получает в качестве входных данных индекс ячейки (ячейку) и количество ячеек вдоль оси (в данном случае n = 5), и возвращает массив с его 9 соседями (включая саму ячейку) с учетом периодичности глобального блока.

Я показываю вам, что я ' Я пробовал, что «почти» работает:

def celdas_vecinas(cell,n):

    return np.mod(cell + np.array([0, -n-1, -n, -n+1, -1, 1, n-1, n, n+1], dtype=np.int64), n**2)

Где я ввел np.mod, чтобы отразить условия периода c. Дело в том, что эта функция хорошо себя ведет только для некоторых значений.

>>> celdas_vecinas(1,5) 
array([ 1, 20, 21, 22,  0,  2,  5,  6,  7]) # right!

>>> celdas_vecinas(21,5)
array([21, 15, 16, 17, 20, 22,  0,  1,  2]) # right!

Но если я ввожу индекс одной из ячеек по углам, происходит следующее:

>>> celdas_vecinas(0,5)
array([ 0, 19, 20, 21, 24,  1,  4,  5,  6]) # should be 9 instead of 19

Также не работает, например, для ячейки = 5.

Кто-нибудь знает, как я могу реализовать эту функцию? Когда индекс ячейки не касается какой-либо границы, это очень легко реализовать, но я не знаю, как включить эффекты periodi c, хотя я предполагаю, что это должно быть связано с функцией np.mod

Ответы [ 4 ]

1 голос
/ 07 мая 2020

Периодичность строк не работает как периодичность столбцов. Я думаю, вы должны сначала получить по 2 ячейки с каждой стороны, а затем двигаться вверх и вниз. Я пробовал это, и, похоже, это сработало:

def celdas_vecinas(cell, n) :
    last_row = n * (cell // n)
    left_cell = last_row + ( cell - last_row - 1 ) % n
    right_cell = last_row + ( cell - last_row + 1 ) % n
    line = np.array( [ left_cell, cell, right_cell ] )
    return np.mod( [ line + n, line, line - n ], n**2)

(я удалил свой предыдущий ответ, поскольку я напортачил)

1 голос
/ 06 мая 2020

Numpy реализация может использовать numpy .argwhere для извлечения индексов значений, создать сетку индексов с numpy .ix_ , и, наконец, примените метод numpy .narray.ravel для сглаживания массива ::

import numpy as np

n = 5
grid = np.arange(n**2).reshape(n,n)[::-1]

def celdas_vecinas_np(grid, v, n):

    x, y = np.argwhere(grid == v)[0]
    idx = np.arange(x-1, x+2) %n
    idy = np.arange(y-1, y+2) %n

    return grid[np.ix_(idx, idy)].ravel()

celdas_vecinas_np(grid, 24, n)
array([ 3,  4,  0, 23, 24, 20, 18, 19, 15])

С другой стороны, для реализации Numba мы не можем использовать numpy.argwhere, но мы можем использовать numpy. Где для получения индексов. Как только мы это сделаем, останется только зацикливаться в правильных диапазонах, а именно:

from numba import njit

@njit
def celdas_vecinas_numba(grid, v, n):

    x, y = np.where(grid == v)
    x, y = x[0], y[0]

    result = []
    for ix in range(x-1, x+2):
        for iy in range(y-1, y+2):
            result.append(grid[ix%n, iy%n])

    return result

celdas_vecinas_numba(grid, 24, n)
[3, 4, 0, 23, 24, 20, 18, 19, 15]

Сравнение производительности с такой маленькой сеткой numba уже работает примерно в 20 раз быстрее на моей локальной машине :

%timeit celdas_vecinas_np(grid, 24, 5)
38 µs ± 1.62 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit celdas_vecinas_numba(grid, 24, n)
1.81 µs ± 93.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
0 голосов
/ 08 мая 2020

На основании ответа Comevussor я получил этот код:

@nb.njit(nb.i8[:](nb.i8, nb.i8), fastmath=True)
def celdas_vecinas(cell,n):

    Nt = n**2 # total number of cells

    x = cell % n; y = cell // n # x,y cell coordinates
    izq =      (x - 1) % n + y * n
    der =      (x + 1) % n + y * n
    arri =     (x % n + (y+1) * n) % Nt
    aba =      (x % n + (y-1) * n) % Nt
    aba_izq =  (izq - n) % Nt
    aba_der =  (der - n) % Nt
    arri_izq = (izq + n) % Nt
    arri_der = (der + n) % Nt

    return np.array([cell, aba_izq, aba, aba_der, izq, der, arri_izq, arri, arri_der])

, который работает со следующей производительностью:

>>> %timeit celdas_vecinas(0,5)
567 ns ± 13.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
0 голосов
/ 07 мая 2020

Попробуйте вот так:

grid = [[20,  21,  22,  23,  24],[15,  16,  17,  18,  19],[10,  11,  12,  13,  14],[5,   6,   7,   8,   9],[0,   1,   2,   3,   4]]
def celdas_vecinas(cell,n):
    Row = [-1, -1, -1, 0, 0, 0, 1, 1, 1]
    Col = [-1, 0, 1, -1, 0, 1, -1, 0, 1]
    x = y = 0
    for i in range(n):
        z = 0;
        for j in range(n):
            if grid[i][j] == cell:
                x = i
                y = j
                z = 1
                break
        if z:
            break

    ans = []
    for i in range(9):
        xx = (n + x + Row[i]) % n
        yy = (n + y + Col[i]) % n
        ans.append(grid[xx][yy])
    return ans;
print(celdas_vecinas(1,5))
print(celdas_vecinas(21,5))
print(celdas_vecinas(5,5))
...