Матрица — это прямоугольная таблица чисел, записанная в строки и столбцы; число на пересечении строки i и столбца j обозначают a_ij. Размер матрицы задают как m x n, где m — количество строк, n — количество столбцов. Ниже разберем виды матриц, шесть базовых операций (сложение, умножение на число, умножение матриц, транспонирование, определитель, обратную матрицу) и покажем каждую кодом на Python с библиотекой numpy и фактическим выводом.
Содержание
Важно сразу развести два близких понятия. Матрица — это сама таблица чисел (объект). Определитель (детерминант) — это одно число, которое считается только для квадратной матрицы и характеризует ее. То есть определитель не «часть» матрицы, а результат операции над ней.
Из чего состоит матрица
Элемент a_ij стоит на пересечении i-й строки и j-го столбца. Нумерация в математике идет с 1, а в Python (numpy) — с 0, это частый источник путаницы при переносе формул в код.
Диагональ, идущая из левого верхнего угла в правый нижний, называется главной. Диагональ из правого верхнего в левый нижний — побочной. Оба понятия нужны для определителя матриц 2×2 и 3×3.
Запись размера читают в порядке «строки на столбцы»: матрица 2 x 3 — это две строки и три столбца, а не наоборот. От размера зависит, какие операции применимы: сложение требует полного совпадения размеров, а умножение — согласования по внутренней размерности. Поэтому перед любой операцией полезно проверить .shape.
Виды матриц
Классификация помогает понять, какие операции вообще применимы: например, определитель и обратная матрица определены только для квадратных.
| Вид | Условие | Пример |
|---|---|---|
| Прямоугольная | m != n | 2 x 3 |
| Квадратная | m == n | 3 x 3 |
| Вектор-строка / вектор-столбец | одна строка / один столбец | 1 x n / m x 1 |
| Нулевая | все элементы = 0 | любой размер |
| Диагональная | вне главной диагонали нули | квадратная |
| Единичная (E) | на главной диагонали 1, остальное 0 | квадратная |
| Симметричная | a_ij == a_ji | квадратная |
Единичная матрица E играет для умножения матриц ту же роль, что число 1 для обычного умножения: A x E = A. Через нее определяют обратную матрицу.
Сложение и вычитание
Складывать и вычитать можно только матрицы одинакового размера: операция идет поэлементно, результат — матрица того же размера.
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(A + B)
# [[ 6 8]
# [10 12]]
print(B - A)
# [[4 4]
# [4 4]]
Если размеры не совпадают, честнее получить ошибку, чем молчаливый неверный результат. Учтите: у numpy есть broadcasting, и он может «растянуть» несовпадающие формы вместо ошибки — для строгого поэлементного сложения проверяйте, что A.shape == B.shape.
Умножение на число
Каждый элемент умножается на скаляр. Размер не меняется.
print(3 * A)
# [[ 3 6]
# [ 9 12]]
Умножение матриц
Здесь скрыта самая частая ошибка новичков в numpy. Оператор * — это поэлементное умножение (произведение Адамара), а не матричное. Матричное умножение — это оператор @ (или np.dot). Это разные операции с разным результатом:
print(A * B) # поэлементно
# [[ 5 12]
# [21 32]]
print(A @ B) # матричное умножение
# [[19 22]
# [43 50]]
Матричное умножение согласовано по размерам не всегда: A x B определено, только если число столбцов A равно числу строк B. При этом (m x k) @ (k x n) дает матрицу (m x n). Элемент результата в позиции (i, j) — сумма произведений i-й строки первой матрицы на j-й столбец второй.
Разберем элемент результата вручную на A @ B. Верхний левый элемент — это первая строка A, умноженная на первый столбец B: 15 + 27 = 5 + 14 = 19. Именно 19 стоит в позиции (0, 0) в выводе выше. Так же считается каждый из оставшихся элементов.
C = np.array([[1, 2, 3], [4, 5, 6]]) # 2 x 3
D = np.array([[7, 8], [9, 10], [11, 12]]) # 3 x 2
print(C @ D)
# [[ 58 64]
# [139 154]]
print(C.shape, D.shape, (C @ D).shape)
# (2, 3) (3, 2) (2, 2)
Отсюда следствие: в общем случае A @ B не равно B @ A — матричное умножение некоммутативно (переставлять множители нельзя).
Транспонирование
При транспонировании строки и столбцы меняются местами: элемент a_ij переходит в позицию a_ji, размер m x n становится n x m. В numpy это атрибут .T.
print(C.T)
# [[1 4]
# [2 5]
# [3 6]]
print(C.T.shape)
# (3, 2)
Определитель (детерминант)
Определитель — это число, определенное только для квадратной матрицы. Для матрицы 2×2 он равен разности произведений главной и побочной диагоналей: det = a11 * a22 — a12 * a21.
Для матрицы 3×3 работает правило треугольников (правило Саррюса): к сумме трех произведений вдоль главной диагонали и параллельных ей троек добавляют со знаком минус три произведения вдоль побочной диагонали. Для матриц 4×4 и крупнее формулы вручную громоздки, на практике их считают программно (через разложение, а не перебором).
A = np.array([[1, 2], [3, 4]])
print(np.linalg.det(A))
# -2.0000000000000004
Ожидаемое точное значение — ровно -2 (14 — 23). Дробный «хвост» -2.0000000000000004 — это не ошибка расчета, а погрешность чисел с плавающей точкой: np.linalg.det считает определитель не по школьной формуле, а через LU-разложение в float. Если нужно целое точное значение, результат стоит округлить: round(np.linalg.det(A)) даст -2. Для матрицы 3×3 в этом примере значение получается точным:
M = np.array([[2, 1, 1], [1, 3, 2], [1, 0, 0]])
print(np.linalg.det(M))
# -1.0
Определитель отвечает на ключевой вопрос: обратима ли матрица. Если det = 0, матрица вырожденная и обратной не имеет.
Обратная матрица
Обратная матрица A^-1 — это такая матрица, что A @ A^-1 = A^-1 @ A = E (единичная). Она существует только для квадратной невырожденной матрицы, то есть когда det(A) не равен нулю (вырожденная — когда det = 0).
Порядок нахождения вручную: посчитать определитель и убедиться, что он не ноль; найти матрицу алгебраических дополнений; транспонировать ее; разделить каждый элемент на определитель. В коде это одна функция:
A = np.array([[1, 2], [3, 4]])
A_inv = np.linalg.inv(A)
print(A_inv)
# [[-2. 1. ]
# [ 1.5 -0.5]]
print(A @ A_inv)
# [[1.0000000e+00 0.0000000e+00]
# [8.8817842e-16 1.0000000e+00]]
Проверка A @ A^-1 дает почти единичную матрицу: вне диагонали стоит 8.88e-16 вместо ровного нуля — снова эффект плавающей точки, а не ошибка. Для сравнения матриц с такой погрешностью используйте np.allclose(A @ A_inv, np.eye(2)), а не строгое равенство.
Если попробовать обратить вырожденную матрицу, numpy честно сообщит о проблеме — у нее нет обратной:
S = np.array([[1, 2], [2, 4]])
print(np.linalg.det(S))
# 0.0
# np.linalg.inv(S) -> LinAlgError: Singular matrix
Выводы
- Матрица — таблица чисел размера m x n; определитель и обратная матрица существуют только для квадратных.
- Сложение и вычитание идут поэлементно и требуют одинакового размера; умножение на число тоже поэлементно.
- В numpy
*— это поэлементное умножение, а матричное —@; для@число столбцов первой матрицы должно равняться числу строк второй, и порядок множителей менять нельзя. - Определитель — одно число: если он равен нулю, матрица вырожденная и обратной не имеет.
- Результаты
np.linalg.detиinvсодержат погрешность плавающей точки; сравнивать черезnp.allclose, при необходимости округлять.
Где применяется / связь с практикой
Матрицы — рабочий инструмент в IT, а не только школьная тема. В компьютерной графике и игровых движках повороты, масштаб и сдвиг объектов задаются умножением координат на матрицу преобразования. Ниже — поворот точки (1, 0) на 90 градусов:
import numpy as np, math
theta = math.pi / 2
R = np.array([[math.cos(theta), -math.sin(theta)],
[math.sin(theta), math.cos(theta)]])
print(np.round(R @ np.array([1, 0]), 8))
# [0. 1.]
В машинном обучении и анализе данных выборка хранится как матрица «объекты x признаки», а обучение линейных моделей и нейросетей — это операции над матрицами. Решение систем линейных уравнений (например, 2x + y = 5 и x + 3y = 10) тоже сводится к матрицам:
K = np.array([[2, 1], [1, 3]])
b = np.array([5, 10])
print(np.linalg.solve(K, b))
# [1. 3.] -> x = 1, y = 3
Освойте тему на практике
Матричная запись встречается в графике, компьютерном зрении, ML, обработке сигналов и экономических расчетах. Разобраться с линейной алгеброй как основой этих задач помогает курс Прикладная математика: там теория идет вместе с практикой на Python. Посмотреть формат и уровень занятий без оплаты можно на открытых уроках.
FAQ
Чем отличается * от @ в numpy?
* перемножает матрицы поэлементно (нужны одинаковые размеры), @ выполняет матричное умножение (нужно согласование: столбцы первой = строки второй). Результаты разные, это частый источник ошибок.
Почему np.linalg.det возвращает не целое число, а вроде -2.0000000000000004?
Функция считает определитель через LU-разложение в числах с плавающей точкой, поэтому возможна крошечная погрешность. Для целочисленного ответа округлите результат через round().
Как понять, что у матрицы нет обратной?
Посчитайте определитель: если он равен нулю, матрица вырожденная и обратной не имеет. Попытка вызвать np.linalg.inv для нее вызовет ошибку LinAlgError: Singular matrix.



