我有一个带有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矩阵的最后一个维度。
注意:我宁愿不依赖第三方库来执行此操作。
答案 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 ...矩阵的任意尺寸大小的包含外壳。在模板参数列表中,它期望使用单个Type
:int
,float
,char
,user 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矩阵时,表示法变得更加困难。但是,这种模板方法确实使代码更具通用性和可读性,可以独立地包含在单个类中,而不是针对各种矩阵大小具有许多不同的矩阵类。