Матрицы и многомерные массивы

Двумерные массивы — матрицы — основа линейной алгебры и научных расчётов в Julia.

Матрица — двумерный массив, тип Matrix{T}, то есть Array{T,2}. Число в фигурных скобках после Array — это размерность.

Создание матриц

Внутри квадратных скобок: элементы строки разделяют пробелом, а строки — точкой с запятой:

A = [1 2 3; 4 5 6]
println(A)
println(size(A))     # (строки, столбцы)

Вывод:

[1 2 3; 4 5 6]
(2, 3)

Готовые матрицы можно создать функциями: zeros(3, 3), ones(2, 4), rand(3, 3) (случайные), fill(7, 2, 2) (заполнить значением).

Индексация: строка, столбец

Элемент матрицы адресуется двумя индексами — A[строка, столбец], оба с 1:

A = [1 2 3; 4 5 6]
println(A[1, 2])     # строка 1, столбец 2
println(A[2, :])     # вся вторая строка
println(A[:, 1])     # весь первый столбец

Вывод:

2
[4, 5, 6]
[1, 4]

Двоеточие : означает «все элементы по этой оси».

Операции линейной алгебры

Julia понимает матричные операции из математики напрямую: * — это матричное умножение, ' — транспонирование:

A = [1 2; 3 4]
B = [5 6; 7 8]
println(A * B)       # матричное произведение
println(A')          # транспонирование

Вывод:

[19 22; 43 50]
[1 3; 2 4]

Это удобнее NumPy, где для матричного умножения нужен оператор @ или функция dot.

Как работает под капотом

Важная техническая деталь: Julia хранит матрицы по столбцам (column-major), как Fortran и MATLAB, а не по строкам, как C и NumPy. Это значит, что элементы одного столбца лежат в памяти подряд. Практическое следствие: перебирать матрицу эффективнее по столбцам. Если во вложенном цикле внешним сделать столбец, а внутренним — строку, код лучше попадает в кэш процессора и работает быстрее.

# эффективный порядок обхода для column-major
A = rand(1000, 1000)
s = 0.0
for j in 1:size(A, 2)      # сначала по столбцам
    for i in 1:size(A, 1)  # потом по строкам
        s += A[i, j]
    end
end

Частые ошибки

Тем, кто пришёл из NumPy, легко перепутать порядок обхода: в NumPy быстрее идти по строкам, в Julia — по столбцам. Также частая ошибка — ожидать, что A * B перемножит элементы поэлементно (как в NumPy без специальных функций). В Julia * для матриц — это именно матричное умножение; для поэлементного нужен «точечный» оператор .*, о котором следующий урок.

Итоги

  • Матрица: строки разделяются ;, элементы — пробелом: [1 2; 3 4].
  • Индексация двумя индексами A[i, j], оба с 1; : — вся ось.
  • * — матричное умножение, ' — транспонирование.
  • Матрицы хранятся по столбцам (column-major) — перебирайте по столбцам для скорости.
Проверьте себя
1. Как в Julia записать матрицу с двумя строками [1 2 3] и [4 5 6]?
A[1,2,3 | 4,5,6]
B[1 2 3; 4 5 6]
C[[1,2,3],[4,5,6]]
Dmatrix(1,2,3,4,5,6)
2. Что делает оператор * для двух матриц в Julia?
AПеремножает элементы поэлементно
BВыполняет матричное умножение
CСкладывает матрицы
DВызывает ошибку
3. В каком порядке Julia хранит элементы матрицы в памяти?
AПо строкам (row-major), как C и NumPy
BПо столбцам (column-major), как Fortran и MATLAB
CВ случайном порядке
DВ виде дерева