Все курсы > Линейная алгебра > Занятие 1
Материалы по линейной алгебре используют определения и примеры следующих курсов:
- 3Blue1Brown⧉
- Khan Academy: Linear Algebra⧉
- Linear Algebra⧉, PCA⧉, ICL
- Linear Algebra⧉, MIT
- Matrix Methods for Data Analysis and Machine Learning⧉, MIT
- Matrix Algebra for Engineers⧉
На первом занятии мы более подробно рассмотрим понятие вектора и векторного пространства.
Ноутбук к сегодняшнему занятию⧉
Понятие вектора
Вектор (vector) — это направленный отрезок, и для нас будет важно, что любой вектор обладает длиной (magnitude) и направлением (direction). Например, возьмем вот такой двумерный вектор $\textbf{v}$
$$ \textbf{v} = \begin{bmatrix} 4 \\ 3 \end{bmatrix} $$
На курсе мы будем обозначать векторы полужирной строчной буквой, например $\textbf{v}$, а матрицы заглавной буквой, например, А.
Вектор $\textbf{v}$ удобно изобразить на координатной плоскости, исходящим из начала координат.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
v = np.array([4, 3]) ax = plt.axes() plt.xlim([0, 5]) plt.ylim([0, 5]) plt.grid() ax.arrow(0, 0, v[0], v[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'r', ec = 'r') plt.annotate('v', xy=(v[0]/2, v[1]/2), xytext=(10, -10), textcoords='offset points', fontsize = 16) plt.show() |

Добавлю, что вектор — частный случай матрицы. В случае вектор-столбца речь идет о матрице размерностью n x 1. В случае вектор-строки — 1 x n. Вектор $\textbf{v}$ — это матрица 2 х 1.
Операции над векторами
Сложение векторов
Складывать векторы очень несложно. Достаточно сложить их компоненты или координаты.
$$ \begin{bmatrix} 4 \\ 3 \end{bmatrix} + \begin{bmatrix} 2 \\ -1 \end{bmatrix} = \begin{bmatrix} 6 \\ 2 \end{bmatrix} $$
1 2 3 4 5 |
a = np.array([4, 3]) b = np.array([2, -1]) a_plus_b = a + b a_plus_b |
1 |
array([6, 2]) |
Графически, если поставить начало второго вектора в конец первого, сложение можно представить как расстояние от начала первого вектора до конца второго. Своего рода путь, пройденный двумя векторами.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
ax = plt.axes() plt.xlim([0, 7]) plt.ylim([0, 7]) plt.grid() arrow_a = ax.arrow(0, 0, a[0], a[1], width = 0.02, head_width = 0.2, head_length = 0.2, length_includes_head = True, fc = 'r', ec = 'r') arrow_b = ax.arrow(a[0], a[1], b[0], b[1], width = 0.02, head_width = 0.2, head_length = 0.2, length_includes_head = True, fc = 'g', ec = 'g') arrow_a_plus_b = ax.arrow(0, 0, a_plus_b[0], a_plus_b[1], width = 0.02, head_width = 0.2, head_length = 0.2, length_includes_head = True, fc = 'b', ec = 'b') plt.legend([arrow_a, arrow_b, arrow_a_plus_b], ['вектор a', 'вектор b', 'вектор a + b'], prop = {'size': 16}) plt.show() |

Сложение векторов ассоциативно $\textbf{a} + \textbf{b} = \textbf{b} + \textbf{a}$.
Умножение на скаляр
Умножение на скаляр просто удлиняет или укорачивает вектор.
$$ 2 \cdot \textbf{v} = 2 \cdot \begin{bmatrix} 4 \\ 3 \end{bmatrix} = \begin{bmatrix} 8 \\ 6 \end{bmatrix} $$
1 2 3 |
v = np.array([4, 3]) double_v = 2 * v double_v |
1 |
array([8, 6]) |
1 2 3 4 5 6 7 8 9 10 11 12 |
ax = plt.axes() plt.xlim([0, 10]) plt.ylim([0, 10]) plt.grid() ax.arrow(0, 0, double_v[0], double_v[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'b', ec = 'b') plt.annotate('2v', xy=(double_v[0], double_v[1]), xytext=(10, -10), textcoords='offset points', fontsize = 16) ax.arrow(0, 0, v[0], v[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'r', ec = 'r') plt.annotate('v', xy=(v[0], v[1]), xytext=(10, -10), textcoords='offset points', fontsize = 16) plt.show() |

Умножение на отрицательное число не только удлиняет или укорачивает вектор, но и переворачивает его направление.
$$ -0,5 \cdot \textbf{v} = -0,5 \cdot \begin{bmatrix} 4 \\ 3 \end{bmatrix} = \begin{bmatrix} -2 \\ -1,5 \end{bmatrix} $$
1 2 3 |
v = np.array([4, 3]) neg_half_v = -0.5 * v neg_half_v |
1 |
array([-2. , -1.5]) |
1 2 3 4 5 6 7 8 9 10 11 12 |
ax = plt.axes() plt.xlim([-5, 5]) plt.ylim([-5, 5]) plt.grid() ax.arrow(0, 0, neg_half_v[0], neg_half_v[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'b', ec = 'b') plt.annotate('-0.5v', xy=(neg_half_v[0], neg_half_v[1]), xytext=(10, -10), textcoords='offset points', fontsize = 16) ax.arrow(0, 0, v[0], v[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'r', ec = 'r') plt.annotate('v', xy=(v[0], v[1]), xytext=(10, -10), textcoords='offset points', fontsize = 16) plt.show() |

Очевидно, что умножение на −1 просто переворачивает направление вектора, но не меняет его длины.
$$ -1 \cdot \textbf{v} = -1 \cdot \begin{bmatrix} 4 \\ 3 \end{bmatrix} = \begin{bmatrix} -4 \\ -3 \end{bmatrix} $$
1 2 3 |
v = np.array([4, 3]) neg_one_v = -1 * v neg_one_v |
1 |
array([-4, -3]) |
1 2 3 4 5 6 7 8 9 10 11 12 |
ax = plt.axes() plt.xlim([-5, 5]) plt.ylim([-5, 5]) plt.grid() ax.arrow(0, 0, neg_one_v[0], neg_one_v[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'b', ec = 'b') plt.annotate('-v', xy=(neg_one_v[0], neg_one_v[1]), xytext=(10, -10), textcoords='offset points', fontsize = 16) ax.arrow(0, 0, v[0], v[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'r', ec = 'r') plt.annotate('v', xy=(v[0], v[1]), xytext=(10, -10), textcoords='offset points', fontsize = 16) plt.show() |

Вычитание и деление на число
Вычитание векторов можно представить как сумму первого вектора со вторым вектором, умноженным на −1.
$$ \begin{bmatrix} 4 \\ 3 \end{bmatrix} + \left( -1 \cdot \begin{bmatrix} 2 \\ -1 \end{bmatrix} \right) = \begin{bmatrix} 2 \\ 4 \end{bmatrix} $$
1 2 3 4 5 6 7 8 |
a = np.array([4, 3]) b = np.array([2, -1]) b_neg = -1 * b a_minus_b = a + b_neg b_neg, a_minus_b |
1 |
(array([-2, 1]), array([2, 4])) |
Графически мы сначала находим вектор $\textbf{-b}$ (зеленая стрелка), а затем прибавляем его к вектору $\textbf{a}$ (красная стрелка).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
plt.figure(figsize = (8, 8)) ax = plt.axes() plt.xlim([0, 7]) plt.ylim([0, 7]) plt.grid() arrow_a = ax.arrow(0, 0, a[0], a[1], width = 0.02, head_width = 0.2, head_length = 0.2, length_includes_head = True, fc = 'r', ec = 'r') arrow_b_neg = ax.arrow(a[0], a[1], b_neg[0], b_neg[1], width = 0.02, head_width = 0.2, head_length = 0.2, length_includes_head = True, fc = 'g', ec = 'g') arrow_b = ax.arrow(a[0], a[1], b[0], b[1], width = 0.0001, head_width = 0.1, head_length = 0.1, length_includes_head = True, fc = 'black', ec = 'black', linestyle = '--') arrow_a_minus_b = ax.arrow(0, 0, a_minus_b[0], a_minus_b[1], width = 0.02, head_width = 0.2, head_length = 0.2, length_includes_head = True, fc = 'b', ec = 'b') plt.legend([arrow_a, arrow_b_neg, arrow_b, arrow_a_minus_b], ['вектор a', 'вектор -b', 'вектор b', 'вектор a+(-b)'], prop = {'size': 16}) plt.show() |

Остается добавить, что деление вектора на число, это всего лишь умножение на обратное число (multiplicative inverse). Разделим вектор $\textbf{v}$ на семь.
$$ \frac{\textbf{v}}{7} = \begin{bmatrix} 4 \\ 3 \end{bmatrix} \cdot \frac{1}{7} = \begin{bmatrix} \frac{4}{7} \\ \frac{3}{7} \end{bmatrix} $$
1 2 3 |
v = np.array([4, 3]) v * (1/7) |
1 |
array([0.57142857, 0.42857143]) |
Тот факт, что мы выразили вычитание через сложение и умножение на скаляр, а деление через умножение на обратное число позволило нам остаться в пределах операций сложения и умножения на скаляр.
Видео про векторы⧉.
Длина вектора
Длину или норму вектора (norm, length, magnitude or size of a vector) рассчитать не сложно, достаточно вспомнить теорему Пифагора. Снова возьмем вектор $\textbf{v}$
$$ \textbf{v} = \begin{bmatrix} 4 \\ 3 \end{bmatrix} $$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
v = np.array([4, 3]) ax = plt.axes() plt.xlim([0, 5]) plt.ylim([-0.01, 5]) plt.grid() ax.arrow(0, 0, v[0], v[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'r', ec = 'r') plt.annotate('v', xy=(v[0]/2, v[1]/2), xytext=(10, -10), textcoords='offset points', fontsize = 16) ax.hlines(y = 0, xmin = 0, xmax = 4, linewidth = 3, color = 'b', linestyles = '--') ax.vlines(x = 4, ymin = 0, ymax = 3, linewidth = 3, color = 'g', linestyles = '--') plt.show() |

Как видно на графике, вектор $\textbf{v}$, смещение вдоль оси x и смещение вдоль оси y образуют прямоугольный треугольник. Значит, длина вектора (гипотенуза) равна квадратному корню из суммы квадратов смещений (катетов или в нашем случае координат).
$$ ||\textbf{v}|| = \sqrt{4^2 + 3^2} = \sqrt{25} = 5 $$
1 |
np.linalg.norm(v) |
1 |
5.0 |
Для n-мерного вектора ничего не меняется.
$$ ||\textbf{v}|| = \sqrt{v_1^2 + v_2^2 + \dots + v_n^2} $$
Например, найдем длину трехмерного вектора $\textbf{w}$.
$$ \textbf{w} = \begin{bmatrix} 6 \\ 3 \\ 2 \end{bmatrix} \rightarrow ||\textbf{w}|| = \sqrt{6^2 + 3^2 + 2^2} = \sqrt{49} = 7 $$
Единичный вектор
Вектор с длиной, равной единице, называют единичным вектором (unit vector). Примерами единичных векторов, с которыми мы будем часто встречаться в пространстве $ R^2 $, являются следующие два вектора
$$ \hat{i} = \begin{bmatrix} 1 \\ 0 \end{bmatrix}, \hat{j} = \begin{bmatrix} 0 \\ 1 \end{bmatrix} $$
Единичный вектор принято обозначать строчной буквой с знаком циркумфлекса, «крышечки» (hat).
Проверим, равна ли их длина единице.
$$ ||\hat{i}|| = \sqrt{1^2 + 0^2} = \sqrt{1} = 1, ||\hat{j}|| = \sqrt{0^2 + 1^2} = \sqrt{1} = 1 $$
Интересно, что с помощью векторов $ \hat{i}, \hat{j} $ можно выразить любой другой вектор в $ R^2 $. Например, вектор $\textbf{v}$ можно представить как
$$ 4 \begin{bmatrix} 1 \\ 0 \end{bmatrix} + 3 \begin{bmatrix} 0 \\ 1 \end{bmatrix} = \begin{bmatrix} 4 \\ 3 \end{bmatrix} \rightarrow 4\hat{i} + 3 \hat{j} $$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
v = np.array([4, 3]) i = np.array([1, 0]) j = np.array([0, 1]) ax = plt.axes() plt.xlim([-0.07, 4.5]) plt.ylim([-0.07, 4.5]) plt.grid() ax.arrow(0, 0, v[0], v[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'r', ec = 'r') plt.annotate('v', xy=(v[0]/2, v[1]/2), xytext=(10, -10), textcoords='offset points', fontsize = 16) 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') plt.show() |

Это обстоятельство нам пригодится в будущем.
Нормализация вектора
Длина нормализованного вектора равна единице. Для того чтобы нормализовать вектор, достаточно разделить вектор на его длину. Создадим единичный вектор $\hat{v}$ для вектора $\textbf{v}$.
$$ \hat{v} = \frac{\textbf{v}}{||\textbf{v}||} = \frac{1}{5} \cdot \begin{bmatrix} 4 \\ 3 \end{bmatrix} = \begin{bmatrix} \frac{4}{5} \\ \frac{3}{5} \end{bmatrix} $$
1 2 3 |
v = np.array([4, 3]) v * (1/np.linalg.norm(v)) |
1 |
array([0.8, 0.6]) |
Скалярное произведение
Важной операцией над векторами является уже знакомое нам скалярное произведение (dot product). В качестве напоминания того, как работает скалярное произведение приведем несложный пример. Пусть даны два вектора $\textbf{v}$ и $\textbf{w}$.
$$ \textbf{v} = \begin{bmatrix} 4 \\ 3 \end{bmatrix}, \textbf{w} = \begin{bmatrix} 2 \\ 1 \end{bmatrix} $$
Найдем их скалярное произведение.
$$ \textbf{v} \cdot \textbf{w} = 4 \cdot 2 + 3 \cdot 1 = 11 $$
Как вы видите, мы перемножаем компоненты векторов и складываем получившиеся произведения.
Скалярное произведение как длина вектора
Интересно, что корень из скалярного произведения вектора на самого себя есть его длина.
$$ \sqrt{ \textbf{v} \cdot \textbf{v} } = \sqrt{4 \cdot 4 + 3 \cdot 3 } = \sqrt{ 4^2 + 3^2 } = \sqrt{25} = 5 $$
1 2 3 |
v = np.array([4, 3]) np.sqrt(v.dot(v)) |
1 |
5.0 |
Угол между векторами
Помимо этого скалярное произведение определяется как произведение длин векторов на косинус угла между ними.
$$ \mathbf a \cdot \mathbf b = ||a|| \cdot ||b|| \cdot cos(\theta) $$
Именно это свойство привело нас к расчету косинусного расстояния между векторами.
$$ cos(\theta) = \frac{\mathbf a \cdot \mathbf b}{||a|| \cdot ||b||} $$
Выведем эту формулу. Для начала вспомним теорему косинусов.

$$ c^2 = a^2 + b^2-2ab \cdot cos(\theta) $$
Теперь представим, что у нас не стороны треугольника, а векторы. Если сторону а обозначить как вектор $ \textbf{a} $, сторону b — как вектор $ \textbf{b} $, то сторона с станет разностью между $ \textbf{a} $ и $ \textbf{b} $. Чтобы убедиться в этом, найдите $ \textbf{-b} $ и приставьте его к окончанию $ \textbf{a} $.

Выразим теорему косинусов через длины векторов.
$$ || a-b ||^2 = ||a||^2 + ||b||^2-2 \cdot ||a|| \cdot ||b|| \cdot cos(\theta) $$
Помня, что длина есть скалярное произведение вектора на самого себя, мы можем выразить левую часть $ || a-b || $ как
$$ (a-b)(a-b) = a \cdot a-a \cdot b-b \cdot a + (-b) \cdot (-b) = ||a||^2-2ab + ||b||^2 $$
Поместим результат в исходное выражение.
$$ ||a||^2-2ab + ||b||^2 = ||a||^2 + ||b||^2-2 \cdot ||a|| \cdot ||b|| \cdot cos(\theta) $$
Сократив подобные члены получим
$$ a \cdot b = ||a|| \cdot ||b|| \cdot cos(\theta) $$
Выводы. Из тригонометрии мы помним, что косинус 90 градусов равен нулю. Как следствие, скалярное произведение перпендиклярных (правильнее сказать ортогональных (orthogonal)) векторов $ \textbf{a} \perp \textbf{b} $ равно нулю.
$$ a \cdot b = ||a|| \cdot ||b|| \cdot cos(90) = 0 $$
Очевидно, что если угол между двумерными векторами меньше 90 градусов, косинус, а значит и скалярное произведение положительны. В противном случае — отрицательны. Для n-мерных векторов положительное скалярное произведение говорит, что они в целом смотрят в одну строну, отрицательное — противоположные.
Для коллинеарных (сонаправленных) векторов скалярное произведение равно произведению их длин, потому что косинус нуля равен единице.
$$ a \cdot b = ||a|| \cdot ||b|| \cdot cos(0) = ||a|| \cdot ||b|| $$
Добавлю, что если $ \textbf{a} $ и $ \textbf{b} $ — единичные векторы, то косинус угла между векторами просто равен его их скалярному произведению.
$$ cos(\theta) = a \cdot b $$
Рассчитаем косинусное расстояние для векторов
$$ \textbf{v} = \begin{bmatrix} 4 \\ 3 \end{bmatrix}, \textbf{w} = \begin{bmatrix} 1 \\ 7 \end{bmatrix} $$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
a = np.array([4, 3]) b = np.array([1, 7]) numerator = np.dot(a, b) aLen = np.linalg.norm(a) bLen = np.linalg.norm(b) denominator = aLen * bLen cosine = numerator / denominator angle = np.arccos(cosine) * 360/2/np.pi cosine, angle |
1 |
(0.7071067811865475, 45.00000000000001) |
1 2 3 |
from scipy.spatial import distance 1 - distance.cosine([4, 3], [1, 7]) |
1 |
0.7071067811865475 |
Внешнее произведение
Под внешним произведением (outer product) понимается умножение вектор-столбца на вектор-строку по обычным правилам матричного умножения. Результатом такого произведения будет матрица, а не число, как в случае скалярного произведения.

Ортогональность векторов
Еще раз продемонстрируем, почему если векторы ортогональны, их скалярное произведение будет равно нулю. Возьмем три вектора, образовывающих прямоугольный треугольник.

Тогда, по теореме Пифагора,
$$ || \mathbf x ||^2 + || \mathbf y ||^2 = || \mathbf x + \mathbf y ||^2 $$
$$ \mathbf x^T \mathbf x + \mathbf y^T \mathbf y = (\mathbf x + \mathbf y)^T (\mathbf x + \mathbf y) $$
$$ \mathbf x^T \mathbf x + \mathbf y^T \mathbf y = \mathbf x^T \mathbf x + \mathbf y^T \mathbf y + \mathbf x^T \mathbf y + \mathbf y^T \mathbf x $$
$$ \mathbf x^T \mathbf x + \mathbf y^T \mathbf y = \mathbf x^T \mathbf x + \mathbf y^T \mathbf y + 2 \mathbf x^T \mathbf y $$
$$ \mathbf 0 = 2 \mathbf x^T \mathbf y $$
$$ \mathbf x^T \mathbf y = \mathbf 0 $$
Проекция вектора на вектор
Подойдем к скалярному произведению с другой стороны. Рассмотрим два вектора $ \textbf{a} $ и $ \textbf{b} $ и найдем проекцию первого вектора на второй.

Проекция через угол между векторами
Говоря неформально, проекцией вектора $ \textbf{a} $ на вектор $ \textbf{b} $ будет такой участок вектора $ \textbf{b} $, что расстояние от точки A до точки B минимально. Минимальным оно будет, если угол OAB будет равен 90 градусов. Получается прямоугольный треугольник. Найдем отрезок OB.
$$ cos(\theta) = \frac {OB}{OA} = \frac {OB}{||a||} \rightarrow OB = ||a|| \cdot cos(\theta) $$
Выразим то же самое через формулу скалярного произведения, заменив $||a|| \cdot cos(\theta) $ на OB.
$$ b \cdot a = ||b|| \cdot ||a|| \cdot cos(\theta) \rightarrow b \cdot a = ||b|| \cdot OB $$
Так мы нашли длину проекции OB. Ее называют числовой или скалярной проекцией (scalar projection).
$$ \frac{b \cdot a}{||b||} = ||a|| \cdot cos(\theta) = OB $$
Более того, если длина вектора $ \textbf{b} $ равна единице, то длина проекции OB просто равна скалярному произведению.
$$ ||b|| = 1 \rightarrow b \cdot a = OB $$
Это объясняет, почему скалярное произведение еще называют projection product.
Очевидное и тем не менее интересное примечание. Обратите внимание на связь понимания скалярного произведения как проекции одного вектора на другой и произведения длин векторов на косинус угла между ними. Если векторы перпендикулярны, проекция одного вектора на другой равна нулю, а значит и произведение проекции второго вектора на первый равно нулю.

Предположим, нас интересует не только длина проекции, но и ее направление. В этом случае говорят про векторную проекцию (vector projection).

Она выражается как произведение нормализованного вектора $ \textbf{b} $ на длину проекции (то есть скалярную проекцию) OB.
$$ proj_\mathbf{b} \textbf{a} = OB \cdot \hat{b} $$
Перепишем OB через скалярное произведение, а $\hat{b}$ через частное вектора $ \textbf{b} $ на его длину.
$$ proj_\mathbf{b} \textbf{a} = \frac{b \cdot a}{||b||} \cdot \frac{b}{||b||} $$
Таким образом, можно сказать, что векторная проекция показывает, длину вектора $ \textbf{a} $ в направлении вектора $ \textbf{b} $.
Пример. Возьмем два вектора a и b и найдем вначале скалярную, затем векторную проекцию вектора a на вектор b.
1 2 3 4 5 |
a = np.array([3, 4]) b = np.array([1, 1]) scalar_proj_a_on_b = np.dot(a, b) / np.linalg.norm(b) scalar_proj_a_on_b |
1 |
4.949747468305833 |
1 2 |
vector_proj_a_on_b = scalar_proj_a_on_b * (b / np.linalg.norm(b)) vector_proj_a_on_b |
1 |
array([3.5, 3.5]) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
a = np.array([3, 4]) b = np.array([1, 1]) proj = np.array([3.5, 3.5]) ax = plt.axes() plt.xlim([-0.07, 4.5]) plt.ylim([-0.07, 4.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 = 'r', ec = 'r') ax.arrow(0, 0, b[0], b[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'k', ec = 'k') ax.arrow(0, 0, proj[0], proj[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'g', ec = 'g') plt.show() |

Матрица проекции
Векторную проекцию можно выразить с помощью матрицы проекции $P$.
$$ proj_\mathbf{b} \textbf{a} = P \cdot \mathbf a = \frac{\mathbf b \mathbf b^T}{\mathbf b^T \mathbf b} \cdot \mathbf a $$
В знаменателе находится скалярное произведение и результатом умножения будет число. В числителе — внешнее произведение и результатом будет матрица. Найдем внешнее произведение из примера выше.
1 2 |
b_bT = np.outer(b, b) b_bT |
1 2 |
array([[1, 1], [1, 1]]) |
Найдем скалярное произведение.
1 2 |
bT_b = np.dot(b, b) bT_b |
1 |
2 |
Создадим матрицу проекции $P$ и умножим ее на вектор $\mathbf a$.
1 2 |
P = b_bT / bT_b P @ a |
1 |
array([3.5, 3.5]) |
Симметрия скалярного произведения
Продемонстрируем с точки зрения проекции, почему $a \cdot b = b \cdot a $. Возьмем два вектора a и b.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
a = np.array([1, 3]) b = np.array([4, 1]) i = np.array([1, 0]) j = np.array([0, 1]) ax = plt.axes() plt.xlim([-0.07, 4]) plt.ylim([-0.07, 4]) 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 = 'r', ec = 'r') ax.arrow(0, 0, b[0], b[1], width = 0.02, head_width = 0.1, head_length = 0.2, length_includes_head = True, fc = 'k', ec = 'k') 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.plot([0, 4], [0, 4], linestyle = 'dashed') plt.show() |

Выше мы сказали, что $ a \cdot b = OB \cdot ||b|| $. То есть скалярное произведение вектора a на вектор b равно произведению проекции a на b, умноженной на длину вектора b.
Продемонстрируем, что произведение проекции вектора a на вектор b, умноженное на длину вектора b, равно произведению проекции вектора b на вектор a, умноженному на длину вектора a.
$$ proj_ba \times || b || = proj_ab \times || a || $$
1 2 |
scalar_proj_a_on_b = np.dot(a, b) / np.linalg.norm(b) scalar_proj_a_on_b * np.linalg.norm(b) |
1 |
7.0 |
1 2 |
scalar_proj_b_on_a = np.dot(b, a) / np.linalg.norm(a) scalar_proj_b_on_a * np.linalg.norm(a) |
1 |
7.0 |
1 |
np.dot(a, b), np.dot(b, a) |
1 |
(7, 7) |
Видео про скалярное произведение векторов⧉.
Векторное произведение
Векторное произведение (cross product) задано только в трехмерном пространстве. Результатом такого произведения будет вектор, перпендикулярный каждому из исходных векторов. Приведем иллюстрацию из Википедии⧉.

Математически векторное произведение задается формулой
$$ a \times b = || a || || b || sin(\theta) $$
Геометрически — это площадь параллелограмма, сформированного из исходных векторов a и b.

Приведем пример.
1 2 3 4 |
a = [1, 2, 3] b = [4, 5, 6] np.cross(a, b) |
1 |
array([-3, 6, -3]) |
Подведем итог
Сегодня мы ввели понятие вектора, познакомились с базовыми операциями с векторами, в частности, изучили скалярное произведение векторов и научились находить скалярную и векторную проекцию одного вектора на другой.
Перейдем к рассмотрению векторного пространства.