Все курсы > Линейная алгебра > Занятие 8
Рассмотрим тему смены базиса. На втором занятии курса мы уже находили координаты вектора в новом базисе.
Ноутбук к сегодняшнему занятию⧉
Общий принцип
Столбцы матрицы преобразований указывают на положение базисных векторов после трансформации. Другими словами, столбцы матрицы — это комбинации новых базисных векторов после проведения преобразования.
Предположим, что нам дана некоторая матрица преобразований $A$.
$$ A = \begin{bmatrix} a_{11} & a_{12} & a_{13} & \dots & a_{1n} \\ a_{21} & a_{22} & a_{23} & \dots & a_{2n} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ a_{m1} & a_{m2} & a_{m3} & \dots & a_{mn} \end{bmatrix} $$
Кроме того, мы знаем старый $ \mathbf v_1, \mathbf v_2,…, \mathbf v_n $ и новый базисы $ \mathbf w_1, \mathbf w_2,…, \mathbf w_m $. Тогда преобразование $T$ можно записать как
- $T(\mathbf v_1) = a_{11} \mathbf w_1 + a_{21} \mathbf w_2 + … + a_{m1} \mathbf w_m $ (то есть первый столбец матрицы $A$)
- $T(\mathbf v_2) = a_{12} \mathbf w_1 + a_{22} \mathbf w_2 + … + a_{m2} \mathbf w_m $ (то есть второй столбец матрицы $A$)
- и т.д.
Другими словами $ A \mathbf v_n = \mathbf w_m $.
Переход в исходный базис
Зададим два вектора $ \mathbf a $ и $ \mathbf b $ в новом базисе. С точки зрения нового базиса векторы имеют координаты
$$ \mathbf a_{new} = \begin{bmatrix} 1 \\ 0 \end{bmatrix}, \mathbf b_{new} = \begin{bmatrix} 0 \\ 1 \end{bmatrix}$$
При этом, пусть в старой (исходной) системе координат новый базис имеет координаты
$$ \mathbf a_{old} = \begin{bmatrix} 3 \\ 1 \end{bmatrix}, \mathbf b_{old} = \begin{bmatrix} 1 \\ 1 \end{bmatrix} $$
Как следствие, матрица преобразований в исходный базис будет иметь вид
$$ A_{old} = \begin{bmatrix} 3 & 1 \\ 1 & 1 \end{bmatrix} $$
Возьмем вектор в новой системе координат $ \mathbf r_{new} = \begin{bmatrix} \frac{3}{2} \\ \frac{1}{2} \end{bmatrix} $.
Выразим вектор $ \mathbf r_{new} $ в исходном (привычном для нас) базисе с помощью координат нового базиса, записанных в старом базисе $ A_{old} $. Другими словами, перенесем вектор $ \mathbf r_{new} $ в соответствии с координатами $ A_{old} $.
$$ \mathbf r_{old} = A_{old} \cdot \mathbf r_{new} = \begin{bmatrix} 3 & 1 \\ 1 & 1 \end{bmatrix} \cdot \begin{bmatrix} \frac{3}{2} \\ \frac{1}{2} \end{bmatrix} = \begin{bmatrix} 5 \\ 2 \end{bmatrix}$$
Продемонстрируем это преобразование с помощью Питона.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# новый базис a = np.array([3, 1]) b = np.array([1, 1]) # старый базис i = np.array([1, 0]) j = np.array([0, 1]) # некоторый вектор r в новом базисе r_new = np.array([1.5, 0.5]) # этот же вектор, но в старом базисе r_old = np.array([5, 2]) ax = plt.axes() plt.xlim([-0.07, 5]) plt.ylim([-0.07, 5]) plt.grid() # выведем новый ax.arrow(0, 0, a[0], a[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'b', ec = 'b') ax.arrow(0, 0, b[0], b[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'b', ec = 'b') # и старый базис ax.arrow(0, 0, i[0], i[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'g', ec = 'g') ax.arrow(0, 0, j[0], j[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'g', ec = 'g') # а также вектор r в обоих базисах ax.arrow(0, 0, r_new[0], r_new[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'k', ec = 'k') ax.arrow(0, 0, r_old[0], r_old[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'r', ec = 'r') plt.show() |

