【问题标题】:How to generalize plain 2D arrays?如何泛化普通的二维数组?
【发布时间】:2015-05-13 02:01:07
【问题描述】:

对于 C++,我看到了一些使用 2D 数组的建议,如下所示:

int* theArray = new int[d1 * d2];

然后可以按如下方式访问元素 (i, j):

int myInt = theArray[i + d2 * j];

我有两个问题:第一,如何访问 3D 数组?有通用公式吗?其次,更重要的是,如果我使用函数会不会有性能损失

int getNumber(int i, int j, int k) 
void setNumber(int i, int j, int k, int theValue)

检索和设置号码?我不想在代码中的某个地方搞砸它,然后永远寻找搞砸的地方。

【问题讨论】:

  • 索引是i + d2*j + d2*d3*k,但这是一道基本的数学题,不是编程题。
  • 顺便说一句,这取决于尺寸顺序。
  • 因为这被标记为array,所以我只需要回答:使用std::array,如果需要运行时大小设置,std::vector。只有在分析之后才能进一步优化。

标签: c++ arrays optimization


【解决方案1】:

推广到三个维度并不难:

int* theArray = new int[d1*d2*d3];

并使用它访问它

int myInt = theArray[i+d1*(j+d2*k)];

就像十进制系统一样。当您将 123 写为 3+10*2+100*3 = 3 + 10*(2+10*3) 时。其中 10 是每个维度的大小。请注意,我已将您的 d2 更改为 d1,因为缩进的是与索引 i 关联的维度的大小。

关于你的第二个问题,调用的函数会降低性能。但是你可以通过inlining 函数来避免这个问题,或者定义一个macro,就性能而言,这相当于根本没有函数调用。

【讨论】:

    【解决方案2】:

    我会说对于二维数组,假设 i 是第一个索引,j 是第二个索引,最好像这样进行索引

    int myInt = theArray[i*d2 + j];
    

    因此更改最后一个索引会为您提供连续的范围。这就是 C 和 C++ 多维数组的实现方式。

    所以如果你的 3D 数组是

    int* theArray = new int[d1*d2*d3];
    

    那么你可以这样访问它:

    int myInt = theArray[(i*d2 + j)*d3 + k];
    

    关于访问器函数 - 如果它们是内联的,则开销将为零。您可能还希望将这一切包装到您的自定义数组类中(但这可能是您已经打算做的)

    【讨论】:

      【解决方案3】:

      要了解如何以更一般的方式解决此问题,您可以查看boost multi_array

      【讨论】:

        【解决方案4】:

        我会选择std::array,而不是进行任何手动索引计算。只需确保最后一个索引是您最有可能循环的索引。 std::array 和 [] 一样快。

        #include <iostream>
        #include <array>
        
        int main() {
        
            constexpr size_t a=5;
            constexpr size_t b=4;
            constexpr size_t c=3;
        
            std::array<std::array<std::array<float,a>, b>, c> arr3d;
        
            for(auto& outer : arr3d){
              for(auto& row : outer){
                for(auto& place : row){
                  place=3.5;
                }
              }
            }
        
            for(size_t i=0;i<a; ++i){
              for(size_t j=0;j<b; ++j){
                for(size_t k=0;k<c; ++k){
                  arr3d[i][j][k]=k; // or whatever
                }
              }
            }
        
            std::cout << arr3d[0][1][2] << std::endl;
        }
        

        如果您需要大小的运行时灵活性,请使用std::vector

        template<typename T> using Vec = std::vector<T>;    
        Vec<Vec<Vec<float>>> arr3d(a,Vec<Vec<float>>(b,Vec<float>(c,0)));
        

        但它的初始化速度会明显变慢,并且取决于您如何使用它(以及 a、b、c 的值)访问也会变慢。 (注意 cmets)。

        其他初始化数组的方法:Other ways to initialize the array

        【讨论】:

        • 你假设大小在编译时是已知的。
        • 向量向量的向量将是堆分配的指针数组,指向堆分配的指针数组,指向堆分配的对象数组。如果按照 Tony 的回答使用平面数组,则只有一个堆分配。
        猜你喜欢
        • 2010-09-18
        • 1970-01-01
        • 2017-07-03
        • 2013-10-31
        • 1970-01-01
        • 2011-12-08
        • 1970-01-01
        • 1970-01-01
        • 2014-01-11
        相关资源
        最近更新 更多