高效的子阵列(2D)访问

时间:2014-03-19 19:50:12

标签: c++ arrays algorithm

注意:我愿意接受更好的头衔......

想象一个n x n方块,存储为整数数组。

在每个n非重叠n x sqrt(n)子方块中生成sqrt(n) - 长度数组的最有效方法是什么?

如果我们想要较小的正方形中的数字,那么这是一个特殊情况(n=9)Sudoku。

我能想到的唯一方法是:

int square[n][n], subsq[n], len;

int s = sqrt(n);

for(int j=0; j<n; j+=s){
    for(int i=0; i<n; i+=s){
        //square[i][j] is the top-left of each sub-square
        len = 0;
        for(int y=j; y<j+s; y++){
            for(int x=i; x<i+s; x++){
                subsq[len] = square[x][y];
                len++;
            }
        }
    }
}

但这似乎很有说服力,如果你原谅我的双关语。

有人提出更有效的建议吗?

1 个答案:

答案 0 :(得分:2)

尽管有四级循环,但您最多只能访问每个数组元素一次,因此您的方法的复杂性为O(n ^ 2),而不是O(n ^ 4),因为四个循环级别表明。而且,由于您实际上想要查看所有元素,因此接近最优。

只有一个可能的次优性:不完整使用缓存行。如果s不是缓存行的倍数,则子方块将在缓存行的中间结束,导致部分数据从内存中获取两次。但是,如果您的子方块不再适合缓存,这只是一个问题,因此您需要一个非常大的问题大小来触发它。 对于数独游戏,没有比你给出的更快的方式。

要解决此高速缓存行问题(一旦确定这确实值得!),您可以一次查看一行矩阵,汇总输出数组中ciel(n/sqrt(n))个子方的数据。这将以下列方式交换循环:

for(int j=0; j<n; j+=s){
    for(int y=j; y<j+s; y++){
        for(int i=0; i<n; i+=s){
            for(int x=i; x<i+s; x++){

但是,只有在遍历单个子方案时需要保留的中间数据很小时,才能解决这个问题。如果您需要将整个数据复制到临时数组中,那么您将无法获得任何收益。


如果您真的想要优化,请尝试远离将数据存储在临时subseq数组中。尝试从矩阵中直接解释您找到的数据。如果您确实正在检查数独方块,则可以避免使用此临时数组。


从您提出问题的方式来看,我认为您的目标是将每个子方格中的数据依次传递给分析函数。如果是这种情况,您只需将指向2D子阵列的指针传递给函数,如下所示:

void analyse(int width, int height, int (*subsquare)[n]) {
    for(int y = 0; y < height; y++) {
        for(int x = 0; x < width; x++) {
            subsquare[y][x];    //do anything you like with this value
        }
    }
}

int main() {
    int square[n][n], subsq[n], len;
    int s = sqrt(n);

    for(int j=0; j<n; j+=s){
        for(int i=0; i<n; i+=s){
            analyse(s, s, (int (*)[n])&square[i][j]);
        }
    }
}

现在,您可以通过改变前两个参数将任何2D子阵列形状传递给分析函数,并完全避免复制。