【问题标题】:How do I map a position in a n dimensional matrix to 1D, in C++ at compile time?如何在编译时在 C++ 中将 n 维矩阵中的位置映射到 1D?
【发布时间】:2019-10-31 13:06:11
【问题描述】:

我必须在编译期间计算一维数组内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 矩阵 A 内元素的示例输出。 A111A121A131。 3 个元素的总和将是 1D 中的位置。例如A121 -&gt; 0 + 3 + 0 = 3A111 将被放置在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

【问题讨论】:

    标签: templates matrix c++17 variadic-templates template-meta-programming


    【解决方案1】:

    如果我理解正确的话……你的样子如下

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

    -- 编辑--

    OP 询问

    你能解释一下这段代码是如何工作的吗?我对 c++ 有点陌生。

    无论如何,我更擅长编写代码来解释......

    我在这里用过的

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

    被称为“fold expression”(或也称为“折叠”或“模板折叠”),是 C++17 的一项新功能(您也可以在 C++14 中获得相同的结果(也是 C++ 11 但不是constexpr),但以一种不太简单和优雅的方式)包括使用运算符扩展可变参数模板包。

    例如,如果你想对索引求和,你可以简单地写

    (as + ...);
    

    表达式变成了

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

    在这种情况下,我使用了逗号是运算符这一事实,所以表达式

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

    【讨论】:

    • 如果你有空闲时间,你能解释一下这段代码是如何工作的吗?我对c++有点陌生。
    • @M.Mac - 答案改进;希望这会有所帮助。
    猜你喜欢
    • 2011-05-05
    • 1970-01-01
    • 2012-12-10
    • 2016-08-30
    • 2014-05-21
    • 1970-01-01
    • 2019-04-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多