讨论 - 创建矩阵时的数组与向量的向量 - 什么是最实用的选择
Discussion - Arrays vs Vector of Vectors when creating matrices - what is the most practical option?
所以我偶然发现了一本名为"C++年科学计算指南">的C++书的练习
以下是练习:"编写代码,为双精度浮点数 A、B、C 的三个 2 × 2 矩阵动态分配内存,并为 A 和 B 的条目赋值。让 C = A + B.扩展代码,使其计算 C 的条目,然后将 C 的条目打印到屏幕。最后,取消分配内存。同样,检查您是否像上一个练习中一样使用 for 循环正确解除分配了内存。
这引起了我的注意,我尝试使用2D数组来解决这个问题(我能够非常轻松地做到这一点)和 使用向量向量(我失败了)。
我做了很多研究并阅读了一些关于 StackOverFlow 的文章,基本上意见是普遍的 - 在处理矩阵时,选项始终是 2D 数组。
但是知道这里有很多程序员(而且我是C++的新手),我真的很想阅读更多关于这个主题的意见!
PS:这是我尝试使用向量向量创建矩阵的失败尝试的一个小片段:
for (int row{ 0 }; row < 2; row++) { // Create Matrix A - goes through the matrix rows for (int col{ 0 }; col < 2; col++) { // goes through the matrix columns temp.push_back(rand() % 201); // add random number to the temporary vector } matrixA.push_back(temp); } // Outputing for (int row{ 0 }; row < matrixA.size(); row++) { // goes through the matrix rows for (int col{ 0 }; col < matrixA.at(row).size(); col++) { // goes through the vectors inside matrixA cout << matrixA.at(row).at(col) << "t"; } cout << endl; }
这是输出: 在此处输入图像描述
问题的根源在于C++没有为多维提供内置支持 数组,以及
矩阵不是数组数组
因此,即使可以在 C/C++ 中有一个数组的数组,例如在double M[3][3]
中,这不是表示矩阵的好方法。即使在编译时修复的情况下,这可能仍然可以,vector< vector<double> >
方式肯定不好。
为什么:
-
下标语法
M[i][j]
仍然反映了这样一个事实,即这是一个数组数组,而不是一个真正的多维数组(M[i,j]
在 C/C++ 中是合法的,但意味着完全不同的东西)。M[i][j]
语法也与数学用法不匹配。访问矩阵数学家通常 写阿i,j
而不是
(一)j
(对应于
M[i][j]
) -
数学和科学计算的经验表明,大多数时候这两个维度通常同样重要。实际上没有外在和内在的维度。切片通常由行和列完成。更复杂的下标模式(如子矩阵或跨字)也很常见。
-
通常需要扁平化操作,即在线性向量中重新组织矩阵元素。
-
0 x N
或M x 0
矩阵是无法实现的。这适用于静态案例double A[N][M]
,也适用于无法0 x N
表示的vector< vector<double> >
。正确处理空矩阵非常重要。矩阵下标可能会生成空矩阵作为合法大小写。另一方面,矩阵乘法总是需要匹配的内部维度。因此0xN
*NxM
是有效的情况,应该不会触发任何错误。 -
vector< vector<double> >
浪费内存。此外,它比需要的要慢(想想一个 N 大的Nx2
矩阵,这有很多开销)。这同样适用于纯 C 中的double **M
。 -
vector< vector<double> >
与外部线性代数库(如LAPACK等)不兼容 -
vector< vector<double> >
可以是非矩形的,即需要确保所有内部向量具有相同的长度。
矩阵是它自己的实体
这就是线性算法包中经常完成的方式。有差异,但本质上都非常相似。
-
最合理的做法是将多维数组视为其自身的实体。它不是一个数组的数组,即使乍一看似乎是一个好主意。
-
数据存储始终是线性的
-
矩阵元素的访问映射到此线性存储中。其实很简单。要访问
i, j
元素,请使用A[i*lda + j]
与前导尺寸的长度lda
。这并不意味着元素在内存中的某种顺序。这种情况通常称为 C 阶。相反的FORTRAN顺序只是交换i
和j
的含义,或者在更高维度的情况下,颠倒索引的顺序。 -
为了真正通用,nd数组包括
struct ndarray { double *buffer; size_t size[DIMS]; size_t strides[DIMS] };
这可以实现拥有(即对缓冲区拥有所有权)或非拥有矩阵。后者对于实现零开销子矩阵或列/行下标可能很重要。
然后访问矩阵的
i,j
元素 (DIM == 2)buffer[ i*strides[0] + j*strides[1] ]
这很容易推广到更高的维度。
此外,矩阵的转置只需要反转
strides
数组。无需复制。行或列下标、子矩阵或获取跨步子矩阵可以作为零开销操作实现。只需要正确填写
strides
数组并相应地设置buffer
和size
。strides
必须在创建矩阵时初始化属性,具体取决于是使用 C 还是 FORTRAN 布局。
- C++从另一个类访问公共静态向量的正确方法是什么
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- 在 2D 向量中使用第三个 [ ] 有什么意义?
- 讨论 - 创建矩阵时的数组与向量的向量 - 什么是最实用的选择
- 将向量作为类>(值)<向量启动和向量<类>[值]有什么区别
- 将信息输入到下面显示的结构向量中的正确语法/格式是什么
- 有什么方法可以将具有不同模板参数的模板类实例放入向量中?
- 重新分配向量时,向量中的内存会发生什么情况
- 在向量中查找大于 0(或通常为 k)的最小元素的最佳方法是什么?
- 什么是点向量的向量?
- 如何声明一个标准::提升直方图的向量?提升直方图的类型是什么?
- 数组为此合并排序函数提供了正确的输出,但向量给出了不正确的输出.出了什么问题?
- 我可以做些什么来改进指针向量中的此搜索?
- 从列表/向量制作嵌套 for 循环的最佳方法是什么?
- 清除结构向量时会发生什么?
- 返回向量元素的 l 值的正确方法是什么?
- C++中这个声明的向量是什么意思
- C++:将向量传递给函数,然后在main中调用函数.错过了什么
- 将一种数据类型的向量复制到同一数据类型的结构向量中的有效方法是什么
- 使用指针向量和非指针向量有什么区别