Дополнительно поясним, что:
- синие стрелки — это новый базис
- зеленые — исходный
- красной стрелкой обозначается вектор $ \mathbf r_{new} $ в новом базисе
- черной — вектор $ \mathbf r_{new} $ в старом базисе
1 2 3 4 5 6 7 8 |
# возьмем матрицу перехода в старый базис A_old = np.array([[3, 1], [1, 1]]) # и убедимся, что вектор r в старом базисе # имеет приведенные выше координаты r_old = np.dot(A_old, r_new) r_old |
1 |
array([5., 2.]) |
Переход в новый базис
Выполним обратное преобразование из исходного базиса в новый. Для этого нам понадобится матрица, обратная исходной матрице $A_{old}$. Можно также сказать, что нам нужно найти исходный базис в координатах нового базиса (т.е. такой, который переводит новый базис (синие стрелки) в исходный (зеленые)).
1 2 3 |
# найдем "наш" базис в координатах "нового" базиса A_new = np.linalg.inv(A_old) A_new |
1 2 |
array([[ 0.5, -0.5], [-0.5, 1.5]]) |
1 2 |
# "вид" из нового базиса на исходный np.dot(A_new, i), np.dot(A_new, j) |
1 |
(array([ 0.5, -0.5]), array([-0.5, 1.5])) |
Выразим вектор $ \mathbf r_{old}$ в новых координатах.
1 2 |
r_new = np.dot(A_new, r_old) r_new |
1 |
array([1.5, 0.5]) |
Мы вернулись к вектору $ \mathbf r_{new} = \begin{bmatrix} \frac{3}{2} \\ \frac{1}{2} \end{bmatrix} $.
Ортонормированный базис
Решим аналогичную задачу, но с тем отличием, что новый базис будет ортонормированным (векторы ортогональны друг другу и имеют единичную длину).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# новый базис a = np.array([1/np.sqrt(2), 1/np.sqrt(2)]) b = np.array([-1/np.sqrt(2), 1/np.sqrt(2)]) # старый базис i = np.array([1, 0]) j = np.array([0, 1]) # координаты вектора в старом базисе r_old = np.array([1/np.sqrt(2), 3/np.sqrt(2)]) ax = plt.axes() plt.xlim([-1, 2]) plt.ylim([-0.07, 3]) plt.grid() ax.arrow(0, 0, a[0], a[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'b', ec = 'b') ax.arrow(0, 0, b[0], b[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'b', ec = 'b') ax.arrow(0, 0, i[0], i[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'g', ec = 'g') ax.arrow(0, 0, j[0], j[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'g', ec = 'g') ax.arrow(0, 0, r_old[0], r_old[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'r', ec = 'r') plt.show() |

