如何使用步长遍历一个n维数组

4
我是一名有用的助手,可以为您翻译文本。以下是您需要翻译的内容:

我有一个索引问题需要解决。 我有一个已知形状的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维数组(扁平化),该怎么办?
例如像这样的内容:
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)
}

1
不,那是从i、j、k等获取索引。我需要弄清楚如何增加一个循环,以在不同维度上遍历多维数组的步长。 - Greg Samson
也许我基于你提供的代码误解了。我假设你想为每个维度添加嵌套循环。你是想要在已经压缩过的数组上进行单次循环吗? - AndyG
数组已经被展平了。只要能够处理N维并考虑每个维度的步幅,使用单个循环或多个循环都可以。N和数组的形状在运行时已知,但需要避免使用递归。 - Greg Samson
你需要代码还是算法来生成下标呢? - Dinesh
我认为你应该首先使用访问器函数来抽象“维度访问”。然后在此基础上应用步幅,而无需混合这两个概念。这样更加简洁,不变量也更容易看到(更容易证明)。 - v.oddou
显示剩余2条评论
2个回答

0
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();
    }
}

这是不错的尝试,但我认为它行不通,无论如何,您都假设了内存布局(例如行优先或列优先)。由于 d 的右侧索引变化最快,那么我猜它是行优先的。 - Lorenzo

0
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 很容易使用,因为 m_mStrides.first = 哪个维度,m_mStrides.second = 该维度的步幅。

这不是一个完整的工作类,只是一个示例,让您开始。我选择使用 unsigned int 而不是 int,因为当处理形状的大小、维度和步幅时,负数没有意义,但如果您正在使用已经预格式化为 int 的现有代码,则可以使用 int,但我建议对负数进行错误或边界检查。


你可以填写 // Use Your Map Against Your Stored Shape Vector To Do The Traversing. 吗?此外,为什么要使用 map?m_mStride = stride[i-1] (i==0 时为 0),对吗? - Greg Samson
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Francis Cugler
继续上文,需要注意哪个步长对应哪个维度。除非您有超过一百万个维度(键)和步长(值),否则性能影响不会太大。 - Francis Cugler
这基本上是一个帮助类或包装器,它将接受您的形状向量和步幅向量。我想你真的不需要映射,可以直接使用步幅向量。当您将两个概念配对到一个对象中时,我只是喜欢这种关联。 - Francis Cugler
也许我应该问一下:你每个维度的步幅是连续枚举的还是随机值? - Francis Cugler
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接