Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import sys
- import numpy as np
- class PermutationMatrix:
- def __init__(self, n):
- if isinstance(n, int):
- self._permutation = list(range(n))
- else:
- permutation = n
- assert sorted(permutation) == list(range(len(permutation)))
- self._permutation = permutation
- def inverse(self):
- result = [None] * len(self._permutation)
- for i, elem in enumerate(self._permutation):
- result[elem] = i
- assert all(elem is not None for elem in result)
- return PermutationMatrix(result)
- def add_right_down(self, other):
- adding = len(self._permutation)
- for elem in other._permutation:
- self._permutation.append(elem + adding)
- assert sorted(self._permutation) == list(range(len(self._permutation)))
- return self
- def asarray(self):
- n = len(self._permutation)
- result = np.zeros((n, n), dtype=int)
- result[self._permutation, np.arange(n)] = 1
- return result
- def apply_to_columns(self, matrix):
- return matrix[:, self._permutation]
- def __matmul__(self, other):
- assert len(self._permutation) == len(other._permutation)
- result = []
- for i in range(len(self._permutation)):
- result.append(self._permutation[other._permutation[i]])
- return PermutationMatrix(result)
- def calculate_power(number, power, modulo):
- if power == 0:
- return 1
- if power == 1:
- return number
- if power % 2 == 0:
- halfed_result = calculate_power(number, power // 2)
- return (halfed_result ** 2) % modulo
- else:
- decremented_result = calculate_power(number, power - 1)
- return (decremented_result * number) % modulo
- def calculate_inverse(number, modulo):
- assert 0 < number < modulo
- return calculate_power(number, modulo - 2, modulo)
- def decompose_matrix(matrix):
- size = matrix.shape[0]
- matrix_lu = matrix[:size // 2, :size // 2]
- matrix_ru = matrix[:size // 2, size // 2:]
- matrix_ld = matrix[size // 2:, :size // 2]
- matrix_rd = matrix[size // 2:, size // 2:]
- return (matrix_lu, matrix_ru, matrix_ld, matrix_rd)
- def multiply_matrices(first_matrix, second_matrix, modulo):
- size = first_matrix.shape[0]
- assert first_matrix.shape == second_matrix.shape == (size, size)
- if size == 1:
- return (first_matrix * second_matrix) % modulo
- assert size % 2 == 0
- (
- first_matrix_lu, first_matrix_ru,
- first_matrix_ld, first_matrix_rd
- ) = decompose_matrix(first_matrix)
- (
- second_matrix_lu, second_matrix_ru,
- second_matrix_ld, second_matrix_rd
- ) = decompose_matrix(second_matrix)
- aux1 = multiply_matrices(first_matrix_lu + first_matrix_rd,
- second_matrix_lu + second_matrix_rd,
- modulo)
- aux2 = multiply_matrices(first_matrix_ld + first_matrix_rd,
- second_matrix_lu,
- modulo)
- aux3 = multiply_matrices(first_matrix_lu,
- second_matrix_ru - second_matrix_rd,
- modulo)
- aux4 = multiply_matrices(first_matrix_rd,
- second_matrix_ld - second_matrix_lu,
- modulo)
- aux5 = multiply_matrices(first_matrix_lu + first_matrix_ru,
- second_matrix_rd,
- modulo)
- aux6 = multiply_matrices(first_matrix_ld - first_matrix_lu,
- second_matrix_lu + second_matrix_ru,
- modulo)
- aux7 = multiply_matrices(first_matrix_ru - first_matrix_rd,
- second_matrix_ld + second_matrix_rd,
- modulo)
- result_lu = (aux1 + aux4 - aux5 + aux7) % modulo
- result_ru = (aux3 + aux5) % modulo
- result_ld = (aux2 + aux4) % modulo
- result_rd = (aux1 - aux2 + aux3 + aux6) % modulo
- result_upper = np.concatenate((result_lu, result_ru), axis=1)
- result_down = np.concatenate((result_ld, result_rd), axis=1)
- result = np.concatenate((result_upper, result_down), axis=0)
- assert result.shape == (size, size)
- return result
- def inverse_lower_triagle_matrix(matrix, modulo):
- n = matrix.shape[0]
- assert matrix.shape == (n, n)
- if n == 1:
- return np.array([[calculate_inverse(matrix[0, 0], modulo)]], dtype=int)
- assert np.all(matrix[np.triu_indices_from(matrix, 1)] == 0)
- assert n % 2 == 0
- B = matrix[:n // 2, :n // 2]
- C = matrix[n // 2:, :n // 2]
- D = matrix[n // 2:, n // 2:]
- B_inv = inverse_lower_triagle_matrix(B, modulo)
- D_inv = inverse_lower_triagle_matrix(D, modulo)
- result = -multiply_matrices(
- multiply_matrices(D_inv, C, modulo),
- B_inv, modulo
- ) % modulo
- return np.block([
- [B_inv, np.zeros((n // 2, n // 2), dtype=int)],
- [result, D_inv],
- ])
- def multiply_by_blocks(square_matrix, rectangular_matrix, modulo):
- assert square_matrix.shape[0] == square_matrix.shape[1] == rectangular_matrix.shape[0]
- assert rectangular_matrix.shape[1] % rectangular_matrix.shape[0] == 0
- matrices = []
- for i in range(0, rectangular_matrix.shape[1], rectangular_matrix.shape[0]):
- submatrix = rectangular_matrix[:, i: i + rectangular_matrix.shape[0]]
- matrices.append(multiply_matrices(square_matrix, submatrix, modulo))
- result = np.concatenate(matrices, axis=1)
- assert result.shape == rectangular_matrix.shape
- return result
- def lup_decompose_recursive(matrix, modulo=2):
- m, n = matrix.shape
- if m == 1:
- L = np.array([[1]], dtype=int)
- non_zero_column = 0
- while matrix[0, non_zero_column] == 0:
- non_zero_column += 1
- permutation = list(range(n))
- permutation[0], permutation[non_zero_column] = non_zero_column, 0
- P = PermutationMatrix(permutation)
- U = P.apply_to_columns(matrix)
- return L, U, P
- B, C = matrix[:m // 2], matrix[m // 2:]
- L1, U1, P1 = lup_decompose_recursive(B, modulo=modulo)
- D = P1.inverse().apply_to_columns(C)
- E = U1[:, :m // 2]
- F = D[:, :m // 2]
- E_inv = inverse_lower_triagle_matrix(E.T, modulo=modulo).T
- FE_inv = multiply_matrices(F, E_inv, modulo=modulo)
- FE_invU1 = multiply_by_blocks(FE_inv, U1, modulo=modulo)
- G = (D - FE_invU1) % modulo
- assert np.all(G[:, :m // 2] == 0)
- G = G[:, m // 2:]
- L2, U2, P2 = lup_decompose_recursive(G, modulo=modulo)
- P3 = PermutationMatrix(m // 2).add_right_down(P2)
- H = P3.inverse().apply_to_columns(U1)
- L = np.block([
- [L1, np.zeros((m // 2, m // 2), dtype=int)],
- [FE_inv, L2],
- ])
- U = np.concatenate([
- H, np.concatenate([
- np.zeros((m // 2, m // 2), dtype=int), U2,
- ], axis=1)
- ], axis=0)
- P = P3 @ P1
- return L, U, P
- def lup_decompose(matrix):
- m, n = matrix.shape
- required_m = 1
- while required_m < m:
- required_m *= 2
- adding = required_m - m
- if adding > 0:
- matrix = np.block([
- [matrix, np.zeros((m, adding), dtype=int)],
- [np.zeros((adding, n), dtype=int), np.eye(adding, dtype=int)],
- ])
- L, U, P = lup_decompose_recursive(matrix)
- P = P.asarray()
- if adding > 0:
- L = L[:-adding, :-adding]
- U = U[:-adding, :-adding]
- P = P[:-adding, :-adding]
- return L, U, P
- def print_matrix(matrix):
- for line in matrix:
- print(' '.join(map(str, line)))
- if __name__ == '__main__':
- matrix = np.array([list(map(int, line.split())) for line in sys.stdin])
- L, U, P = lup_decompose(matrix)
- print_matrix(L)
- print_matrix(U)
- print_matrix(P)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement