Заполнить диагональ и антидиагонал со смещением строки, индексы столбца в массиве - NumPy / Python - PullRequest
2 голосов
/ 07 июня 2019

У меня есть матрица n на n.Учитывая позицию, я хотел бы заменить диагонали этой позиции на конкретное значение.

Я пытался использовать np.fill_diagonal, но не получил правильный результат.

import numpy as np

n = 8
available = np.array([["O" for _ in range(n)] for _ in range(n)])

def update_available(column, row):
    available[column][row] = "X"
    np.fill_diagonal(available[column-row:],"X")
    np.fill_diagonal(available[::-1,:][column-row-1:],"X")

Для update_available(4,1) я хочу получить следующий вывод:

   0  1  2  3  4  5  6  7
0  O  O  O  O  O  X  O  O
1  O  O  O  O  X  O  O  O
2  O  O  O  X  O  O  O  O
3  X  O  X  O  O  O  O  O
4  O  X  O  O  O  O  O  O
5  X  O  X  O  O  O  O  O
6  O  O  O  X  O  O  O  O
7  O  O  O  O  X  O  O  O

1 Ответ

1 голос
/ 07 июня 2019

Подход № 1: Трансляция-маскировка

Вот один с masking -

def fill_cross(a, row, col, newval):
    n = len(a)
    r = np.arange(n)
    m1 = r[::-1,None] == r + n-row-col-1
    m2 = r[:,None] == r+row-col
    a[m1|m2] = newval
    return a

Подход № 2: NumPy-Eye-Masking

Поместив те же самые смещения в np.eye, мы могли бы сделать его немного более компактным , вот так -

def fill_cross_v2(a, row, col, newval):
    n = len(a)
    m1 = np.eye(n,k=-row+col,dtype=bool)
    m2 = np.eye(n,k=n-col-row-1,dtype=bool)[:,::-1]
    a[m1|m2] = newval
    return a

Подход № 3: Уплощенное присвоение

Мы также можем использовать slicing. Идея состоит в том, чтобы выяснить начальные сглаженные индексы для диагональных и антидиагональных единиц и нарезать массив на сглаженные n+1 (n = длина массива) шагов и назначить новые значения. Это должно быть намного эффективнее, особенно на больших массивах. Реализация будет выглядеть примерно так -

def fill_cross_v3(a, row, col, newval):
    if row+col>=n:
        anti_diag_start = (row+col-n+1,n-1)
    else:
        anti_diag_start = (0,row+col)

    if row>col:
        diag_start = (row-col,0)
    else:
        diag_start = (0,col-row)

    r,c = [np.ravel_multi_index(i,a.shape) for i in [diag_start,anti_diag_start]]
    a.ravel()[r:r+(n-diag_start[0]-diag_start[1])*(n+1):n+1] = newval
    a.ravel()[c:c*(n+1):n-1] = newval
    return a

Пробный прогон -

In [71]: a
Out[71]: 
array([[0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0]])

In [72]: fill_cross(a, row=4, col=1, newval=-1)
Out[72]: 
array([[ 0,  0,  0,  0,  0, -1,  0,  0],
       [ 0,  0,  0,  0, -1,  0,  0,  0],
       [ 0,  0,  0, -1,  0,  0,  0,  0],
       [-1,  0, -1,  0,  0,  0,  0,  0],
       [ 0, -1,  0,  0,  0,  0,  0,  0],
       [-1,  0, -1,  0,  0,  0,  0,  0],
       [ 0,  0,  0, -1,  0,  0,  0,  0],
       [ 0,  0,  0,  0, -1,  0,  0,  0]])

Синхронизация на больших массивах -

In [509]: a = np.zeros((1000,1000))

In [510]: %timeit fill_cross(a, row=200, col=700, newval=-1)
     ...: %timeit fill_cross_v2(a, row=200, col=700, newval=-1)
     ...: %timeit fill_cross_v3(a, row=200, col=700, newval=-1)
1.64 ms ± 15.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
632 µs ± 787 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
438 µs ± 12.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [511]: a = np.zeros((5000,5000))

In [512]: %timeit fill_cross(a, row=2200, col=2700, newval=-1)
     ...: %timeit fill_cross_v2(a, row=2200, col=2700, newval=-1)
     ...: %timeit fill_cross_v3(a, row=2200, col=2700, newval=-1)
66.6 ms ± 680 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
39.1 ms ± 245 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
17.3 ms ± 50.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
...