Я пытаюсь найти минимальное количество переходов для перехода от значения одной ячейки w к значению другой ячейки v в следующей матрице X с помощью Python.

Есть ли эффективный способ сделать это?

manhattan distance for matrix

import numpy as np
from numpy.typing import NDArray 


def manhattan_distance(X: NDArray[int], w: int, v: int) -> int:
     # something
     return distance
    

X = np.array([
    [1, 2, 2, 3, 3],
    [1, 2, 2, 4, 4],
    [5, 5, 6, 6, 6],
    [7, 8, 9, 9, 9],
    [10, 10, 10, 10, 10],
]).astype(np.int_)


# Expected result
manhattan_distance(X, 1, 2) # ①
-> 1
manhattan_distance(X, 2, 3) # ②
-> 1
manhattan_distance(X, 3, 6) # ③
-> 2
manhattan_distance(X, 3, 5) # ④
-> 4

Я пытался реализовать это следующим образом, но, похоже, это довольно медленно.

import numpy as np
from numpy.typing import NDArray 

def manhattan_distance(X: NDArray[int], w: int, v: int) -> int:
    xx, yy = np.where(X == w)
    xx_, yy_ = np.where(X == v)
    distance = int(min_dist(xx, xx_) + min_dist(yy, yy_))
    return distance
  
def min_dist(xx, xx_):
    min_dist = np.inf
    for i in xx:
        for j in xx_:
            dist = np.sqrt((i - j)**2)
            min_dist = dist if dist < min_dist else min_dist
    return min_dist

Есть ли способ ускорить этот расчет?

0
ugen 10 Окт 2021 в 13:12

2 ответа

Лучший ответ

Используйте cdist чтобы вычислить все попарные расстояния, чтобы найти индексы значений, используйте np.argwhere

from scipy.spatial.distance import cdist
import numpy as np
from numpy.typing import NDArray

X = np.array([
    [1, 2, 2, 3, 3],
    [1, 2, 2, 4, 4],
    [5, 5, 6, 6, 6],
    [7, 8, 9, 9, 9],
    [10, 10, 10, 10, 10],
]).astype(np.int32)


def manhattan_distance(X: NDArray[int], w: int, v: int) -> int:
    return cdist(np.argwhere(X == w), np.argwhere(X == v), "cityblock").min()


print(manhattan_distance(X, 1, 2))
print(manhattan_distance(X, 2, 3))
print(manhattan_distance(X, 3, 6))
print(manhattan_distance(X, 3, 5))

Вывод

1.0
1.0
2.0
4.0
2
Dani Mesejo 10 Окт 2021 в 10:33

Просто добавьте тайминги для разных размеров матриц, чтобы показать OP, что ответ @Dani Mesejo действительно намного быстрее. Для маленьких матриц различия, конечно, будут небольшими.

def manhattan_distance_dani_masejo(X, w: int, v: int) -> int:
    return cdist(np.argwhere(X == w), np.argwhere(X == v), "cityblock").min()


def manhattan_distance_ugen(X, w: int, v: int) -> int:
    xx, yy = np.where(X == w)
    xx_, yy_ = np.where(X == v)
    distance = int(min_dist(xx, xx_) + min_dist(yy, yy_))
    return distance

def min_dist(xx, xx_):
    min_dist = np.inf
    for i in xx:
        for j in xx_:
            dist = np.sqrt((i - j)**2)
            min_dist = dist if dist < min_dist else min_dist
    return min_dist


def gen_data(matrix_size):
    return np.random.randint(0, matrix_size, (matrix_size, matrix_size), dtype=np.int32)

for matrix_size in [5, 50, 500]:
    print('Matrix size: {}'.format(matrix_size))
    X = gen_data(matrix_size)
    print('ugen: ', timeit.timeit("manhattan_distance_ugen(X, np.random.randint(0, matrix_size), np.random.randint(0, matrix_size))",
                        "from __main__ import manhattan_distance_ugen, X, matrix_size; import numpy as np",
                        number=10))
    print('dani masejo: ', timeit.timeit("manhattan_distance_dani_masejo(X, np.random.randint(0, matrix_size), np.random.randint(0, matrix_size))",
                        "from __main__ import manhattan_distance_dani_masejo, X, matrix_size; import numpy as np",
                        number=10))

Результат:

Matrix size: 5
ugen:  0.0019634239999999914
dani masejo:  0.0006071939999999776
Matrix size: 50
ugen:  0.093326557
dani masejo:  0.0008874660000000034
Matrix size: 500
ugen:  9.112327058
dani masejo:  0.027754558000001595
2
dankal444 10 Окт 2021 в 10:48