В дополнение к примеру "броненосца" Вики Будхираджи, это 2D версия accmarray, использующая семантическую функцию, аналогичную функции matlab:
arma::mat accumarray (arma::mat& subs, arma::vec& val, arma::rowvec& sz)
{
arma::u32 ar = sz.col(0)(0);
arma::u32 ac = sz.col(1)(0);
arma::mat A; A.set_size(ar, ac);
for (arma::u32 r = 0; r < ar; ++r)
{
for (arma::u32 c = 0; c < ac; ++c)
{
arma::uvec idx = arma::find(subs.col(0) == r &&
subs.col(1) == c);
if (!idx.is_empty())
A(r, c) = arma::sum(val.elem(idx));
else
A(r, c) = 0;
}
}
return A;
}
Вход sz представляет собой вектор из двух столбцов, который содержит: num строк / num cols для выходной матрицы A. Подматрица представляет собой 2 столбца с одинаковыми num строками val. Num строк val в основном sz.rows по sz.cols.
Ввод sz (размер) на самом деле не является обязательным и может быть легко определен путем поиска максимального значения в подстолбцах.
arma::u32 sz_rows = arma::max(subs.col(0)) + 1;
arma::u32 sz_cols = arma::max(subs.col(1)) + 1;
или
arma::u32 sz_rows = arma::max(subs.col(0)) + 1;
arma::u32 sz_cols = val.n_elem / sz_rows;
матрица вывода теперь:
arma::mat A (sz_rows, sz_cols);
функция accmarray становится:
arma::mat accumarray (arma::mat& subs, arma::vec& val)
{
arma::u32 sz_rows = arma::max(subs.col(0)) + 1;
arma::u32 sz_cols = arma::max(subs.col(1)) + 1;
arma::mat A (sz_rows, sz_cols);
for (arma::u32 r = 0; r < sz_rows; ++r)
{
for (arma::u32 c = 0; c < sz_cols; ++c)
{
arma::uvec idx = arma::find(subs.col(0) == r &&
subs.col(1) == c);
if (!idx.is_empty())
A(r, c) = arma::sum(val.elem(idx));
else
A(r, c) = 0;
}
}
return A;
}
Например:
arma::vec val = arma::regspace(101, 106);
arma::mat subs;
subs << 0 << 0 << arma::endr
<< 1 << 1 << arma::endr
<< 2 << 1 << arma::endr
<< 0 << 0 << arma::endr
<< 1 << 1 << arma::endr
<< 3 << 0 << arma::endr;
arma::mat A = accumarray (subs, val);
A.raw_print("A =");
Произведите этот результат:
A =
205 0
0 207
0 103
106 0
Этот пример находится здесь: http://fr.mathworks.com/help/matlab/ref/accumarray.html?requestedDomain=www.mathworks.com
за исключением индексов подводных лодок, броненосец - это индекс на основе 0, где matlab - на основе 1.
К сожалению, предыдущий код не подходит для большой матрицы. Два цикла for с находкой в векторе между ними - это действительно плохо. Код хорош для понимания концепции, но его можно оптимизировать как один цикл, подобный следующему:
arma::mat accumarray(arma::mat& subs, arma::vec& val)
{
arma::u32 ar = arma::max(subs.col(0)) + 1;
arma::u32 ac = arma::max(subs.col(1)) + 1;
arma::mat A(ar, ac);
A.zeros();
for (arma::u32 r = 0; r < subs.n_rows; ++r)
A(subs(r, 0), subs(r, 1)) += val(r);
return A;
}
Единственное изменение:
- инициализация выходной матрицы с нулями.
- зацикливать на подстроки для получения выходных индексов
- накапливать val для вывода (sub и val синхронизируются по строкам)
1-D версия (вектор) функции может выглядеть примерно так:
arma::vec accumarray (arma::ivec& subs, arma::vec& val)
{
arma::u32 num_elems = arma::max(subs) + 1;
arma::vec A (num_elems);
A.zeros();
for (arma::u32 r = 0; r < subs.n_rows; ++r)
A(subs(r)) += val(r);
return A;
}
Для тестирования 1D версии:
arma::vec val = arma::regspace(101, 105);
arma::ivec subs;
subs << 0 << 2 << 3 << 2 << 3;
arma::vec A = accumarray(subs, val);
A.raw_print("A =");
Результат соответствует примерам matlab (см. Предыдущую ссылку)
A =
101
0
206
208
Это не строгая копия функции matlab accumarray. Например, функция matlab позволяет выводить vec / mat с размером, определенным sz, который больше, чем размер intrinsec для sub / val duo.
Может быть, это может быть идеей для дополнения к броненосцу API. Предоставление единого интерфейса для разных размеров и типов.