c++ - 初始化一个未知维度的数组

标签 c++ multidimensional-array

我很惊讶我找不到这个问题。我试图将它(使用一些不错的未经测试的代码)概括为每个人都可以从中受益的东西。

假设我有一个多维:

template <int dims> class Point { public: double data[dims]; };

现在我创建一个多维数组:

 template <int dims> void foobar(int count0, ...) {
    //Using variadic function.  Could also use variadic templates in C++ (arguably better)
    int counts[dims], total_count=count0; counts[0]=count0;
    va_list args; va_start(args,count0);
    for (int i=1;i<dims;++i) {
        int count = va_arg(args,int);
        counts[i] = count;
        total_count *= count;
    }
    va_end(args);

    Point<dims>* array = new Point<dims>[total_count];

    //...
}

如您所见,array 是未知维度的多维数组,以一维数组表示。


我的问题:如何干净地将该数组初始化为其多维网格点?

这是我想要的 1、2 和 3 维示例行为。显然,我不想为我可能想要使用的每一个可能的维度写这个!目标是概括这一点。

//Example: dim==1
for (int x=0; x<counts[0]; ++x) {
    Point<1>& point = array[x];
    point.data[0] = (x+0.5) / (double)counts[0];
}

//Example: dim==2
for (int y=0; y<counts[1]; ++y) {
    for (int x=0; x<counts[0]; ++x) {
        Point<2>& point = array[y*counts[0]+x];
        point.data[0] = (x+0.5) / (double)counts[0];
        point.data[1] = (y+0.5) / (double)counts[1];
    }
}

//Example: dim==3
for (int z=0; z<counts[2]; ++z) {
    for (int y=0; y<counts[1]; ++y) {
        for (int x=0; x<counts[0]; ++x) {
            Point<3>& point = array[(z*counts[1]+y)*counts[0]+x];
            point.data[0] = (x+0.5) / (double)counts[0];
            point.data[1] = (y+0.5) / (double)counts[1];
            point.data[2] = (z+0.5) / (double)counts[2];
        }
    }
}

同样,我的问题是:以一种干净的方式将以上内容概括为任意数量的嵌套循环/维度。

注意:我提出了一些令人讨厌的方法,它们既不优雅又缓慢。特别是,如果可能的话,我想避免递归,因为这将非常频繁地在高维小型数据集上调用。 注意:在 C 中有明显的相似之处,因此 C 或 C++ 都可以。首选 C++11。

最佳答案

编辑以回应评论和更新的问题

