-1

After some extensive search I could not find a clear answer to the following issue. When one is to compare the 1D (vector<type>) vs 2D (<vector<vector<type>>>) vector implementation there is obviously a matter of execution time etc. These issues were discussed here: 2D vector vs 1D vector.

However, consider for example matrix multiplication function. If two, for example, 2x2 and 2x2 matrices are multiplied, a 2x2 matrix results. However, one can use also 1D matrices, for example like here (I try to use a clumsy yet mathematical notation; consider all dimensions appropriately matching):

[ 2D matrix, K rows x N columns ] * [ 1D column vector, N elements ] = [ 1D column vector, K elements ]

Basically, 2D vector is multiplied by 1D vector, and results in 1D vector. Another possibility is to use 2D dimensional vector, like this:

[ 2D matrix, K rows x N columns ] * [ 2D matrix, N rows x 1 column ] = [ 2D matrix, K rows x 1 column ]

So only 2D vectors are used. Finally, the implementation may work like this:

[ 2D matrix, K rows x N columns ] * [ 2D matrix, N rows x 1 column ] = [ 1D column vector, K elements ]

where despite input values are 2D vectors, the result is recognized as 1D and as such provided by the function.

The particular implementation is not that important. One can write a function that takes any type of the vector (1D or 2D). Overall, the result is what matters, if such function is to be used frequently in the program. Basically, the questions are what is the most common practise? What is in principle better to use in the program? 2D vectors are more powerful, yet slower. Also, what bothers me, and may make my code chaotic is the decision whether to use 1D vectors

vector<type> vec(N, def_val);

whenever possible, or 2D vectors, which (in case of a 1 dimension) would have, in fact, two possibilities:

vector<vector<type>> vec(N, vector<type>(1, def_val));
vector<vector<type>> vec(1, vector<type>(N, def_val));

Many thanks for comments on this topic.

9
  • 2
    The biggest runtime issue with having a vector of vectors is that your memory is not allocated contiguously. The biggest design issue is that a vector of vectors in NOT a matrix. This is where you really should design a Matrix class, which can contain a std::array<std::array<type>,width>,height> and then you do all the index calculations by hand (or use std::mdspan to do that for you). Benefit you now have a "strong type" so it will be more clear what your code does. Commented Sep 13 at 9:08
  • 5
    Side note, just use a math library. Chances are you will benefit most from hardware acceleration too Commented Sep 13 at 9:09
  • 1
    Note that since C++23 you can overload the Built-in subscript operator for user defined types (option 3) to provide the appearance of a multi-dimensional array (which may be a 1D backing array). Commented Sep 13 at 9:10
  • 1
    I think you're confusing the internal representation with it representing a vector or a matrix. You should write a (2D?) matrix class backed by a 1D vector. Commented Sep 13 at 9:13
  • 4
    the question you ask has no answer. There is not one choice that is "in principle better to use". There are tradeoffs to be made and you pick what fits a given use case. Generally there is no "better" unless you speficy the requrements. Commented Sep 13 at 9:44

1 Answer 1

3

However, consider for example matrix multiplication function. If two, for example, 2x2 and 2x2 matrices are multiplied, a 2x2 matrix results.

Let's walk through this example to see where your misunderstanding is.

We write a class to represent a 2x2 matrix. For now we only focus on the public interface:

 struct Matrix22 {
      double& operator()(int i,int j);
      const double& operator()(int i,int j) const;
 private:
      /*...*/
 };

That's sufficient to implement matrix multiplication:

Matrix22 mult(const Matrix22& a, const Matrix22& b) {
    Matrix22 c;
    c(0,0) = a(0,0)*b(0,0) ...
    // ...
    return c;
}

Code that uses the function can look like this

Matrix22 a,b;
Matrix22 c = mult(a,b);

The important thing to notice here is that apart from Matrix22 private parts, nothing is really missing. We can implement an algorithm to multiply two matrices, we can write code that uses the algorithm, without knowing anything about how the data is actually stored internally.

If you write a function to multiply two matrices you do not write a function that takes a 1D or a 2D std::vector. You write a function that gets two matrices and returns a matrix.

With that out of the way, we can turn to the question on how to store the elements. Just to reiterate: the way you store them should not affect the algorithm nor the code using the algorithm (generally this isn't always simple to achieve, but it is what you should strive for). How you store the elements depends.

You could use std::array:

struct Matrix {
    double& operator()(int i,int j){ return data[i][j]; }
    const double& operator()(int i,int j) const { return data[i][j]; }
    std::array<std::array<double,2>,4> data;
};

You can choose between row-major or column-major by swapping data[i][j] with data[j][i]. You could use a 1d std::array by replacing data[i][j] with data[i+2*j] or data[2*i+j].

You could use a std::vector, but I don't know why you would, because there is no need to resize nor dynamically allocate a 2x2 matrix.

You could use a completely different representation. For example for a sparse matrix you would use an associative container that stores indices together with the elements. For that, the public interface does not have to look any different.

It depends.

I suggest you to take a look at how big algebra libraries do it. Eigen is rather popular.

Sign up to request clarification or add additional context in comments.

3 Comments

Very useful answer. Thanks. I know about various libraries (like Eigen, I used LAPACK in the very past with Fortran). Nevertheless, I try to implement sth by myself to learn for learning purposes :-)
If the answer solved your issue you can consider to mark it as accepted. See: What should I do when someone answers my question?.
the answer is about writing it yourself. Only in the end I refer you to existing libraries because in the limited space of this answer it isnt possible to cover all there is to say about matrices. If you want to get into the topic, you should also read about expression templates.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.