Векторы и матрицы
Вектор — плоский список чисел, например направление в пространстве или набор количеств. Матрица — список строк. Здесь описаны скалярное и векторное произведения, матричное умножение, определитель и обратная матрица, решение систем линейных уравнений и численные методы линейной алгебры: собственные значения, QR- и LU-разложения и сингулярное разложение.
Эти функции пригодятся для учебных задач по линейной алгебре, геометрических преобразований и систем линейных уравнений. Векторы и матрицы — обычные списки, поэтому к ним применимы и операции из раздела Списки и статистика, в том числе доступ по индексу.
Векторы
dot([1, 2, 3], [4, 5, 6]) даёт 32dot([3, 4], [4, -3]) даёт 0cross([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].
Запись и умножение матриц
Напишите матрицу в виде списка строк, каждая строка в собственных квадратных скобках:
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: умножение на неё оставляет другую матрицу без изменений.
* между двумя матрицами - это умножение матрицы, которое требует, чтобы левая матрица имело столько же столбцов, сколько правая матрица имеет строк. Чтобы умножить матрицу на вектор, напишите вектор в виде столбца, по одному элементу в строке:
# Rotate the point (3, 1) by 90° anticlockwiserotate = [[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].
Определитель, обратная матрица, след и ранг
M = [[2, 1], [5, 3]]det(M) даёт 1inverse(M) даёт [[3, -1], [-5, 2]]M * inverse(M) даёт [[1, 0], [0, 1]]trace(M) даёт 5inverse([[2, 1], [1, 3]]) даёт [[0.6, -0.2], [-0.2, 0.4]]det([[1, 2], [2, 4]]) даёт 0rank([[1, 2, 3], [2, 4, 6]]) даёт 1Функциям det, inverse и trace (сумма элементов главной диагонали) нужна квадратная матрица. Нулевой определитель означает, что матрица вырождена: её строки линейно зависимы и обратной матрицы нет. Поэтому inverse([[1, 2], [2, 4]]) вызывает ошибку. Функция rank определяет число линейно независимых строк и принимает также прямоугольные матрицы. В последней строке вторая строка вдвое больше первой, поэтому ранг равен 1.
Эти четыре функции используют точные дроби, а не десятичные приближения. Поэтому M * inverse(M) возвращает в точности единичную матрицу.
Решение системы линейных уравнений
Предположим, три кофе и два чая стоят 13,50 фунтов стерлингов, а один кофе и четыре чая стоят 11,50 фунтов стерлингов. Поместите количество каждого напитка в строку для каждого уравнения, столбцы в фиксированном порядке (кофе, чай) и итоги в соответствующих списках:
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; см. Суммы, решение уравнений и математический анализ.
Собственные значения
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
Каждое разложение возвращает список матриц. Получайте отдельные части по индексам:
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. Сохранённые значения содержат все вычисленные цифры, поэтому сравнивайте результаты визуально или по величине разности, а не через ==.
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.
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 вычисляются в точных дробях для любой допустимой матрицы. При вводе и передаче результата в дальнейшие вычисления округления нет. Поэтому произведение матрицы на обратную даёт в точности единичную матрицу:
[[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]]) даёт -2inverse([[1, 2], [3, 4]]) даёт [[-2, 1], [1.5, -0.5]]inverse([[1, 2], [3, 4]]) * [[1, 2], [3, 4]] даёт [[1, 0], [0, 1]]Каждое условие проверяется до того, как появится ответ, и в сообщении говорится, какое из них не удалось:
det([[1, 2, 3], [4, 5, 6]]) даёт Unsupported: Expected a square matrixinverse([[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Элементы матрицы должны быть числами без единиц. Единица измерения, денежная сумма или комплексное значение в любой ячейке останавливает вычисление: матричные операции здесь не сохраняют такие типы.
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]], называются плохо обусловленными. Небольшие ошибки округления становятся большими, когда вы работаете с ними, поэтому разложения и собственные значения таких матриц могут потерять точность, а очень экстремальные случаи могут превышать числовые пределы и давать ошибку.
Полные правила находятся в справке по продвинутой математике.
Всё вместе
В трёх чеках с рынка указаны количества яблок, бананов и апельсинов и общая сумма, но нет цен отдельных товаров:
# Each row: apples, bananas, orangesbaskets = [[2, 3, 1], [1, 2, 3], [4, 1, 2]]paid = [2.15, 2.70, 3.05]det(baskets) даёт 25prices = solve_system(baskets, paid) даёт [0.4, 0.25, 0.6]# Check the prices against every receiptbaskets * 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 фунтов стерлингов.