Угол между векторами и проекция
Из скалярного произведения вытекают две практичные вещи: угол между стрелками и «тень» одной стрелки на другую.
Проекция вектора $\vec{a}$ на вектор $\vec{b}$ — это «тень» $\vec{a}$, отброшенная вдоль направления $\vec{b}$; она показывает, какая часть $\vec{a}$ идёт в сторону $\vec{b}$.
Перевернём формулу скалярного произведения и получим инструмент для измерения угла. А затем разложим вектор на две части — вдоль выбранного направления и поперёк него. Это разложение — основа метода наименьших квадратов, сжатия данных и компьютерной графики.
Косинус угла
Из равенства $\vec{a} \cdot \vec{b} = \lVert \vec{a} \rVert \lVert \vec{b} \rVert \cos\theta$ выражаем косинус:
$$\cos\theta = \frac{\vec{a} \cdot \vec{b}}{\lVert \vec{a} \rVert \, \lVert \vec{b} \rVert}$$
Эта величина всегда лежит в диапазоне от $-1$ до $1$. Значение около $1$ означает «смотрят в одну сторону», около $0$ — «перпендикулярны», около $-1$ — «смотрят в противоположные стороны». Именно эту «косинусную близость» используют, чтобы измерять схожесть текстов и пользователей.
Проекция
Проекция $\vec{a}$ на $\vec{b}$ — вектор, направленный вдоль $\vec{b}$, длина которого равна «тени» $\vec{a}$:
$$\text{proj}_{\vec{b}}\,\vec{a} = \frac{\vec{a} \cdot \vec{b}}{\vec{b} \cdot \vec{b}} \, \vec{b}$$
Коэффициент перед $\vec{b}$ — это число, говорящее «во сколько раз взять $\vec{b}$». Остаток $\vec{a} - \text{proj}_{\vec{b}}\,\vec{a}$ перпендикулярен $\vec{b}$ — так любой вектор раскладывается на «вдоль» и «поперёк».
import math
def dot(a, b):
return sum(x * y for x, y in zip(a, b))
a = [3, 4]
b = [1, 0]
cos = dot(a, b) / (math.sqrt(dot(a, a)) * math.sqrt(dot(b, b)))
angle = math.degrees(math.acos(cos))
print("cos =", round(cos, 4))
print("angle (deg) =", round(angle, 2))
k = dot(a, b) / dot(b, b)
proj = [k * x for x in b]
print("proj a on b =", proj)Вывод:
cos = 0.6 angle (deg) = 53.13 proj a on b = [3.0, 0.0]
Что показал расчёт
Вектор $\vec{b} = (1, 0)$ — это ось $x$. Проекция $\vec{a} = (3, 4)$ на неё равна $(3, 0)$: именно столько $\vec{a}$ «идёт вправо». Косинус угла $0{,}6$ соответствует $53{,}13°$ — углу той самой стрелки $(3, 4)$ относительно горизонтали.
Как работает под капотом
Чтобы найти угол, мы делим скалярное произведение на произведение длин и берём арккосинус через math.acos; math.degrees переводит радианы в градусы. В формуле проекции знаменатель $\vec{b} \cdot \vec{b}$ — это квадрат длины $\vec{b}$; он нормирует ответ так, чтобы результат не зависел от длины $\vec{b}$, а только от её направления. Если бы $\vec{b}$ был единичным ($\lVert \vec{b} \rVert = 1$), знаменатель стал бы равен 1 и формула упростилась до $(\vec{a} \cdot \vec{b})\,\vec{b}$.
Частые ошибки
- Забывать делить на длины при вычислении косинуса — тогда получится не косинус, а просто скалярное произведение.
- Передавать в
math.acosзначение чуть больше 1 из-за ошибок округления — функция бросит ошибку. На практике результат подрезают к диапазону $[-1, 1]$. - Путать проекцию-вектор и её длину (скалярную проекцию). Коэффициент $k$ — число, $k\vec{b}$ — вектор.
Итог
- Косинус угла = скалярное произведение, делённое на произведение длин.
- Косинусная близость измеряет «схожесть направлений» и широко применяется в ML.
- Проекция раскладывает вектор на часть вдоль $\vec{b}$ и перпендикулярный остаток.
- Для единичного $\vec{b}$ проекция упрощается до $(\vec{a} \cdot \vec{b})\,\vec{b}$.