Пропустить к содержанию
Руководство Varlig
Главная страница Varlig

Векторы и матрицы

Вектор — плоский список чисел, например направление в пространстве или набор количеств. Матрица — список строк. Здесь описаны скалярное и векторное произведения, матричное умножение, определитель и обратная матрица, решение систем линейных уравнений и численные методы линейной алгебры: собственные значения, QR- и LU-разложения и сингулярное разложение.

Эти функции пригодятся для учебных задач по линейной алгебре, геометрических преобразований и систем линейных уравнений. Векторы и матрицы — обычные списки, поэтому к ним применимы и операции из раздела Списки и статистика, в том числе доступ по индексу.

Векторы

calc
dot([1, 2, 3], [4, 5, 6]) даёт 32
dot([3, 4], [4, -3]) даёт 0
cross([1, 0, 0], [0, 1, 0]) даёт [0, 0, 1]
cross([1, 2, 3], [4, 5, 6]) даёт [-3, 6, -3]
v = [3, 4]
sqrt(dot(v, v)) даёт 5

Функция dot перемножает соответствующие элементы и складывает результаты. Нулевое скалярное произведение означает перпендикулярность векторов, как у [3, 4] и [4, -3]. Функция cross возвращает вектор, перпендикулярный обоим исходным: векторное произведение направления x на направление y даёт направление z. Длина вектора равна квадратному корню из его скалярного произведения с самим собой.

dot нужны два вектора одинаковой длины, а cross нужны два вектора из трех элементов. Чтобы добавить векторы или масштабировать их, используйте обычную арифметику списка: [1, 2] + [3, 4] - [4, 6] и 2 * [3, 4] - [6, 8].

Запись и умножение матриц

Напишите матрицу в виде списка строк, каждая строка в собственных квадратных скобках:

