在 C++ 中迭代具有可变维度大小的 N 维矩阵
Posted
技术标签:
【中文标题】在 C++ 中迭代具有可变维度大小的 N 维矩阵【英文标题】:Iterating over an N-dimensional matrix with variable dimension sizes in C++ 【发布时间】:2019-05-08 14:22:30 【问题描述】:我正在尝试迭代一个 n 维矩阵,其中矩阵的每个维度也具有可变大小。
我特别希望每个元素都具有多维坐标,例如 for loop
提供 i
以访问数组中的元素。
我想基本上重新创建一个嵌套 for 循环,如下面的代码,我可以在其中将任何不同类型的数字放入 dimensionSize
。
std::vector<int> dimensionSize = 1, 4, 4;
for (int i = 0; i < dimensionSize [0]; i++)
for (int j = 0; j < dimensionSize [1]; j++)
for (int k = 0; k < dimensionSize [2]; k++)
std::cout << i << " " << j << " " << k << std::endl;
这个嵌套循环的结果是……
0 0 0
0 0 1
0 0 2
0 0 3
0 1 0
0 1 1
0 1 2
0 1 3
0 2 0
0 2 1
0 2 2
0 2 3
0 3 0
0 3 1
0 3 2
0 3 3
到目前为止,我在 C++ 中遍历 n 维矩阵时发现的所有示例/解决方案都是非常混乱的代码,而且对于固定维度大小,例如9x9x9 或 3x3x3。
但是我的矩阵可以是 1x256x513x3 或各种尺寸。
我目前有以下功能,它接近于我想要的,但是它有一个问题。
void iterate_over_n_dimensions (std::vector<int>& counters,
std::vector<int> dimensionSizes,
int currentDimension)
for (long i = 0; i < dimensionSizes [currentDimension]; i++)
print_counter (counters);
if (currentDimension + 1 != dimensionSizes.size())
iterate_over_n_dimensions (counters,
dimensionSizes,
currentDimension + 1);
counters [currentDimension] ++;
counters [currentDimension] %= dimensionSizes [currentDimension];
运行以下...
int main(int argc, const char * argv[])
std::vector<int> dimensionSizes = 1, 4, 4;
std::vector<int> counters(dimensionSizes.size(), 0);
iterate_over_n_dimensions (counters, dimensionSizes, 0);
return 0;
这个递归函数的结果是……
0 0 0
0 0 0
0 0 0
0 0 1
0 0 2
0 0 3
0 1 0
0 1 0
0 1 1
0 1 2
0 1 3
0 2 0
0 2 0
0 2 1
0 2 2
0 2 3
0 3 0
0 3 0
0 3 1
0 3 2
0 3 3
您可以看到,每次嵌套 for 循环完成时,输出都会重复!重复...
0 1 0
0 2 0
0 3 0
我添加的维度越多,这个问题就越严重。 例如,一个 1x4x4x2 的矩阵在每次完成其第 4 维循环时重复,在其他地方更多。
如果有人能提出一个与我当前的函数一样简洁的解决方案,我将不胜感激!
现在我将不得不实现一个包含大量 for 循环的大型 switch 语句,并将维数限制在 5 个左右。
【问题讨论】:
请注意它被称为 dimension 而不是 dimention。 一定要递归吗? 修正了拼写!不,它不一定是递归的,但我找不到一个非递归修复的干净示例。 尝试将其展平为一维。让大多数事情变得更容易。 @skeller 矩阵是一个非常尴尬的字节数组,其中每个维度都有自己的位步长。我正在尝试将其复制到一个特征矩阵,你是对的,可以展平。 【参考方案1】:如果我理解正确,您想迭代“n 维矩阵”(实际上不称为“矩阵”)的所有元素,并且需要一种获取所有索引的方法。我会从这样的开始:
#include <iostream>
#include <vector>
struct multi_index
std::vector<size_t> dimensions;
std::vector<size_t> current;
multi_index(std::vector<size_t> dim) : dimensions(dim), current(dim.size())
void print()
for (const auto& e : current) std::cout << e << " ";
std::cout << "\n";
bool increment()
for (size_t i=0;i<dimensions.size();++i)
current[i] += 1;
if (current[i] >= dimensions[i])
current[i] = 0;
else
return true;
return false;
;
int main()
multi_index x1,2,3;
do
x.print();
while (x.increment());
然后根据具体需求进行调整。例如,向量可能是std::array
s,并且取决于实际的容器,您可能需要一种简单的方法来访问容器中的元素,而无需编写data[ x[0] ][ x[1] ][ x[2] ]
。
请注意,通常最好将数据扁平化为一维数据结构,然后仅映射索引。特别是当尺寸固定时,您可以从数据局部性中受益。虽然上面允许您编写一个循环来遍历所有维度,但您需要反向映射,即多个索引到一个平面索引。以下内容未经过彻底测试,仅用于概述思路:
#include <vector>
#include <utility>
template <typename T>
struct multi_dim_array
std::vector<T> data;
std::vector<size_t> dimensions;
multi_dim_array(std::vector<size_t> dim)
size_t total = 1;
for (const auto& d : dim) total *= d;
data.resize(total);
T& get(std::initializer_list<int> index)
return data[flatten_index(index)];
size_t flatten_index(std::initializer_list<int> index)
size_t flat = 0;
size_t sub_size = 1;
for (auto x = std::make_pair(0u,index.begin());
x.first < dimensions.size() && x.second != index.end();
++x.first, ++x.second)
flat += (*x.second) * sub_size;
sub_size *= dimensions[x.first];
return flat;
;
int main()
multi_dim_array<int> x2,2,3;
x.get(1,1,1) = 5;
一旦将多维数据存储在平面数组/向量中,编写一个简单的循环来迭代所有元素应该很简单。
PS:老实说我并没有费心在你的代码中找到问题,递归让我头晕:P
【讨论】:
像魅力一样工作!非常感谢你!递归是可怕的。我要将您的代码添加到我的帖子中:) @parkywolfy 不要添加页面上已有的代码。接受答案是 Stack Overflow 的工作方式。当我们看到绿色复选标记时,我们知道是您接受了这个答案。 @perkywolfy 是的,“将您的代码添加到我的帖子中”如果您的意思是您的问题,那么请不要这样做。 Questions 是用来提问的,asnwers 是用来回答的。你的问题很好,如果你把这段代码添加到你的问题中,这将是一个糟糕的问题,我的回答将是多余的 @parkywolfy 严格来说,矩阵有两个维度。具有更多维度的结构宁愿称为张量,这是将矩阵概念推广到 n 维度 @parkywolfy 我觉得答案中缺少了一些东西,我添加了一些关于如何展平多维的内容。顺便说一句,我警告过你;)看到下一行的
我感到很痛苦,所以我真的不想接受你的编辑。我的意思是它的品味问题,但它绝对不是我的【参考方案2】:
我不确定我是否理解了这个问题。如果你想访问多维数组的每个元素一次,下面的代码应该可以完成这项工作(在我的例子中,计数器计算每个元素被访问的次数,所以最后计数应该等于 dimensionSizes):
void print_counter(std::vector<int>& counters)
for (unsigned int i = 0; i < counters.size(); ++i)
std::cout << counters[i] << " ";
std::cout << std::endl;
void iterate_over_n_dimensions(std::vector<int>& counters, std::vector<int> dimensionSizes, int currentDimension)
for (long i = 0; i < dimensionSizes[currentDimension]; i++)
counters[currentDimension] ++;
print_counter(counters);
if (currentDimension + 1 != dimensionSizes.size())
iterate_over_n_dimensions(counters,
dimensionSizes,
currentDimension + 1);
int main(int argc, const char * argv[])
std::vector<int> dimensionSizes = 1, 4, 4 ;
std::vector<int> counters(dimensionSizes.size(), 0);
iterate_over_n_dimensions(counters, dimensionSizes, 0);
return 0;
输出是:
1 0 0
1 1 0
1 2 0
1 3 0
1 4 0
1 4 1
1 4 2
1 4 3
1 4 4
【讨论】:
这并不能解决我的问题,因为我想访问每个元素一次,而不仅仅是“计算每个元素被访问的次数”。我也不能使用算法的输出,因为当我想使用计数器作为坐标访问每个元素时,它超出了维度的范围。 它只会访问每个元素一次,如果没有,计数器的末尾应该有不同的值 这个答案甚至没有访问每个元素一次,看看你的输出。该算法应生成以 0, 0, 0 开头并以 0, 3, 3 结尾的数字的每个排列。 好吧,我还没有完全理解你的问题以上是关于在 C++ 中迭代具有可变维度大小的 N 维矩阵的主要内容,如果未能解决你的问题,请参考以下文章