简体   繁体   中英

C++: generate multidimensional vector of unknown depth

I have a function in C++ (call it "weightmatrix") that takes a vector of size n as input, which contains a series of numbers a , b , c ..., and returns an n -dimensional vector made up of subvectors of size c, b, a...

That was complicated sounding. Basically, in practice it should look like this:

vector<int> vector_sizes = {2, 3, 1}; 
cout << "Resulting matrix: " << weightmatrix(vector_sizes); // Function takes vector of size 3
/* Returns the following 3 dimensional matrix:
   { { {0, 0}, 
       {0, 0}, 
       {0, 0} }, 
    {0, 0, 0} }
*/

It's a weird one, I know. I just need to generate a vector without knowing beforehand how many dimensions it will be. Any help or advice you could throw in my way would be awesome.

I took the hint of Galik to make a small sample:

#include <cassert>
#include <iostream>
#include <vector>

template <typename ELEM>
class NDArrayT {
  private:
    // dimensions
    std::vector<size_t> _dims;
    // data
    std::vector<ELEM> _data;

  public:
    NDArrayT(const std::vector<size_t> &dims):
      _dims(dims)
    {
      size_t size = _dims.empty() ? 0 : 1;
      for (size_t dim : _dims) size *= dim;
      _data.resize(size);
    }
    NDArrayT(
      const std::vector<size_t> &dims,
      const std::vector<ELEM> &data):
      NDArrayT<ELEM>(dims)
    {
      assert(_data.size() == data.size());
      std::copy(data.begin(), data.end(), _data.begin());
    }

    ELEM& operator[](const std::vector<size_t> &indices)
    {
      size_t i = 0, j = 0;
      for (size_t n = _dims.size(); j < n; ++j) {
        i *= _dims[j]; i += indices[j];
      }
      return _data[i];
    }

    const ELEM& operator[](const std::vector<size_t> &indices) const
    {
      size_t i = 0, j = 0;
      for (size_t n = _dims.size(); j < n; ++j) {
        i *= _dims[j]; i += indices[j];
      }
      return _data[i];
    }
};

using namespace std;

ostream& operator<<(ostream &out, const vector<size_t> &values)
{
  const char *sep = "";
  for (size_t value : values) {
    out << sep << value; sep = ", ";
  }
  return out;
}

bool inc(vector<size_t> &indices, const vector<size_t> &dims)
{
  for (size_t i = indices.size(); i--;) {
    if (++indices[i] < dims[i]) return false;
    indices[i] = 0;
  }
  return true; // overflow
}

int main()
{
  // build sample data
  vector<double> data(2 * 3 * 4);
  for (size_t i = data.size(); i--;) data[i] = (double)i;
  // build sample array
  typedef NDArrayT<double> NDArrayDouble;
  const vector<size_t> dims = { 2, 3, 4 };
  NDArrayDouble a(dims, data);
  // print sample array (check subscript)
  vector<size_t> indices(dims.size(), 0);
  do {
    cout << "a[" << indices << "]: " << a[indices] << endl;
  } while (!inc(indices, dims));
  // done
  return 0;
}

Compiled and tested on ideone . Output is:

a[0, 0, 0]: 0
a[0, 0, 1]: 1
a[0, 0, 2]: 2
a[0, 0, 3]: 3
a[0, 1, 0]: 4
a[0, 1, 1]: 5
a[0, 1, 2]: 6
a[0, 1, 3]: 7
a[0, 2, 0]: 8
a[0, 2, 1]: 9
a[0, 2, 2]: 10
a[0, 2, 3]: 11
a[1, 0, 0]: 12
a[1, 0, 1]: 13
a[1, 0, 2]: 14
a[1, 0, 3]: 15
a[1, 1, 0]: 16
a[1, 1, 1]: 17
a[1, 1, 2]: 18
a[1, 1, 3]: 19
a[1, 2, 0]: 20
a[1, 2, 1]: 21
a[1, 2, 2]: 22
a[1, 2, 3]: 23

The "arithmetic" to manage multi-dimensional arrays in contiguous memory is actually quite simple. I guess, the most "revolutionary" idea of this sample is the operator[]() which uses a std::vector<size_t> to provide the indices for each dimension.

While I was writing this down, a lot of alternatives for indexing came in my mind. – There is much space for fantasy...

Eg for linear (one-dimensional) access, a second operator[] for size_t might be provided as well.

Here is a solution using a template MultiVector class that returns a MultiVectorView from it's operator[] .

The underlying data is stored in a plain std::vector but can be accessed with the vec[x][y][z] syntax.

There is no checking for correct usage, and only very basic functionality is implemented but it gives an idea how it could be done.

#include <iostream>
#include <vector>

template <typename T>
class MultiVector;

template <typename T>
class MultiVectorView {
public:
    MultiVectorView(MultiVector<T>& vec_, int index_, int dimension_) : vec(vec_), index(index_), dimension(dimension_) {}

    MultiVector<T>& vec;
    int index;
    int dimension;

    MultiVectorView& operator[](std::size_t n_index) {
        int index_multiplyer = 1;
        for (int i=0; i<dimension; ++i)
            index_multiplyer *= vec.dimensions[i];
        index += n_index*index_multiplyer;
        dimension++;
        return *this;
    }

    operator T() {
        return vec.content[index];
    }

    MultiVectorView& operator=(T val) {
        vec.content[index] = val;
        return *this;
    }
};

template <typename T>
class MultiVector {
public:
    MultiVector(std::vector<int> dimensions_) : dimensions(dimensions_) {
        int size = dimensions[0];
        for (int i = 1; i<dimensions.size(); ++i)
            size *= dimensions[i];

        content.resize(size);
    }

    MultiVectorView<T> operator[](std::size_t index) {
        return MultiVectorView<T>(*this, index, 1);
    }

    std::vector<T> content;
    std::vector<int> dimensions;
};

int main() {
    std::vector<int> v = {2,3,2};
    MultiVector<int> test(v);

    int tmp = 0;
    for (int x = 0; x < v[0]; ++x)
        for (int y = 0; y < v[1]; ++y)
            for (int z = 0; z < v[2]; ++z) {
                test[x][y][z] = tmp++;
            }

    for (int i=0; i<test.content.size(); ++i)
        std::cout << std::endl << test.content[i] << " ";

    int b = test[1][2][1];

    std::cout << std::endl << "b = " << b << std::endl << "test[0][1][1] = " << test[0][1][1] << std::endl;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM