注意:我愿意接受更好的头衔......
想象一个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++;
}
}
}
}
但这似乎很有说服力,如果你原谅我的双关语。
有人提出更有效的建议吗?
答案 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子阵列形状传递给分析函数,并完全避免复制。