Есть ли пример для accumarray () в C / C ++ - PullRequest
2 голосов
/ 21 января 2012

Мы пытаемся понять точную функцию MATLAB, хотели написать код C / C ++ для того же самого для нашего понимания. Может ли кто-нибудь помочь нам с примером / псевдокодом?

Ответы [ 4 ]

2 голосов
/ 21 января 2012

Согласно документации ,

Функция обрабатывает ввод следующим образом:

  1. Узнайте, сколько уникальных индексов в сабвуферах. Каждый уникальный индекс определяет ячейку в выходном массиве. Максимальное значение индекса в subs определяет размер выходного массива.

  2. Узнайте, сколько раз повторяется каждый индекс.

  3. Это определяет, сколько элементов значений будет накапливаться в каждом бине в выходном массиве.

  4. Создать выходной массив. Выходной массив имеет размер max (subs) или размер sz.

  5. Суммируйте записи в значениях в ячейках, используя значения индексов в подпрограммах, и применяйте забавные значения к записям в каждой ячейке.

  6. Заполните значения в выходных данных для позиций, не указанных подпунктами. Значение по умолчанию - ноль; используйте fillval для установки другого значение.

Итак, перевод на C ++ (это непроверенный код), * ​​1035 *

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 голос
/ 21 января 2012

Большое спасибо за ваш ответ. Мы смогли полностью понять и реализовать то же самое в C ++ (мы использовали броненосец ). Вот код:

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

Надеюсь, это поможет и другим! Еще раз спасибо всем, кто внес вклад:)

0 голосов
/ 13 октября 2016

В дополнение к примеру "броненосца" Вики Будхираджи, это 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. Предоставление единого интерфейса для разных размеров и типов.

0 голосов
/ 21 января 2012

Вот что я придумал.Примечание: я пошел на удобочитаемость (поскольку вы хотели лучше понять), а не на оптимизацию.О, и я никогда не использовал 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

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...