1 2 3 |
# выпишем вектор в исходном базисе еще раз r_old = np.array([1/np.sqrt(2), 3/np.sqrt(2)]) r_old |
1 |
array([0.70710678, 2.12132034]) |
1 2 |
# проверим ортонормированность нового базиса np.dot(a, a).round(2), np.dot(b, b).round(2), np.dot(a, b) |
1 |
(1.0, 1.0, 0.0) |
Возьмем вектор с координатами в новом базисе $ \mathbf r_{new} = \begin{bmatrix} 2 \\ 1 \end{bmatrix} $. Преобразуем его в вектор в старом базисе.
1 2 3 4 5 6 7 8 9 10 |
# координаты вектора в новом базисе r_new = np.array([2, 1]) # новый базис в наших координатах A_old = np.array([[1/np.sqrt(2), -1/np.sqrt(2)], [1/np.sqrt(2), 1/np.sqrt(2)]]) # найдем r_old r_old = np.dot(A_old, r_new) r_old |
1 |
array([0.70710678, 2.12132034]) |
Выполним обратное преобразование, т.е. снова выразим вектор в новом базисе.
1 2 3 |
A_new = np.linalg.inv(A_old) np.dot(A_new, r_old) |
1 |
array([2., 1.]) |
Так как новый базис ортонормален, мы можем использовать проекции на векторы нового базиса $ \mathbf a $ и $ \mathbf b $.
1 2 |
scalar_proj_r_on_a = np.dot(r_old, a) / np.linalg.norm(a) ** 2 scalar_proj_r_on_a |
1 |
2.0 |
1 2 |
scalar_proj_r_on_b = np.dot(r_old, b) / np.linalg.norm(b) ** 2 scalar_proj_r_on_b |
1 |
1.0000000000000002 |
1 2 3 4 |
# так как базис имеет длину 1, нормализовывать векторы базиса не обязательно scalar_proj_r_on_a = np.dot(r_old, a) / np.linalg.norm(a) scalar_proj_r_on_b = np.dot(r_old, b) / np.linalg.norm(b) scalar_proj_r_on_a, scalar_proj_r_on_b |
1 |
(1.9999999999999998, 1.0) |
Преобразования в новом базисе
Предположим, что у нас есть вектор в новом базисе, и мы хотим повернуть его на 45 градусов против часовой стрелки. Проблема в том, что мы умеем поворачивать векторы только в старой, обычной системе координат.
Решением могло бы быть:
- Преобразование нового вектора в старый базис
- Поворот в старом, исходном базисе
- Возврат к новому базису
Если предствить, что $A_{old}$ преобразует новый базис в старый, $R$ вращает на 45 градусов в старом базисе, а $A_{old}^{-1} = A_{new}$ возвращает вектор из старого базиса в новый, то в целом преобразование можно записать, как
$$ \mathbf r_{new, 45^{\circ}} = A_{new} \cdot (R \cdot (A_{old} \cdot \mathbf r_{new})) $$
Посмотрим, как это можно реализовать на практике.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# возьмем новый базис в старой системе координат A_old = np.array([[3, 1], [1, 1]]) # найдем матрицу поворота против часовой стрелки # в обычной системе координат theta = np.radians(45) Rotate = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) # найдем новый базис в новой системе координат A_new = np.linalg.inv(A_old) A_new |
1 2 |
array([[ 0.5, -0.5], [-0.5, 1.5]]) |
1 2 |
# возьмем вектор в новом базисе r_new = np.array([1.5, 0.5]) |
1 2 3 4 5 |
# (1) сначала преобразуем "новый" вектор в обычную систему координат # (2) затем применим вращение # (3) после этого вернем вектор в новый базис rotate45_new = A_new @ (Rotate @ (A_old @ r_new)) rotate45_new |
1 |
array([-1.41421356, 6.36396103]) |
Мы получили повернутый на 45 градусов вектор в новом базисе.
Ортогональные матрицы
Мы привыкли работать в ортонормированном базисе. Например, для двумерного стандартного базиса $\begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}$ легко запомнить, что матрица расширения векторов в два раза будет выглядеть как
$$ S = \begin{bmatrix} 2 & 0 \\ 0 & 2 \end{bmatrix} $$
Математически у ортогональной (orthogonal) матрицы также есть преимущества. В частности, для ортогональной матрицы справедливо следующее (следует из умножения соответствующих векторов матрицы)
$$ Q^TQ = I $$
Если $Q$ — квадратная матрица, то
$$ Q^T = Q^{-1} $$
Другими словами, как умножение на транспонированную матрицу, так и умножение на обратную матрицу дают единичную матрицу, а значит транспонированная матрица равна обратной.
Если кроме того, векторы ортогональной матрицы имеют единичную длину, то матрица становится ортонормальной (orthonormal).
1 2 3 4 5 6 7 8 9 |
# пример ортонормальной матрицы Q = np.array([[1, 0, 0], [0, 1, 0], [0, 0, -1]]) # возьмем вектор-столбцы матрицы i = Q[:, 0] j = Q[:, 1] k = Q[:, 2] |
1 2 |
# убедимся в ортогональности np.dot(i, j).round(), np.dot(j, k).round(), np.dot(k, i).round() |
1 |
(0, 0, 0) |
1 2 |
# найдем норму каждого из векторов np.linalg.norm(i), np.linalg.norm(j), np.linalg.norm(k) |
1 |
(1.0, 1.0, 1.0) |
1 |
np.array_equal(Q.T, np.linalg.inv(Q)) |
1 |
True |
Ортогональная матрица увеличивает пространство на единицу, а значит ее определитель равен 1 или $-1$. При отрицательном определителе пространство переворачивается (в частности, вектор $ \mathbf i$ становится слева от $ \mathbf j$).
1 |
np.linalg.det(Q) |
1 |
-1.0 |
Дополнительно замечу, что так как $ A^TA = AA^T $, то можно сказать, что у ортогональной матрицы ортогональны как столбцы, так и строки.
Резюмируем преимущества ортогонального (и тем более ортонормального) базиса:
- проще найти обратную матрицу
- преобразование обратимо
- матрица проекции (помня, что $Q^TQ = I$) равна $ P = Q(Q^TQ)^{-1}Q^T = QQ^T $. Если $Q$ квадратная, то $ QQ^T = I $. Убедимся, что выполняются свойства матрицы проекции
- $ QQ^T $ — симметричная матрица
- $ (QQ^T)(QQ^T) = Q(Q^TQ)Q^T = QIQ^T = QQ^T $
Многие уравнения существенно упрощаются, если использовать ортонормальные матрицы. Рассмотрим нормальные уравнения.
$$ A^TA \mathbf x^* = A^T \mathbf b \Rightarrow Q^TQ \mathbf x^* = Q^T \mathbf b \Rightarrow \mathbf x^* = Q^T \mathbf b $$
Другими словами, точка проекции $x^*_i$ просто равна скалярному произведению вектор-строки $\mathbf q^T_i$ на вектор $\mathbf b$, $x^*_i = \mathbf q^T_i \cdot \mathbf b$.
Процесс Грама-Шмидта
Посмотрим как найти ортонормированный базис. Используемый для этого метод называется процессом Грама-Шмидта (the Gram-Schmidt process).
Описание
Рассмотрим абстрактный пример в двумерном пространстве. Возьмем векторы $\mathbf v_1$ и $\mathbf v_2$ и найдем через них ортонормальный базис $\mathbf e_1$ и $\mathbf e_2$. Вначале убедимся, что это два линейно независимых векторов (иначе пространство схлопнется и подобрать базис не получится). Линейную независимость можно проверить через определитель.
Кроме того, возможно будет полезно еще раз заметить, что одно и то же пространство, а точнее линейную оболочку могут образовывать разные базисы.

