Armadillo для C++: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Armadillo для C++: полное руководство по матричным вычислениям

1. Что это и зачем нужна?

Armadillo — это высокопроизводительная библиотека линейной алгебры для C++, которая предоставляет удобный и интуитивно понятный интерфейс для работы с матрицами, векторами и кубами (трёхмерными массивами). Она спроектирована так, чтобы сочетать скорость низкоуровневых библиотек (таких как LAPACK и BLAS) с простотой использования, характерной для языков высокого уровня, таких как MATLAB или Python (NumPy).

Зачем она нужна? Если вы разрабатываете приложения, связанные с машинным обучением, компьютерным зрением, обработкой сигналов, робототехникой, физическим моделированием или любыми другими областями, где требуются интенсивные матричные вычисления, Armadillo станет вашим незаменимым инструментом. Она позволяет писать чистый, читаемый код, который при этом выполняется максимально быстро благодаря автоматической оптимизации и использованию аппаратного ускорения (через OpenBLAS, Intel MKL или Apple Accelerate).

В отличие от сырых циклов по элементам массива, Armadillo предлагает высокоуровневые операции: умножение матриц, решение систем линейных уравнений, вычисление собственных значений, сингулярное разложение (SVD) и многое другое. Библиотека активно используется в исследовательских проектах и промышленности, а её API настолько близок к MATLAB, что код часто можно переносить практически один в один.

2. Установка

Armadillo — это библиотека для C++, поэтому она не устанавливается через pip или npm. Вместо этого её нужно установить как системную библиотеку или подключить к проекту через менеджер пакетов C++ (например, vcpkg, Conan) или вручную.

Рекомендуемый способ (Linux/macOS):

# Ubuntu / Debian
sudo apt-get install libarmadillo-dev

# macOS (через Homebrew)
brew install armadillo

# Fedora / RHEL
sudo dnf install armadillo-devel

Установка через vcpkg (Windows, Linux, macOS):

git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh  # или bootstrap-vcpkg.bat на Windows
./vcpkg install armadillo

Компиляция с Armadillo (g++/clang): После установки линкуйте библиотеку:

g++ -std=c++11 -O2 my_program.cpp -larmadillo -o my_program

Для максимальной производительности рекомендуется установить также оптимизированный бэкенд BLAS (например, OpenBLAS или Intel MKL). Armadillo автоматически найдёт его, если он установлен.

3. Быстрый старт

Минимальный рабочий пример, демонстрирующий создание матриц и базовые операции:

#include <iostream>
#include <armadillo>

using namespace arma;

int main() {
    // Создаём матрицу 3x3, заполненную единицами
    mat A = ones<mat>(3, 3);
    std::cout << "Матрица A:\n" << A << std::endl;

    // Создаём вектор-столбец
    vec b = {1, 2, 3};
    std::cout << "Вектор b:\n" << b << std::endl;

    // Решаем систему линейных уравнений A * x = b
    vec x = solve(A, b);
    std::cout << "Решение x:\n" << x << std::endl;

    // Проверка: A * x должно быть равно b
    std::cout << "Проверка A * x:\n" << A * x << std::endl;

    return 0;
}

Вывод: Программа создаст единичную матрицу (все элементы равны 1), решит систему и выведет результат.

4. Основные методы и классы

Armadillo предоставляет три основных класса: mat (матрица double), vec (вектор double), cx_mat (комплексная матрица). Ниже приведены 8 ключевых методов с примерами.

4.1. mat::mat() — Конструкторы

Сигнатура: mat(n_rows, n_cols, fill_type)

Описание: Создаёт матрицу заданного размера. fill_type может быть fill::zeros, fill::ones, fill::eye (единичная) или fill::randu (случайная).

#include <armadillo>
using namespace arma;

int main() {
    mat A(4, 5, fill::zeros);   // Матрица 4x5 из нулей
    mat B = eye<mat>(3, 3);     // Единичная матрица 3x3
    mat C = randu<mat>(2, 2);   // Случайная матрица 2x2
    std::cout << "A:\n" << A << "\nB:\n" << B << "\nC:\n" << C << std::endl;
    return 0;
}

4.2. .n_rows и .n_cols — Размерность

Сигнатура: mat::n_rows, mat::n_cols

Описание: Свойства (атрибуты), возвращающие количество строк и столбцов матрицы.

mat M = randu<mat>(5, 3);
std::cout << "Строк: " << M.n_rows << ", Столбцов: " << M.n_cols << std::endl;

4.3. solve() — Решение СЛАУ

Сигнатура: vec solve(const mat& A, const vec& b)

Описание: Решает систему линейных уравнений A * x = b. Использует LU-разложение с частичным выбором главного элемента.