如果您需要性能和“优雅”,我会:

  • 放弃您的多维数组方法并将其展平(即一维数组)。没有 new,没有指针,使用带有 std::vectorstd::array 的 C++ 现代方法。
  • 使用通用嵌套循环“生成器”等方便的方法为您的多维数组提供一个抽象容器
  • 用固定大小的数组替换您的可变参数(因为您在编译时知道 dims

所以我找到了以下解决方案,它与您的实现和需求非常一致,并尽量保持简单。

我设法以“现代 C++11 方式”重写了一个小的 MultiArray 类。我在这里考虑 count 维度在编译时可能是未知的,因此现在使用 std::vector。当然可以使用 std::array 获得更通用的编译时代码,请参阅下面我的原始答案。

#include <iostream>
#include <array>
#include <vector>
#include <numeric>

template<size_t DIMS>
class MultiArray {
public:
    // Point here is just an array
    using Point = std::array<double,DIMS>;

    // fill data_ with an init array
    // not that count is just a fix sized array here no variadic arguments needed
    MultiArray(const std::array<size_t, DIMS>& count) 
        : data_{init_array(count)} {}

private:   
    // the init functions are used for the constructor
    void init_point(Point& point, const std::array<size_t,DIMS>& coord, const std::array<size_t, DIMS>& count) {
        std::cout << " -> { ";
        for (size_t i = 0; i < DIMS; i ++) {
            point[i] = (coord[i] + 0.5) / count[i];
            std::cout << point[i] << ";";
        }
        std::cout << " }\n";
    }

    std::vector<Point> init_array(const std::array<size_t, DIMS>& count) {
        std::vector<Point> data(std::accumulate(count.begin(), count.end(), 1, std::multiplies<int>())); // accumulate computes the prod of DIMS total_count
        std::array<size_t, DIMS> current{};
        size_t i=0;
        do {
             for (size_t i = 0; i < DIMS; i ++)
                 std::cout << current[i] << ";";
            init_point(data[i++],current,count);
        } while (increment(current, count));
        return data;
    }

    // the following function allows to imitate the nested loop by incrementing multidim coordinates
    bool increment( std::array<size_t, DIMS>& v, const std::array<size_t, DIMS>& upper) {
        for (auto i = v.size(); i-- != 0; ) {
            ++v[i];
            if (v[i] != upper[i]) {
                return true;
            }
            v[i] = 0;
        }
        return false;
    }   
private:
    std::vector<Point> data_; // A flatten multi dim vector of points
};


int main() {
   std::array<size_t, 3> count{{4,5,3}};
   MultiArray<3> test{count};
}

Live on Coliru

正如您在结果中看到的,data_ 可以针对 N 维度进行一般初始化。如果您需要更高级别的抽象类,您可以在我的原始答案下方查看,您可以在其中执行一些方便的操作(即访问 grid[{i,j,k}] 以填充值)。

原始答案

我需要一个多维网格来满足我的需要,碰巧在 code review 上要求改进我的代码.这是一个工作 example当然,有些功能对您来说可能是不必要的……我的实现与模板和编译时计算有关。请注意,维度大小必须在编译时已知。

简而言之,这个类看起来像这样:

template<typename T, size_t... DIMS> // variadic template here for the dimensions size
class MultiGrid {
   // Access from regular idx such as grid[64]
   T& operator[] (size_type idx)       { return values_[idx]; };
   // Access from multi dimensional coordinates such as grid[{6,3,4}]
   T& operator[] (const std::array<size_t,sizeof...(DIMS)>& coord)       { // can give code for runtime here };
private:
  std::array<T,sizeof...(DIMS)> data_;
}

然后你可以构造你的多维数组并以这些方式初始化它:

MultiGrid<float,DIM1,DIM2,DIM3> data; // 3D
// MultiGrid<float,DIM1,DIM2,DIM3,DIM4> data; // 4D
// etc...

// initialize it like this with nested arrays
for (size_t z=0; z < DIM3; z ++)
  for (size_t y=0; y < DIM2; y ++)
    for (size_t x=0; x < DIM1; x ++)
      data[{x,y,z}] = [...] // whatever

// or like this in C++11/14 way
for (auto &x : data) x = [...] // this is convenient to provide a container like approach since no nested arrays are needed here.

如果您需要为可变嵌套循环指定一个算法来填充值,您可以查看 here并用第一个答案这样做:

// here lower_bound is 0-filled vector
std::vector<int> current = lower_bound;
do {
   data[current] = [...] // fill in where current is a coordinate
} while (increment(current, lower_bound, upper_bound));

如果您需要我在实现过程中遗漏的内容,请随时提出。如果有人可以指出改进之处,我也会很高兴。

关于c++ - 初始化一个未知维度的数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32320753/

相关文章:

c++ - 匿名结构作为返回类型

java - 我必须找到两个数组多维和锯齿状数组之间的共同元素

c - 如何通过 qsort 对 C 中的 3D 字符数组进行排序

c++ - 如何使用 cout 格式化输出

c++ - 数组结构

c++ - 如何为一组对象重载运算符

c++ - 当我调用工作簿的 PrintPreview() 方法时出现 Com 错误 800a03ec

c++ - 动态二维数组丢失第二维

java - 无法将sharedPreferance中保存的JSON格式字符串转换为多维数组

php - 将数组和多维数组的组合插入到 MySQL 表中