Numpy борется, потому что он работает с целыми числами фиксированного размера, такими как int32
или int64
(какое из них используется здесь, зависит от вашей установки python). Такое экспонирование матрицы быстро делает записи больше предела этого типа данных, что приводит к усечению.
С обычными Python целыми числами можно использовать этот вид реализации (сначала умножьте матрицу и применять модульное сокращение впоследствии), поскольку обычные целые числа Python могут иметь гораздо более высокие значения. Например:
def mm(A, B):
return [[sum([x*y for (x, y) in zip(row, col)]) for col in zip(*B)] for row in A]
def knightDialer(N: int) -> int:
M = [[0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[0, 0, 0, 0, 1, 0, 0, 0, 1, 0],
[1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
[0, 1, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 1, 0, 0, 0, 0, 0]]
N = N - 1
res = [[int(i == j) for j in range(len(M))] for i in range(len(M))]
while N:
if N & 1: res = mm(res, M)
M = mm(M, M)
N >>= 1
print(M)
return sum([sum(i) for i in zip(*res)]) % (10**9 + 7)
Применение модульного сокращения во время возведения в степень позволяет numpy матрицам работать без битов:
def knightDialer(N: int) -> int:
M = np.matrix([[0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[0, 0, 0, 0, 1, 0, 0, 0, 1, 0],
[1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
[0, 1, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 1, 0, 0, 0, 0, 0]], dtype=np.int64)
N = N - 1
res = np.eye(M.shape[0], dtype=np.int64)
while N:
if N & 1: res = res * M % (10**9 + 7)
M = M * M % (10**9 + 7)
N >>= 1
return np.sum(res) % (10**9 + 7)
Использование dtype=np.int64
необходимо в случае, если ваш установка целочисленный тип по умолчанию int32
. int32
достаточно велик, чтобы содержать числа ниже 10**9 + 7
, но во время умножения матриц между двумя такими числами (а затем они также суммируются) будут произведения, и в это время может произойти переполнение, если использовалось int32
.