Настройка:
import numpy as np
from functools import reduce
N = 6
A = np.random.randint(0,10,(N,N))
i,j = np.random.randint(0,N,2)
A
# array([[8, 9, 8, 1, 9, 4],
# [0, 3, 5, 4, 5, 2],
# [2, 7, 4, 6, 2, 7],
# [9, 8, 1, 5, 1, 9],
# [0, 1, 0, 8, 0, 3],
# [3, 4, 5, 0, 6, 7]])
i,j
# (5, 1)
Помощники:
I = np.identity(N)
e = I[:,None] # standard base row vectors
eT = I[...,None] # standard base column vectors
Операция строки:
rowops = [I + eT[i]@e[i]*(1/A[i,j]-1)] + [I - (eT[k]*A[k,j])@(e[i]/A[i,j]) for k in range(6) if k!=i]
Первый масштабирует i-й ряд. Другие вычитают i-ю строку, умноженную на множитель, из k-й строки.
Давайте применим их один за другим и отследим j-й столбец:
B = A.copy()
for r in reversed(rowops):
print(B[:,j])
B = r@B
# [9 3 7 8 1 4]
# [9. 3. 7. 8. 0. 4.]
# [9. 3. 7. 0. 0. 4.]
# [9. 3. 0. 0. 0. 4.]
# [9. 0. 0. 0. 0. 4.]
# [0. 0. 0. 0. 0. 4.]
print(B[:,j])
# [0. 0. 0. 0. 0. 1.]
Теперь давайте объединим все строки водиночная операция, умножая их:
combined = reduce(np.matmul,rowops)
Это дает действительно тот же результат, когда применяется к A
np.allclose(combined@A,B)
# True
Этот комбинированный оператор мы могли бы получить непосредственно:
R = I + (eT[i]-A@eT[j])@e[i]/A[i,j]
Проверка:
np.allclose(combined,R)
# True
Применительно к A:
R@A
# array([[ 1.25, 0. , -3.25, 1. , -4.5 , -11.75],
# [ -2.25, 0. , 1.25, 4. , 0.5 , -3.25],
# [ -3.25, 0. , -4.75, 6. , -8.5 , -5.25],
# [ 3. , 0. , -9. , 5. , -11. , -5. ],
# [ -0.75, 0. , -1.25, 8. , -1.5 , 1.25],
# [ 0.75, 1. , 1.25, 0. , 1.5 , 1.75]])