使用嵌套的std :: vector在C ++中实现3D矩阵

时间:2018-12-05 19:41:32

标签: c++ c++11 matrix

我有一个带有MxNxL元素的3D矩阵。 M和N在编译时都是已知的。而L是可变的,取决于用户的输入。但是它将在程序开始时提供,并且在程序的生命周期内永远不会改变。我想在C ++ 11中实现此Matrix,并希望为用户提供灵活性,使其可以沿矩阵的第1维和第2维旋转3D Matrix。

我想知道实现此矩阵的最佳和最有效的设计选择是什么?

我已经看到了以下使用std::vector的解决方案。使用std::vector,用户可以使用std::rotate旋转任意尺寸。解决方案来自于此thread。但是,vsoftco提到使用嵌套向量不利于使其线性化。但是由于我需要跨维度旋转,因此使用线性数组会使处理变得困难。

#include <vector>

#define M_SIZE 360
#define N_SIZE 180 

template<typename T>
using vec = std::vector<T>;

int main()
{
    uint16_t L;
    std::cin << L;
    vec<vec<vec<double>>> v{M_SIZE, vec<vec<double>>{N_SIZE, vec<double>{L}}};
}

同样,可以使用动态c数组,但是std::rotate仅适用于3D矩阵的最后一个维度。

注意:我宁愿不依赖第三方库来执行此操作。

1 个答案:

答案 0 :(得分:2)

我不知道这是否可以帮助您实现所追求的目标,但这是我开始使用现代C ++功能构建的Matrix类。例如variadic templates。 Matrix类自身包含在单个头文件中。

#ifndef MATRIX_H
#define MATRIX_H

#include <cstddef>  // std::size_t
#include <numeric>  // std::accumulate
#include <vector>

namespace /*Your namespace name here*/ {

template<typename Type, std::size_t... Dims>
class Matrix {
public:
    static const std::size_t numDims_ = sizeof...(Dims);

private:
    std::size_t numElements_;

    std::vector<Type> elements_;
    std::vector<std::size_t> strides_;

public:
    Matrix() noexcept;

    template<typename... Args>
    Matrix(Args&&... args) noexcept;

    const Type& operator[](std::size_t idx) const;

    std::size_t numElements() const { return elements_.size(); }

    const std::vector<std::size_t>& strides() const { return strides_; }
    const std::vector<Type>& elements() const { return elements_; }
};

template<typename Type, std::size_t... Dims>
Matrix<Type, Dims...>::Matrix() noexcept :
    strides_({ Dims... }) {
    using std::begin;
    using std::end;
    auto mult = std::accumulate(begin(strides_), end(strides_), 1, std::multiplies<>());
    numElements_ = mult;
    elements_.resize(numElements_);
}

template<typename Type, std::size_t... Dims>
template<typename... Args>
Matrix<Type, Dims...>::Matrix(Args&&... args) noexcept :
    elements_({ args... }),
    strides_({ Dims... }) {
    numElements_ = elements_.size();
}

template<typename T, std::size_t... d>
const T& Matrix<T, d...>::operator[](std::size_t idx) const {
    if (idx > numElements_)
        throw std::runtime_error("Invalid index");
    return elements_[idx];
}

} // Your namespace name here
#endif MATRIX_H

以及一个使用它的小型示例程序:

#include <iostream>
#include <exception>

#include "Matrix.h"

int main() {    
    try {
        using /*your namespace name here*/;
        Matrix<double, 2, 2, 2> mat( 1.0 ,1.1, 1.2, 1.3,
                                     1.4, 1.5, 1.6, 1.7 );

        // Print the elements from the use of getting the vector
        for (auto& e : mat.elements()) {
            std::cout << e << " ";
        }
        std::cout << '\n';

        // Print the elements from the use of using operator[]
        for ( std::size_t n = 0; n < mat.numElements(); n++ ) {
            std::cout << mat[n] << " ";
        }
        std::cout << '\n';

        // Print out the strides
        std::cout << "Number of strides: " << mat.numDims_ << '\n';
        for (auto& s : mat.strides()) {
            std::cout << s << " ";
        }
        std::cout << '\n';

    } catch ( std::exception& e ) {
        std::cerr << e.what();
        return EXIT_FAILURE;
    }    
    return EXIT_SUCCESS;
}

-输出-

 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7
 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7
 Number of strides: 3
 2 2 2

该类还远远不够完整,因为它只是任何MxNx ... Zx ...矩阵的任意尺寸大小的包含外壳。在模板参数列表中,它期望使用单个Typeintfloatcharuser defined等。{之后的tempale参数的可变参数{1}}确定此矩阵有多少个维,以及每个维的维大小。例子:

Type

使用高维矩阵时,很难对其进行可视化,但是可以使用Matrix<type,1> = a 1 matrix which in essence would be a scalar Matrix<type,1,2> = a 1x2 matrix and would be considered a vector Matrix<type,3,1> = a 3x1 matrix and would be considered a vector Matrix<type,2,2> = a 2x2 matrix Matrix<type,3,4> = a 3x4 matrix Matrix<type,3,3,3> = a 3x3x3 Matrix (3D-Matrix) and has 27 elements Matrix<type,3,4,5,3,2> = a 3x4x5x3x2 (5D - Matrix) and has 360 elements // The number of elements will always equal the product of all of the strides. // When creating an arbitrary matrix size; careful consideration needs to be taken // when it comes to how many dimensions and the size of that dimension. Even lower // dimensional matrices can explode in the amount of elements... Matrix<type, 128, 356, 242> = a 128x356x242 (3D - Matrix) but has 11,027,456 elements // It is the Matrix's user defined variadic constructor that the number of arguments // in the parameter list that has to equal the total amount of elements defined by // the product of all of its strides. 向量轻松地对其进行管理。我们可以将它们用于正确的索引编制。

这种设计方法具有挑战性的方面是能够实现实际的算术运算符。为此,如果我们有两个具有不同步幅的不同矩阵,它们将被视为不同类型。当涉及到矩阵乘法时,事情变得更加困难,因为矩阵A和矩阵B的边缘必须具有相同的大小。例如:4x2 * 2x3将得出4x3矩阵。而这只是2D矩阵问题,进入3D,4D,... ND矩阵时,表示法变得更加困难。但是,这种模板方法确实使代码更具通用性和可读性,可以独立地包含在单个类中,而不是针对各种矩阵大小具有许多不同的矩阵类。