Accumarray函数在c++中的实现

本文介绍了如何在C++中实现类似于MATLAB的Accumarray函数,探讨了函数的基本原理并提供了代码示例。针对大型矩阵,提出了一种更高效的单循环优化方案,同时给出了1D版本的实现,并保证了与MATLAB示例的一致性。文章还讨论了可能扩展Armadillo库API的可能性,以支持不同维度和类型的输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}

The sz input is a two columns vector that contain : num rows / num cols for the output matrix A. The subs matrix is a 2 columns with same num rows of val. Num rows of val is basically sz.rows by sz.cols.

The sz (size) input is not really mandatory and can be deduced easily by searching the max in subs columns.

arma::u32 sz_rows = arma::max(subs.col(0)) + 1;
arma::u32 sz_cols = arma::max(subs.col(1)) + 1;

or

arma::u32 sz_rows = arma::max(subs.col(0)) + 1;
arma::u32 sz_cols = val.n_elem / sz_rows;

the output matrix is now :

arma::mat A (sz_rows, sz_cols);

the accumarray function become :

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;
}

For example :

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 =");

Produce this result :

A = 
    205    0
      0  207
      0  103
    106    0

This example is found here : http://fr.mathworks.com/help/matlab/ref/accumarray.html?requestedDomain=www.mathworks.com except for the indices of subs, armadillo is 0-based indice where matlab is 1-based.

Unfortunaly, the previous code is not suitable for big matrix. Two for-loop with a find in vector in between is really bad thing. The code is good to understand the concept but can be optimized as a single loop like this one :

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;
}

The only change are :

  • init the output matrix with zero's.
  • loop over subs rows to get the output indice(s)
  • accumulate val to output (subs & val are row synchronized)

A 1-D version (vector) of the function can be something like :

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;
}

For testing 1D version :

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 =");

The result is conform with matlab examples (see previous link)

A = 
    101
      0
    206
    208

This is not a strict copy of matlab accumarray function. For example, the matlab function allow to output vec/mat with size defined by sz that is larger than the intrinsec size of the subs/val duo.

Maybe that can be a idea for addition to the armadillo api. Allowing a single interface for differents dimensions & types.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值