calc
shape([[1, 2, 3], [4, 5, 6]]) даёт [2, 3]
transpose([[1, 2, 3], [4, 5, 6]]) даёт [[1, 4], [2, 5], [3, 6]]
identity(3) даёт [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
[[1, 2], [3, 4]] * [[5, 6], [7, 8]] даёт [[19, 22], [43, 50]]

Функция shape возвращает [rows, columns]. transpose меняет местами строки и столбцы. identity(n) создаёт единичную матрицу размера n на n: умножение на неё оставляет другую матрицу без изменений.

* между двумя матрицами - это умножение матрицы, которое требует, чтобы левая матрица имело столько же столбцов, сколько правая матрица имеет строк. Чтобы умножить матрицу на вектор, напишите вектор в виде столбца, по одному элементу в строке:

calc
# Rotate the point (3, 1) by 90° anticlockwise
rotate = [[0, -1], [1, 0]]
rotate * [[3], [1]] даёт [[-1], [3]]
rotate * rotate даёт [[-1, 0], [0, -1]]

Два поворота на 90° дают пол-оборота, поэтому rotate * rotate меняет оба знака. Плоский список вроде [3, 1] справа от матрицы не принимается. Если в имени хранится плоский список, transpose([v]) превращает его в столбец.

Матрицы одинаковой формы могут быть сложены и вычтены, а также умножены или разделены на числовое значение, как и другие списки.

Несколько правил применяются к каждой операции матрицы:

  • Каждая строка должна иметь одинаковое количество элементов.
  • Элементы должны быть числами без единиц измерения. Единицы и денежные суммы не поддерживаются.
  • Матрица может иметь не более 32 строк и 32 столбца.
  • Сокращённая запись с точкой с запятой не поддерживается. Используйте [[1, 2], [3, 4]], а не [1, 2; 3, 4].

Определитель, обратная матрица, след и ранг

calc
M = [[2, 1], [5, 3]]
det(M) даёт 1
inverse(M) даёт [[3, -1], [-5, 2]]
M * inverse(M) даёт [[1, 0], [0, 1]]
trace(M) даёт 5
inverse([[2, 1], [1, 3]]) даёт [[0.6, -0.2], [-0.2, 0.4]]
det([[1, 2], [2, 4]]) даёт 0
rank([[1, 2, 3], [2, 4, 6]]) даёт 1

Функциям det, inverse и trace (сумма элементов главной диагонали) нужна квадратная матрица. Нулевой определитель означает, что матрица вырождена: её строки линейно зависимы и обратной матрицы нет. Поэтому inverse([[1, 2], [2, 4]]) вызывает ошибку. Функция rank определяет число линейно независимых строк и принимает также прямоугольные матрицы. В последней строке вторая строка вдвое больше первой, поэтому ранг равен 1.

Эти четыре функции используют точные дроби, а не десятичные приближения. Поэтому M * inverse(M) возвращает в точности единичную матрицу.

Решение системы линейных уравнений

Предположим, три кофе и два чая стоят 13,50 фунтов стерлингов, а один кофе и четыре чая стоят 11,50 фунтов стерлингов. Поместите количество каждого напитка в строку для каждого уравнения, столбцы в фиксированном порядке (кофе, чай) и итоги в соответствующих списках:

calc
solve_system([[3, 2], [1, 4]], [13.5, 11.5]) даёт [3.1, 2.1]

Порядок результатов совпадает с порядком столбцов: кофе стоит £3,10, чай — £2,10. Валютные символы в исходных данных опустите, поскольку матричные элементы должны быть числами без единиц.

solve_system(A, b) решает A * x = b для x. Ему нужна квадратная матрица, одна строка на неизвестное и плоский список правых сторон одинаковой длины. Он использует точные дроби, поэтому ответ не имеет ошибки округления.

Если у системы нет единственного решения, появляется соответствующая ошибка. Например, solve_system([[1, 2], [2, 4]], [3, 6]) не работает, потому что второе уравнение — удвоенное первое. Функция solve_system решает только линейные системы. Для уравнения вроде x^2 = 2 используйте solve; см. Суммы, решение уравнений и математический анализ.

Собственные значения

calc
eigenvalues([[2, 1], [1, 2]]) даёт [1, 3]
eigenvalues([[4, 1], [2, 3]]) даёт [5, 2]
eigenvalues([[0, -1], [1, 0]]) даёт [i, -i]

Функция eigenvalues принимает квадратную матрицу и возвращает плоский список. Поворот на 90° не сохраняет ни одного действительного направления, поэтому его собственные значения — комплексная пара i и -i; см. Комплексные числа. Не полагайтесь на порядок элементов результата.

Разложения QR, LU и SVD

Каждое разложение возвращает список матриц. Получайте отдельные части по индексам:

calc
A = [[3, 0], [4, 5]]
parts = qr(A)
q = parts[0] даёт [[0.6, -0.8], [0.8, 0.6]]
r = parts[1] даёт [[5, 4], [0, 3]]
q * r даёт [[3, 0], [4, 5]]

Обратное перемножение множителей даёт A, что позволяет проверить разложение. Двоичные вычисления с плавающей точкой оставляют небольшие погрешности порядка 10⁻¹⁵; в списках и матрицах Varlig показывает их как 0. Сохранённые значения содержат все вычисленные цифры, поэтому сравнивайте результаты визуально или по величине разности, а не через ==.

calc
lu([[2, 1], [4, 3]]) даёт [[[0, 1], [1, 0]], [[1, 0], [0.5, 1]], [[4, 3], [0, -0.5]]]

lu возвращает [P, L, U], где P записывает любые замены строк, а P * A равен L * U. Здесь строки были заменены местами, чтобы сначала использовалось большее число, 4.

calc
B = [[3, 1], [4, 7]]
f = svd(B)
u = f[0]
s = f[1]
vt = f[2]
u * [[s[0], 0], [0, s[1]]] * vt даёт [[3, 1], [4, 7]]
svd([[3, 4]]) даёт [[[1]], [5], [[0.6, 0.8]]]

Функция svd возвращает [U, s, Vt], где s — плоский список сингулярных значений по убыванию. В учебниках B равняется U * diag(s) * Vt. Функции diag здесь нет; создайте диагональную матрицу самостоятельно, например [[s[0], 0], [0, s[1]]].

Функция Входные данные и результат
identity(n) Единичная матрица размером n на n
shape(A) [rows, columns]
transpose(A) Строки и столбцы меняются местами
det(A) Определитель квадратной матрицы
inverse(A) Матрица, обратная квадратной матрице с ненулевым определителем
trace(A) Сумма элементов главной диагонали квадратной матрицы
rank(A) Число линейно независимых строк; любая прямоугольная матрица
solve_system(A,b) Решение A * x = b для квадратной матрицы при единственном решении
eigenvalues(A) Собственные значения квадратной матрицы, в том числе комплексные, в произвольном порядке
qr(A) [Q, R], где Q * R приблизительно равно A; любая прямоугольная матрица
lu(A) [P, L, U], где P * A приблизительно равно L * U; квадратная матрица
svd(A) [U, s, Vt], где s — плоский список; любая прямоугольная матрица

Для матрицы, которая не является квадратной, qr и svd возвращают тонкие множители: для матрицы 3 на 2 Q также 3 на 2, а не 3 на 3.

Где результат точный, а где вычисление отклоняется

Сложение, вычитание и умножение матриц, умножение матрицы на число, а также transpose, det и inverse вычисляются в точных дробях для любой допустимой матрицы. При вводе и передаче результата в дальнейшие вычисления округления нет. Поэтому произведение матрицы на обратную даёт в точности единичную матрицу:

calc
[[1, 2], [3, 4]] + [[10, 20], [30, 40]] даёт [[11, 22], [33, 44]]
[[1, 2], [3, 4]] - [[1, 1], [1, 1]] даёт [[0, 1], [2, 3]]
3 * [[1, 2], [3, 4]] даёт [[3, 6], [9, 12]]
[[1, 2], [3, 4]] * [[0, 1], [1, 0]] даёт [[2, 1], [4, 3]]
det([[1, 2], [3, 4]]) даёт -2
inverse([[1, 2], [3, 4]]) даёт [[-2, 1], [1.5, -0.5]]
inverse([[1, 2], [3, 4]]) * [[1, 2], [3, 4]] даёт [[1, 0], [0, 1]]

Каждое условие проверяется до того, как появится ответ, и в сообщении говорится, какое из них не удалось:

calc
det([[1, 2, 3], [4, 5, 6]]) даёт Unsupported: Expected a square matrix
inverse([[1, 2], [2, 4]]) даёт Unsupported: Singular matrix has no inverse
[[1, 2], [3, 4]] + [[1, 2, 3], [4, 5, 6]] даёт Unsupported: List dimensions do not match
[[1, 2, 3], [4, 5, 6]] * [[1, 2], [3, 4]] даёт Unsupported: Matrix inner dimensions do not match

Элементы матрицы должны быть числами без единиц. Единица измерения, денежная сумма или комплексное значение в любой ячейке останавливает вычисление: матричные операции здесь не сохраняют такие типы.

calc
det([[1 kg, 2], [3, 4]]) даёт Unsupported: Matrix entries must be plain numbers; units, money and complex values are not supported

Только список строк читается как матрица. Плоский список сохраняет свое собственное значение, поэтому [1, 2] + [3, 4] по-прежнему является пунктом за пунктом [4, 6], а [1, 2] * [3, 4] все еще является ошибкой, указывающей на dot.

Общие правила точности для уравнений, матриц и статистики собраны в разделе Гарантии вычислительного движка.

Алгоритмы и ограничения

det, inverse, rank и solve_system работают с точными дробями, в пределах пределов калькулятора по размеру числового значения. eigenvalues, qr, lu и svd работают в двоичной системе с плавающей запятой, поэтому их ответы являются близкими приближениями. Их итерации прекращаются после 10 000 шагов.

Матрицы, ряды которых почти зависимы, такие как [[1, 1], [1, 1.0000001]], называются плохо обусловленными. Небольшие ошибки округления становятся большими, когда вы работаете с ними, поэтому разложения и собственные значения таких матриц могут потерять точность, а очень экстремальные случаи могут превышать числовые пределы и давать ошибку.

Полные правила находятся в справке по продвинутой математике.

Всё вместе

В трёх чеках с рынка указаны количества яблок, бананов и апельсинов и общая сумма, но нет цен отдельных товаров:

calc
# Each row: apples, bananas, oranges
baskets = [[2, 3, 1], [1, 2, 3], [4, 1, 2]]
paid = [2.15, 2.70, 3.05]
det(baskets) даёт 25
prices = solve_system(baskets, paid) даёт [0.4, 0.25, 0.6]
# Check the prices against every receipt
baskets * transpose([prices]) даёт [[2.15], [2.7], [3.05]]
# What would 5 apples, 2 bananas and 4 oranges cost?
dot([5, 2, 4], prices) даёт 4.9

Ненулевой детерминант подтверждает, что квитанций достаточно, чтобы найти один набор цен: яблоки по 40 пенсов, бананы по 25 пенс и апельсины по 60 пенс. Умножение корзин на цены в столбце воспроизводит итоги, и dot оценивает новую корзину в 4,90 фунтов стерлингов.