【问题标题】:how to traverse an n-dimensional array with stride如何跨步遍历n维数组
【发布时间】:2015-07-15 22:04:42
【问题描述】:

我有一个要解决的索引问题。 我有一个形状已知的 n 维数组。 我想跨步遍历数组(每个暗淡可能不同)。

对于固定尺寸,我会使用嵌套的 for 循环(小数组)并逐步递增:

std::vector<int> shape = {10, 10}; // h,w
int num_dim = shape.size();
std::vector<int> stride = {1,2};

for (int i = 0; i< shape[0]; i+=stride[0]) {
    for (int j = 0; j< shape[1]; j+=stride[1]) {
     //print flattened index (row major)
     printf("index: %d\n",i*shape[0]+j);
    }

}

但是我将如何使用 n 维数组(展平)来做到这一点? IE。类似:

std::vector<int> shape = {10, 10}; // h,w
int num_dim = shape.size();
std::vector<int> stride = {1,2};

int shape_size = 1;
for (int i = 0; i< num_dim; ++i) {
shape_size *= shape[i];
}

int ind = 0;
while (ind < shape_size) {
 // somehow incr ind by the correct amount according to stride, and shape
 // or check if the ind is in the stride (less desirable)
}

【问题讨论】:

  • i*shape[0]+j*shape[1]+k (3D)、i*shape[0]+j*shape[1]+k*shape[2]+l (4D) 等?
  • 没有。这是从 i、j、k 等获取索引。我需要弄清楚如何增加一个循环,该循环以不同维度的步幅遍历多维数组。
  • 也许我根据您提供的代码误解了。我假设您会为每个维度添加嵌套循环。你想要一个已经扁平化的数组上的单个循环吗?
  • 阵列已经展平。只要它适用于 N 维并考虑每个暗淡的步幅,使用单个或更多循环就可以了。 N 和数组的形状是已知的,但在运行时设置。我也想避免使用递归。
  • 你想要代码还是只是一个算法来生成下标?

标签: c++ arrays algorithm


【解决方案1】:
class Foo {
public:
    std::vector<int> shape = { 10, 10 }; // h,w
    std::vector<int> stride = { 1, 2 };
    std::vector<int> d = { 0, 0 };
    bool add_end = false;
    void AddStride(int index) {
        d[index] += stride[index];
        if (d[index] < shape[index]) {
            return;
        } else {
            if (index == 0) {
                add_end = true;
                return;
            }
            d[index] = 0;
            index--;
            AddStride(index);
        }
    }
    bool AddStride() {
        AddStride(d.size() - 1);
    }
};
int main() {
    Foo f;
    while(f.add_end != true) {
        //do something
        f.AddStride();
    }
}

【讨论】:

  • 这是一个不错的尝试,但我认为它不起作用,无论哪种方式你都假设内存布局(例如 row-major 或 col-major)。由于d 最右边的索引变化最快,所以我猜它是行主要的。
【解决方案2】:
class Traverse {
private:
    unsigned m_numDim;
    std::vector<unsigned int> m_vShape;
    std::map<unsigned int, unsigned int> m_mStrides;

public:
    Traverse( const std::vector<unsigned int>& shape, const std::vector<unsigned int>& strides );
    std::vector<unsigned int>& traverse();
}; // Traverse

// ---------------------------------------------------------------
// Traverse()
Traverse::Traverse( const std::vector<unsigned int>& shape, const std::vector<unsigned int>& strides ) {
    if ( shape.empty() || strides.empty() ) {
       return;
    }
    m_vShape = shape;
    m_numDim = m_vShape.size();

    // Here Use The Passed In Vector Of Known Strides To Create Your Map As      
    // An Association For Each Dimension With Its Stride
    for ( unsigned index = 0; index < strides.size(); index++ ) {
        m_mStrides[index+1] = strides[index];
    }         
} // Traverse

// ----------------------------------------------------------------
// traverse()
std::vector<unsigned int>& Traverse::traverse() {
    std::vector<unsigned int> vTemp;

    for ( unsigned index = 0; index < m_numDim; ++index ) {
        // Use Your Map Against Your Stored Shape Vector To Do The Traversing.
    }

    return vTemp; // Or m_vShape;
} // traverse

知道 m_mStrides.first = 哪个维度和 m_mStrides.second = 该维度中的步幅,在这里 m_mStrides 很容易工作。

这不是一个完整的工人阶级,而只是一个帮助您入门的插图。我也选择使用无符号整数而不是整数,因为在处理形状的大小、尺寸和步幅时,负数没有意义,但是如果您使用的是已经使用 int 预先格式化的现有代码,则可以,但我建议这样做错误或边界检查是否定的。

【讨论】:

  • 可以填写// Use Your Map Against Your Stored Shape Vector To Do The Traversing.吗?另外,为什么要使用地图? m_mStride = stride[i-1] (0 代表 i==0) 对吧?
  • 我是在演示一个例子,我没有实现遍历向量的工作。它是为了说明一个起点。我解释了为什么我在答案中选择了地图如果您有一个尺寸为 { 4, 6, 9 } 的向量 = 3,而步幅向量 { 3, 5, 4 } 则地图的键值从索引,因此每个维度的键分别为 1,2,3,相对于每个维度的每个步幅,映射中的值将是 3,5,4。所以你有一个维度和步幅 [1,3]、[2,5]、[3,4] 的映射。当你去遍历你的向量时,你知道......
  • (续...)哪个步幅是哪个维度。除非您有超过一百万个维度(键)和跨度(值),否则性能影响应该不会那么大。
  • 它基本上是一个帮助器或包装器类,它将接收您的形状向量和步幅向量。我猜你真的不需要地图,你可以直接使用步幅矢量。我只是喜欢将两个概念组合成一个对象时的关联。
  • 也许我应该问:您的每个维度的步幅是连续枚举还是随机值?
【解决方案3】:

这是一篇非常好的文章解释它:https://ajcr.net/stride-guide-part-1/ 阅读关于 n 维步幅的第 2 部分以及文章的第 2 部分(第 1 部分顶部的链接)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-11
    • 2013-03-25
    • 2011-07-18
    • 1970-01-01
    • 1970-01-01
    • 2021-12-28
    相关资源
    最近更新 更多