需要一种用于平面表示中的数组索引的算法

时间:2014-01-08 09:51:56

标签: c++ c arrays algorithm

以下是这种情况:我正在尝试实现一个与N维数组一起使用的解决方案,类似下面的代码就可以实现(不是真正的编程语言):

int a[10,14,56]

会创建一个三维数组(即:一个Cuboid),或者:

int a[10,20]

显然会创建一个矩阵。

为了能够表示数据,我决定为元素创建一个“平坦”的存储区域。因此,对于3维向量,我分配10 * 14 * 56整数,对于第二个,我分配10 * 20整数。

现在,问题出现了:对于在给定索引处检索元素,解决方案是自我解释1维数组,以及二维数组((i, j)处的值i计算行数和j对数组N x M中的列进行计数,其中N是行数,M是列数)我制定了公式:

array[N, M] -> flat_memory [N * M]
flat_index(i,j) = M * i + j

对于我提出的三维数组:

array[N, M, L] -> flat_memory[N * M * L]
flat_index(i, j, k) = L * i + M * j + k

但是这感觉很糟糕......似乎我无法得到推广的步骤:(所以在这里我转向社区:我的逻辑/计算中的缺陷是什么?那里有任何算法吗?那种问题?

4 个答案:

答案 0 :(得分:4)

尝试使用所需的索引写下一些值。因此,对于N = 1,M = 2,L = 3,例如:

i  j  k  index
0  0  0  0
0  0  1  1
0  0  2  2
0  1  0  3
0  1  1  4
0  1  2  5
1  0  0  6
...

现在你可以观察到i的每次增加,指数应该增加6, 这是M*L。对于j的每次增加,索引应该增加3,即L

(更一般地说,你需要将一些维度的索引乘以所有不太重要的维度'索引)

所以我们有:

array[N, M, L] -> flat_memory[N * M * L]
flat_index(i, j, k) = M * L * i + L * j + k

这绝不是唯一有效的方法。您可以根据需要重新排列顺序,适当地更改被乘数,这些都是平展它的有效方法:

flat_index(i, j, k) = M * L * i + M * k + j

flat_index(i, j, k) = N * L * j + L * i + k
flat_index(i, j, k) = N * L * j + N * k + i

flat_index(i, j, k) = M * N * k + M * i + j
flat_index(i, j, k) = M * N * k + N * j + i

答案 1 :(得分:2)

我认为一种概括方式如下

阵列[N,M,K] - > flat_memory [N * M * K]

flat_index(i,j,k)=(M * N)* i + M * j + k

阵列[N,M,K,L] - > flat_memory [N * M * K * L]

flat_index(i,j,k,l)=(M * N * K)* i +(M * N)* j + M * k + l

答案 2 :(得分:2)

您可能对以下代码感兴趣,这些代码允许您使用任何“动态”维度:

#include <cassert>
#include <cstddef>

#include <vector>

template<typename T>
class MultiArray
{
public:
    explicit MultiArray(const std::vector<size_t>& dimensions) :
        dimensions(dimensions),
        values(computeTotalSize(dimensions))
    {
        assert(!dimensions.empty());
        assert(!values.empty());
    }

    const T& get(const std::vector<size_t>& indexes) const
    {
        return values[computeIndex(indexes)];
    }
    T& get(const std::vector<size_t>& indexes)
    {
        return values[computeIndex(indexes)];
    }

    size_t computeIndex(const std::vector<size_t>& indexes) const
    {
        assert(indexes.size() == dimensions.size());

        size_t index = 0;
        size_t mul = 1;

        for (size_t i = 0; i != dimensions.size(); ++i) {
            assert(indexes[i] < dimensions[i]);
            index += indexes[i] * mul;
            mul *= dimensions[i];
        }
        assert(index < values.size());
        return index;
    }

    std::vector<size_t> computeIndexes(size_t index) const
    {
        assert(index < values.size());

        std::vector<size_t> res(dimensions.size());

        size_t mul = values.size();
        for (size_t i = dimensions.size(); i != 0; --i) {
            mul /= dimensions[i - 1];
            res[i - 1] = index / mul;
            assert(res[i - 1] < dimensions[i - 1]);
            index -= res[i - 1] * mul;
        }
        return res;
    }

private:
    size_t computeTotalSize(const std::vector<size_t>& dimensions) const
    {
        size_t totalSize = 1;

        for (auto i : dimensions) {
            totalSize *= i;
        }
        return totalSize;
    }

private:
    std::vector<size_t> dimensions;
    std::vector<T> values;
};

让我们测试一下:

int main()
{
    MultiArray<int> m({3, 2, 4});

    m.get({0, 0, 3}) = 42;
    m.get({2, 1, 3}) = 42;

    for (size_t i = 0; i != 24; ++i) {
        assert(m.computeIndex(m.computeIndexes(i)) == i);
    }
    return 0;
}

答案 3 :(得分:0)

显然,对于一维数组,您只需要一个索引来处理所有元素:

array[N] -> flat_memory[N] <br />
flat_index(i) = i

如果您添加另一个维度(正如您所说):

array[N, M] -> flat_memory[N * M]<br />
flat_index(i, j) = M * i + j

还有第三个:

array[N, M, K] -> flat_memory[N * M * L]<br />
flat_index(i, j, k) = (M * i + j) * N + k

我认为你现在可以更清楚地看到这种模式了。对于每个新维度,乘以最后一个维度的大小并添加新索引。