Шаг 1. Направление первого вектора $\mathbf v_1$ изменять не будем, нормализуем его и назовем $\mathbf e_1$ (зеленая стрелка).
Шаг 2. Вектор $e_2$ можно представить как составляющую в направлении вектора $\mathbf e_1$ ($proj_{\mathbf e_1} \mathbf v_2$, желтый вектор) и перпендикулярную ему составляющую.
Составляющая в направлении вектора $\mathbf e_1$ представляет собой векторную проекцию $\mathbf v_2$ на $\mathbf e_1$, то есть
$$ proj_{\mathbf e_1} \mathbf v_2 = \frac{\mathbf v_2 \cdot \mathbf e_1}{|| \mathbf e_1 ||} \frac{\mathbf e_1}{|| \mathbf e_1 ||} = \frac{\mathbf v_2 \cdot \mathbf e_1}{1} \frac{\mathbf e_1}{1} = (\mathbf v_2 \cdot \mathbf e_1) \mathbf e_1 $$
Шаг 3. Перпендикулярную $\mathbf e_1$ составляющую (фиолетовый вектор, назовем его $\mathbf u_2$) можно представить, как разницу векторов $\mathbf v_2$ и $proj_{\mathbf e_1} \mathbf v_2$, то есть
$$ \mathbf u_2 = \mathbf v_2-proj_{\mathbf e_1} \mathbf v_2 = \mathbf v_2-(\mathbf v_2 \cdot \mathbf e_1) \mathbf e_1 $$
Шаг 4. Мы нашли вектор $\mathbf u_2$ перпендикулярный вектору $\mathbf e_1$. Для того чтобы превратить $\mathbf u_2$ в $\mathbf e_2$ остается его нормализовать
$$ \mathbf e_2 = \frac{\mathbf u_2}{|| \mathbf u_2 || } $$
Для большего количества измерений процесс будет выглядеть аналогично. Обобщим процесс для трех измерений.
$$ \mathbf e_1 = \frac{\mathbf v_1}{|| \mathbf v_1 ||} $$
$$ \mathbf u_2 = \mathbf v_2-proj_{\mathbf e_1} \mathbf v_2 \rightarrow \mathbf e_2 = \frac{\mathbf u_2}{|| \mathbf u_2 ||} $$
$$ \mathbf u_3 = \mathbf v_3-proj_{\mathbf e_1} \mathbf v_3-proj_{\mathbf e_2} \mathbf v_3 \rightarrow \mathbf e_3 = \frac{\mathbf u_3}{|| \mathbf u_3 ||} $$
Разумеется, ничто не мешает нам объединить вектор-столбцы $\mathbf e_1, \mathbf e_2, \mathbf e_3$ в матрицу
$$ E = \Bigg[ \mathop{\mathbf e_1}\limits_|^| \ \mathop{\mathbf e_2}\limits_|^| \ \mathop{\mathbf e_3}\limits_|^| \Bigg] $$
Такая матрица переводит из ортонормального базиса в неортонормальный, для перехода к ортонормальному базису требуется обратная ей матрица.
Пример
Предположим, у нас есть некоторый неортономальный базис линейно независимых векторов, формирующих трехмерную линейную оболочку. У нас есть вектор в этом базисе, и мы хотим выполнить его отражение. Опять же матрицы отражения в неортонормальном базисе у нас нет. Как поступить?
- Найти ортонормальный базис в задаваемом исходными векторами пространстве
- Взять матрицу отражения в ортонормальном базисе и выполнить отражение
- Вернем вектор из ортонормального базиса в исходный
Приведем векторы в исходном неортонормальном базисе
1 2 3 |
v1 = np.array([1, 1, 1]) v2 = np.array([2, 0, 1]) v3 = np.array([3, 1, -1]) |
Ортонормированный базис
Проверим, что векторы линейно независимы.
1 2 3 |
V = np.concatenate([v1, v2, v3]).reshape(3, 3).T np.linalg.det(V), np.linalg.matrix_rank(V) |
1 |
(6.0, 3) |
Найдем первый базисный вектор $e_1$.
1 2 |
e1 = v1 / np.linalg.norm(v1) e1 |
1 |
array([0.57735027, 0.57735027, 0.57735027]) |
Найдем второй базисный вектор $e_2$.
1 2 3 4 5 6 |
# нормализовывать e1 не надо vector_proj_v2_to_e1 = np.dot(v2, e1) * e1 u2 = v2 - vector_proj_v2_to_e1 e2 = u2 / np.linalg.norm(u2) e2 |
1 |
array([ 7.07106781e-01, -7.07106781e-01, -1.57009246e-16]) |
Найдем третий базисный вектор $e_3$.
1 2 3 4 5 6 |
vector_proj_v3_to_e1 = np.dot(v3, e1) * e1 vector_proj_v3_to_e2 = np.dot(v3, e2) * e2 u3 = v3 - vector_proj_v3_to_e1 - vector_proj_v3_to_e2 e3 = u3 / np.linalg.norm(u3) e3 |
1 |
array([ 0.40824829, 0.40824829, -0.81649658]) |
Проверим ортогональность и единичную длину.
1 |
np.dot(e1, e2).round(), np.dot(e2, e3).round(), np.dot(e1, e3).round() |
1 |
(-0.0, 0.0, 0.0) |
1 |
np.linalg.norm(e1), np.linalg.norm(e2), np.linalg.norm(e3) |
1 |
(1.0, 1.0, 1.0) |
Соединим их в матрицу. Эта матрица будет переводить вектор из ортонормированного базиса в неортонормированный.
1 2 |
from_ort_basis = np.concatenate([e1, e2, e3]).reshape(3, 3).T from_ort_basis |
1 2 3 |
array([[ 5.77350269e-01, 7.07106781e-01, 4.08248290e-01], [ 5.77350269e-01, -7.07106781e-01, 4.08248290e-01], [ 5.77350269e-01, -1.57009246e-16, -8.16496581e-01]]) |
Сразу найдем обратную матрицу, которая преобразует вектор из неортонормированного базиса в ортонормированный.
1 2 3 |
# воспользуемся свойством ортонормальной матрицы # inv(ort) = ort.T to_ort_basis = from_ort_basis.T |
QR-разложение
Замечу, что ортонормальный базис также можно найти с помощью разложения матрицы на Q и R компоненты (QR-decomposition). Первый компонент этого разложения Q и будет ортонормальным базисом. Приведем пример.
1 2 |
Q, R = np.linalg.qr(V) Q |
1 2 3 |
array([[-0.57735027, 0.70710678, -0.40824829], [-0.57735027, -0.70710678, -0.40824829], [-0.57735027, 0. , 0.81649658]]) |
Убедимся в его ортонормальности.
1 2 3 4 |
q1, q2, q3 = Q[:, 0], Q[:, 1], Q[:, 2] np.dot(q1, q2).round(), np.dot(q2, q3).round(), np.dot(q1, q3).round(), \ np.linalg.norm(q1).round(), np.linalg.norm(q2).round(), np.linalg.norm(q3).round() |
1 |
(-0.0, 0.0, 0.0, 1.0, 1.0, 1.0) |
Подробнее о разложениях матрицы мы поговорим на следующем занятии. Вернемся к нашей задаче.
Матрица отражения
Возьмем матрицу отражения в ортонормальном базисе. В данном случае ее задача будет заключаться в том, чтобы отразить вектор относительно плоскости, задаваемой $e_1$ и $e_2$.
1 2 3 4 5 6 |
# зададим матрицу отражения в базисе e1, e2, e3 # векторы e1 и e2 трогать не будем # вектор e3 будет обратным Reflect = np.array([[1, 0, 0], [0, 1, 0], [0, 0, -1]]) |
Отражение и смена базиса
Теперь возьмем вектор в исходном неортонормальном базисе.
1 2 |
# зададим вектор r базисе векторов v1, v2, v3 r = np.array([2, 3, 5]) |
Последовательно применим смену базиса, отражение и возвращение в исходный базис.
1 2 3 |
# отображение в исходном базисе через промежуточный ортонормальный r_new_reflected = from_ort_basis @ (Reflect @ (to_ort_basis @ r)) r_new_reflected |
1 |
array([3.66666667, 4.66666667, 1.66666667]) |
В результате мы получили отраженный вектор в исходном неортонормальном базисе.
Подведем итог
Мы рассмотрели тему смены базиса, в частности, перехода к новому базису, возвращения к исходному, свойства ортонормального базиса и способ перехода к ортонормальному базису, называемый процессом Грама-Шмидта.
Дополнительные материалы
Перейдем ко второму разделу курса и теме разложения матриц.