N行M列矩阵的所有AXB子矩阵中的最大元素是什么?

3

我试图从一个 NXM 的矩阵中找到任何子矩阵 AXB 的最大元素。我实现了稀疏树方法,但是无法优化它。事实上,我需要解决范围查询的问题,因此需要优化代码。在进行预计算时,对于任何 N、M 值,时间复杂度为 O(NMlog(N)log(M))。如何将其改进为 (N*M)。这是我的预计算代码:

for(int i=0; i<n;i++)
  for(int j=0; j<m;j++)
        cin>>arr[i][j];

for(int i=0; pow(2,i) <= n; i++)
        for(int j=0; pow(2,j) <= m; j++)
            for(int x=0; x + pow(2,i)-1 < n; x++)
                for(int y = 0; y + pow(2,j) -1 < m; y++)
                {
                    i=(int)i;
                    j=(int)j;
                    if (i == 0 && j == 0)
                            M[x][y][i][j] = arr[x][y]; 
                    else if (i == 0)
                            M[x][y][i][j] = maxi(2,M[x][y][i][j-1], M[x][(int)(y+pow(2,(j-1)))][i][j-1]);
                    else if (j == 0)
                            M[x][y][i][j] = maxi(2,M[x][y][i-1][j], M[(int)(x+ pow(2,(i-1)))][y][i-1][j]);
                    else 
                            M[x][y][i][j] = maxi(4,M[x][y][i-1][j-1], M[(int)(x + pow(2,(i-1)))][y][i-1][j-1], M[x][(int)(y+pow(2,(j-1)))][i-1][j-1], M[(int)(x + pow(2,(i-1)))][(int)(y+pow(2,(j-1)))][i-1][j-1]);
                }

对于输入的x、y、x1和y1

 k = log(x1 - x + 1);
 l = log(y1 - y + 1);
 int max_element = max(4,M[x][y][k][l], M[(int)(x1 - pow(2,k) + 1)][y][k][l], M[x][(int)(y1 - pow(2,l) + 1)][k][l], M[(int)(x1 - pow(2,k) + 1)][(int)(y1 - pow(2,l) + 1)][k][l]);

如何提高此代码的性能。请帮忙。
1个回答

3

这个解决方案并不比 O(N*M*Log(N)*Log(M)) 更好,但它比你的实现好。

通过观察你的for循环的执行顺序和数组M的访问方式,会出现太多的内存跳跃和缓存未命中,导致程序运行缓慢。

例如:

查看以下循环所需的时间:

int M[1000][1000][11][11];
for(int i = 0 ; i <= 10 ; i++){
    for(int j = 0 ; j <= 10 ; j++){
        for(int x = 0 ; x < 1000 ; x++){
            for(int y = 0 ;  y < 1000 ; y++){
                M[x][y][i][j] = 1;
            }
        }
    }
}

上述执行需要1.9秒
int M[11][11][1000][1000];
for(int i = 0 ; i <= 10 ; i++){
    for(int j = 0 ; j <= 10 ; j++){
        for(int x = 0 ; x < 1000 ; x++){
            for(int y = 0 ;  y < 1000 ; y++){
                M[i][j][x][y] = 1;
            }
        }
    }
}

这个循环只需要0.2秒。因此,总是尝试编写循环以便有序访问内存。

更多细节可以阅读这里

所以,如果按照以下方式更改代码,它将更快:

M[Log(n)][Log(m)][n][m];
for(int i=0; (1<<i) <= n; i++)
        for(int j=0; (1<<j) <= m; j++)
            for(int x=0; x + (1<<i)-1 < n; x++)
                for(int y = 0; y + (1<<j) -1 < m; y++)
                {
                    i=(int)i;
                    j=(int)j;
                    if (i == 0 && j == 0)
                            M[i][j][x][y] = arr[x][y]; 
                    else if (i == 0)
                            M[i][j][x][y] = maxi(2,M[i][j-1][x][y], M[i][j-1][x][(y+(1<<(j-1)))]);
                    else if (j == 0)
                            M[i][j][x][y] = maxi(2,M[i-1][j][x][y], M[i-1][j][(x+ (1<<(i-1)))][y]);
                    else 
                            M[i][j][x][y] = maxi(4,M[i-1][j-1][x][y], M[i-1][j-1][(x + (1<<(i-1)))][y], M[i-1][j-1][x][(y+(1<<(j-1)))], M[i-1][j-1][(x + (1<<(i-1)))][(y+(1<<(j-1)))]);
                }

还可以进行一项优化,如果您要计算log()的次数过多(即10的幂次方为10^5或更大),则可以使用31-__builtin_clz()代替log()

k = 31-__builtin_clz(x1 - x + 1);
l = 31-__builtin_clz(y1 - y + 1);
int max_element = max(4,M[k][l][x][y], M[k][l][(x1 - (1<<k) + 1)][y], M[k][l][x][(y1 - (1<<l) + 1)], M[k][l][(x1 - (1<<k) + 1)][(y1 - (1<<l) + 1)]);

谢谢,但是你修改的算法对于每个x、y、x1、y1都给出了错误的答案。而且,在这个操作之后,我的数组也被改变了。为什么会发生这种情况? - vishwas gupta
我没有改变你的算法。我只是更改了数组元素的访问方式,以使其更快。不要复制粘贴此代码,这可能会导致错误。 - sudoer
我已经按照 https://dev59.com/f5ffa4cB1Zd3GeqP_7ZC 中所述实现了。这是我的代码链接 https://ideone.com/ntfXKB。请问您能告诉我哪里有问题吗? - vishwas gupta
你之前复制了错误的代码并进行了编辑。现在只需使用上面的新代码,这将会给出正确的输出结果。 - sudoer
感谢指出我的错误。但是在x1=1,y1=3,x2=1,y2=4的情况下失败了。给定矩阵的输出应该是4,但它输出了3。 - vishwas gupta
非常感谢,这真的帮了我很多。 - vishwas gupta

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