mat A = {{2, 1}, {1, 3}};
vec b = {4, 5};
vec x = solve(A, b);
std::cout << "x = \n" << x << std::endl;

4.4. inv() — Обратная матрица

Сигнатура: mat inv(const mat& A)

Описание: Вычисляет обратную матрицу. Для больших матриц рекомендуется использовать solve() вместо явного обращения.

mat A = {{1, 2}, {3, 4}};
mat A_inv = inv(A);
std::cout << "A_inv:\n" << A_inv << std::endl;
std::cout << "A * A_inv:\n" << A * A_inv << std::endl; // Должна быть единичная

4.5. det() — Определитель

Сигнатура: double det(const mat& A)

Описание: Вычисляет определитель квадратной матрицы.

mat A = {{1, 2}, {3, 4}};
double d = det(A);
std::cout << "det(A) = " << d << std::endl;

4.6. svd() — Сингулярное разложение

Сигнатура: bool svd(mat& U, vec& s, mat& V, const mat& A)

Описание: Выполняет сингулярное разложение матрицы A: A = U * diag(s) * V.t(). Возвращает true в случае успеха.

mat A = {{1, 2}, {3, 4}, {5, 6}};
mat U, V;
vec s;
svd(U, s, V, A);
std::cout << "U:\n" << U << "\ns:\n" << s << "\nV:\n" << V << std::endl;

4.7. .t() — Транспонирование

Сигнатура: mat mat::t()

Описание: Возвращает транспонированную матрицу. Не изменяет исходную.

mat A = {{1, 2, 3}, {4, 5, 6}};
mat A_t = A.t();
std::cout << "A_t:\n" << A_t << std::endl;

4.8. .submat() — Извлечение подматрицы

Сигнатура: mat mat::submat(uword row1, uword col1, uword row2, uword col2)

Описание: Извлекает непрерывный блок матрицы от (row1, col1) до (row2, col2).

mat A = randu<mat>(5, 5);
mat B = A.submat(0, 0, 2, 2); // Левый верхний угол 3x3
std::cout << "Подматрица B:\n" << B << std::endl;

5. Реальный практический пример: Линейная регрессия

В этом примере мы реализуем метод наименьших квадратов для линейной регрессии. Дана матрица признаков X (с дополнительным столбцом единиц для свободного члена) и вектор целевых значений y. Задача — найти вектор коэффициентов theta, минимизирующий ||X*theta - y||^2.

#include <iostream>
#include <armadillo>
#include <vector>

using namespace arma;
using namespace std;

int main() {
    // --- 1. Генерация синтетических данных ---
    // Истинная зависимость: y = 2*x1 + 3*x2 + 5 + шум
    size_t m = 100; // Количество примеров
    size_t n = 2;   // Количество признаков

    // Матрица признаков X (m x n)
    mat X = randu<mat>(m, n);
    // Истинные коэффициенты
    vec true_theta = {2.0, 3.0, 5.0}; // [2, 3, intercept]

    // Добавляем столбец единиц для свободного члена (intercept)
    mat X_with_ones = join_horiz(ones<mat>(m, 1), X); // (m x (n+1))

    // Генерируем целевую переменную с шумом
    vec noise = 0.1 * randn<vec>(m);
    vec y = X_with_ones * true_theta + noise;

    cout << "Сгенерировано " << m << " точек данных." << endl;

    // --- 2. Решение нормального уравнения ---
    // theta = (X^T * X)^{-1} * X^T * y
    vec theta_hat = solve(X_with_ones.t() * X_with_ones, X_with_ones.t() * y);

    cout << "Истинные коэффициенты: " << true_theta.t();
    cout << "Оценённые коэффициенты: " << theta_hat.t();

    // --- 3. Оценка качества ---
    vec y_pred = X_with_ones * theta_hat;
    double mse = mean(square(y - y_pred));
    double r2 = 1 - (sum(square(y - y_pred)) / sum(square(y - mean(y))));

    cout << "Среднеквадратичная ошибка (MSE): " << mse << endl;
    cout << "Коэффициент детерминации (R^2): " << r2 << endl;

    // --- 4. Предсказание для новых данных ---
    mat X_new = {{0.5, 0.8}, {1.2, 0.3}};
    mat X_new_with_ones = join_horiz(ones<mat>(X_new.n_rows, 1), X_new);
    vec y_new_pred = X_new_with_ones * theta_hat;
    cout << "Предсказания для новых точек:\n" << y_new_pred << endl;

    return 0;
}

Этот пример демонстрирует: генерацию данных, конкатенацию матриц (join_horiz), решение системы через solve(), поэлементные операции (square, mean, sum) и предсказание.

6. Советы по производительности и типичные ошибки

Рекомендаций