Как мне сопоставить положение в размерной матрице с 1D в C ++ во время компиляции? - PullRequest
0 голосов
/ 31 октября 2019

Мне нужно вычислить положение элемента в размерной матрице n внутри одномерного массива во время компиляции. Вкратце, отображение array[i][j][k]....[n] -> array[...], где i,j,k,...,n в {1,2,3} и размерность каждого индекса DIM = 3. Это означает, что в каждой строке и столбце есть 3 элемента.

Моя основная проблема - записать суммирование для n индексов (пакет параметров) в качестве шаблона и использовать constexpr для оценки суммы во время компиляции.

В результате моего исследования в других статьях стека была получена следующая формула для 3 измерения:

    a[i][j][k] -> a[(i*DIM*DIM) + (j*DIM) + k]

Если мы расширим его до n измерений, то получится следующая формула:

    a[i][j][k]....[n] -> a[(n*DIM ^ (indexAmount-1)] +... + (i*DIM*DIM) + (j*DIM) + k].

Кроме того, i написал код для генерации сложений суммы с использованием шаблонов и constexpr, что показано в приведенном ниже коде.

    /**
    * calculates DIM3^(indexPos)
    */
    template<auto count>
    int constexpr multiple_dim(){
        if constexpr (count == 0){
            return 1;
        }else{
            return DIM3 * multiple_dim<count-1>();
        }
    }

    /**
    *
    *calculates addends for the end summation
    * e.g if we have 3 indices i,j,k. j would be at position 2
    * and j = 1. The parameters would be IndexPos = 2, index = 1.

    */
    template<auto indexPos, auto index>
    int constexpr calculate_flattened_index(){
        if constexpr (indexPos == 0){
            return (index-1);
        }else{
            return (index-1) * multiple_dim<indexPos>();
        }
    }

    /**
     * calculates the position of an element inside a
     * nD matrix and maps it to a position in 1D
     * A[i][j]..[n] -> ???? not implemented yet
     * @tparam Args
     * @return
     */
    template<auto ...Args>
    [[maybe_unused]] auto constexpr pos_nd_to_1d(){
     /* maybe iterate over all indices inside the parameter pack?
        const int count = 1;
        for(int x : {Args...}){

        }
        return count;
    */
    }

Пример вывода для элементов внутри 3D Matrix A. A111, A121, A131. Сумма по 3 элементам будет позицией в 1D. Например, A121 -> 0 + 3 + 0 = 3. A111 будет помещен в одномерный массив в array[3].

    std::cout << "Matrix A111" << std::endl;
    //A111
    std::cout << calculate_flattened_index<0 , 1>() << std::endl;
    std::cout << calculate_flattened_index<1 , 1>() << std::endl;
    std::cout << calculate_flattened_index<2 , 1>() << std::endl;
    std::cout << "Matrix A121" << std::endl;
    //A121
    std::cout << calculate_flattened_index<0 , 1>() << std::endl;
    std::cout << calculate_flattened_index<1 , 2>() << std::endl;
    std::cout << calculate_flattened_index<2 , 1>() << std::endl;
    std::cout << "Matrix A131" << std::endl;
    //A131
    std::cout << calculate_flattened_index<0 , 1>() << std::endl;
    std::cout << calculate_flattened_index<1 , 3>() << std::endl;
    std::cout << calculate_flattened_index<2 , 1>() << std::endl;

    Output:
    Matrix A111
    0
    0
    0
    Matrix A121
    0
    3
    0
    Matrix A131
    0
    6
    0

Требуемый вывод может выглядеть следующим образом:

Вызов функции

    pos_nd_to_1d<1,1,1>() //A111 
    pos_nd_to_1d<1,2,1>() //A121 
    pos_nd_to_1d<1,3,1>() //A131 

Вывод:

    0 //0+0+0
    3 //0+3+0
    6 //0+6+0

1 Ответ

1 голос
/ 31 октября 2019

Если я правильно понимаю ... вы выглядите следующим образом

template <auto ... as>
auto constexpr pos_nd_to_1d ()
 { 
   std::size_t  i { 0u };

   ((i *= DIM, i += as - 1u), ...);

   return i;
 }

Или, может быть, вы можете использовать std::common_type, для i,

std::common_type_t<decltype(as)...>  i {};

, но для индексовЯ предлагаю использовать std::size_t (также std::size_t ... as).

Ниже приведен полный пример компиляции

#include <iostream>

constexpr auto DIM = 3u;

template <auto ... as>
auto constexpr pos_nd_to_1d ()
 { 
   std::size_t  i { 0u };

   ((i *= DIM, i += as - 1u), ...);

   return i;
 }


int main ()
 {
   std::cout << pos_nd_to_1d<1u, 1u, 1u>() << std::endl;
   std::cout << pos_nd_to_1d<1u, 2u, 1u>() << std::endl;
   std::cout << pos_nd_to_1d<1u, 3u, 1u>() << std::endl;
 }

- EDIT -

ОП спрашивает

Не могли бы вы объяснить, как работает этот код? Я немного новичок в c ++.

Я лучше умею кодировать, объясняяв любом случае ...

То, что я использовал здесь

   ((i *= DIM, i += as - 1u), ...);
//...^^^^^^^^^^^^^^^^^^^^^^   repeated part

, называется " сложение выражения " (или также "сворачивание" или "сворачивание шаблона"), и является новой функцией C ++ 17 (вы можете получить тот же результат и в C ++ 14 (также C ++ 11, но не constexpr), но менее простым и элегантным способом), который состоит в расширении переменнойПакет шаблонов с оператором.

Например, если вы хотите суммировать индексы, вы можете просто написать

(as + ...);

и выражение станет

(a0 + (a1 + (a2 + (/* etc */))));

Inэтот случай я использовал тот факт, чтозапятая является оператором, поэтому выражение

   ((i *= DIM, i += as - 1u), ...);

становится

   ((i *= DIM, i += a0 - 1u), 
     ((i *= DIM, i += a1 - 1u),
        ((i *= DIM, i += a2 - 1u),
           /* etc. */ )))))

Заметьте, что таким образом первое i *= DIM не используется (поскольку i инициализируется с нуля)но следующие i *= DIM умножают as - 1u правильное количество раз

Так, когда as... равно 1, 2, 1, например, вы получите

  (1 - 1)*DIM*DIM + (2 - 1)*DIM + (1 - 1)
...