在C / C ++中是否有一个accumarray()的例子

时间:2012-01-21 05:03:59

标签: c++ c matlab

我们正在尝试理解MATLAB的准确性函数,想为我们的理解编写相同的C / C ++代码。有人可以用样本/伪代码帮助我们吗?

4 个答案:

答案 0 :(得分:2)

根据the documentation

  

该函数按如下方式处理输入:

     
      
  1. 找出潜艇中有多少独特的指数。每个唯一索引定义输出数组中的bin。最大索引值   subs确定输出数组的大小。

  2.   
  3. 找出每个索引重复的次数。

  4.   
  5. 这决定了在输出数组的每个bin中将累积多少个val元素。

  6.   
  7. 创建输出数组。输出数组的大小为max(subs)或大小为sz。

  8.   
  9. 使用subs中的索引值将val中的条目累积到bin中,并对每个bin中的条目应用fun。

  10.   
  11. 在输出中填写未被subs引用的位置。默认填充值为零;使用fillval设置不同的   值。

  12.   

因此,转换为C ++(这是未经测试的代码),

template< typename sub_it, typename val_it, typename out_it,
          typename fun = std::plus< typename std::iterator_traits< val_it >::value_type >,
          typename T = typename fun::result_type >
out_it accumarray( sub_it first_index, sub_it last_index,
                   val_it first_value, // val_it last_value, -- 1 value per index
                   out_it first_out,
                   fun f = fun(), T fillval = T() ) {
    std::size_t sz = std::max_element( first_index, last_index ); // 1. Get size.
    std::vector< bool > used_indexes; // 2-3. remember which indexes are used

    std::fill_n( first_out, sz, T() ); // 4. initialize output

    while ( first_index != last_index ) {
        std::size_t index = * first_index;
        used_indexes[ index ] = true; // 2-3. remember that this index was used
        first_out[ index ] = f( first_out[ index ], * first_value ); // 5. accumulate
        ++ first_value;
        ++ first_index;
    }

    // If fill is different from zero, reinitialize untouched values
    if ( fillval != T() ) {
        out_it fill_it = first_out;
        for ( std::vector< bool >::iterator used_it = used_indexes.begin();
              used_it != used_indexes.end(); ++ used_it ) {
            if ( * used_it ) * fill_it = fillval;
        }
    }

    return first_out + sz;
}

这有一些缺点,例如,对整个列向量重复调用累加函数而不是一次。输出放在first_out引用的预分配存储中。索引向量必须与值向量的大小相同。但是应该很好地捕获大部分功能。

答案 1 :(得分:1)

非常感谢您的回复。我们能够在C ++中完全理解和实现它(我们使用armadillo)。这是代码:

colvec TestProcessing::accumarray(icolvec cf, colvec T, double nf, int p)
{
    /* ******* Description   *******

    here cf is the matrix of indices

    T is the values whose data is to be
    accumulted in the output array S.

    if T is not given (or is scaler)then accumarray simply converts
    to calculation of histogram of the input data

    nf is the the size of output Array

    nf >= max(cf)
    so pass the argument accordingly

    p is not used in the function 

    ********************************/

    colvec S; // output Array 

    S.set_size(int(nf)); // preallocate the output array 

    for(int i = 0 ; i < (int)nf ; i++)
    {
        // find the indices in cf corresponding to 1 to nf
        // and store in unsigned integer array q1 
        uvec q1 = find(cf == (i+1));
        vec q ;
        double sum1 = 0 ;

        if(!q1.is_empty())
        {
            q = T.elem(q1) ; // find the elements in T having indices in q1 
            // make sure q1 is not empty 

            sum1 = arma::sum(q); // calculate the sum and store in output array 
            S(i) = sum1;
        }

        // if q1 is empty array just put 0 at that particular location
        else
        {
            S(i) = 0 ;
        }
    }
    return S;
}

希望这对其他人也有帮助! 再次感谢所有贡献者:)

答案 2 :(得分:0)

这就是我想出的。注意:我追求可读性(因为你想要最好地理解),而不是进行优化。哦,我从来没有使用过MATLAB,我刚才看到了这个样本:

val = 101:105;
subs = [1; 2; 4; 2; 4]
subs =
    1 
    2 
    4 
    2 
    4 

A = accumarray(subs, val)
A =
   101       % A(1) = val(1) = 101
   206       % A(2) = val(2)+val(4) = 102+104 = 206
     0       % A(3) = 0
   208       % A(4) = val(3)+val(5) = 103+105 = 208

无论如何,这是代码示例:

#include <iostream>
#include <stdio.h>
#include <vector>
#include <map>

class RangeValues
{   
public:
    RangeValues(int startValue, int endValue)
    {
        int range = endValue - startValue;

        // Reserve all needed space up front
        values.resize(abs(range) + 1);

        unsigned int index = 0;
        for ( int i = startValue; i != endValue; iterateByDirection(range, i), ++index )
        {
            values[index] = i;
        }
    }

    std::vector<int> GetValues() const { return values; }

private:
    void iterateByDirection(int range, int& value)
    {
        ( range < 0 ) ? --value : ++value;
    }

private:
    std::vector<int> values;
};

typedef std::map<unsigned int, int> accumMap;

accumMap accumarray( const RangeValues& rangeVals )
{
    accumMap aMap;
    std::vector<int> values = rangeVals.GetValues();

    unsigned int index = 0;
    std::vector<int>::const_iterator itr = values.begin();
    for ( itr; itr != values.end(); ++itr, ++index )
    {
        aMap[index] = (*itr);       
    }
    return aMap;
}

int main()
{
    // Our value range will be from -10 to 10
    RangeValues values(-10, 10);

    accumMap aMap = accumarray(values);

    // Now iterate through and check out what values map to which indices.
    accumMap::const_iterator itr = aMap.begin();
    for ( itr; itr != aMap.end(); ++itr )
    {
    std::cout << "Index: " << itr->first << ", Value: " << itr->second << '\n';
    }

//Or much like the MATLAB Example:
cout << aMap[5]; // -5, since out range was from -10 to 10

}

答案 3 :(得分:0)

除了Vicky Budhiraja&#34;犰狳&#34;例如,这是一个使用与matlab函数类似的语义的精确2D版本:

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输入是一个两列向量,包含:输出矩阵A的num rows / num cols。子矩阵是2列,具有相同的num行val。 num行的val基本上是sz.rows由sz.cols。

sz(大小)输入并不是必需的,可以通过搜索子列中的max来轻松推断。

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

准确性函数变为:

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-loop在中间的向量中找到是非常糟糕的事情。代码很好理解这个概念,但可以像这样一个循环优化:

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

唯一的变化是:

  • 使用零&lt; li>初始化输出矩阵。
  • 循环子行以获取输出indice(s)
  • 累积val到输出(subs&amp; 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函数允许输出尺寸由sz定义的vec / mat,该尺寸大于sub / val duo的intrinsec尺寸。

也许这可以成为添加到犰狳api的想法。允许单个界面用于不同尺寸和尺寸。类型。