【问题标题】:how to make a sum of submatrices如何对子矩阵求和
【发布时间】:2018-10-30 15:52:41
【问题描述】:

我需要为每个子矩阵计算值的总和。 例如,如果我有 [[1,1,2],[2,3,4]],则生成的矩阵将是:

 M[0][0] = 1 M[0][1] = 1+1 = 2 M[0][2] = 1+1+2 = 4
 M[1][0] = 1+2 = 3 M[1][1] = 1+1+2+3 = 7 M[1][2] = 1+1+2+2+3+4 = 13

M = [[1,2,4],[3,7,13]]

我制作了这段代码

`N = []
 M = []
 summ = 0
 n= list(map(int, input().split()))
 while n != []:
     N.append(n)
     n = list(map(int, input().split()))
 for i in range(len(N)):
     M.append([0 for i in range(len(N[0]))])
     summ = 0
     for j in range(len(N[0])):
         summ += N[i][j]
         M[i][j] = M[i-1][j] + summ ` 

问题是当矩阵很大时会变得非常慢。 我需要在 0.5 秒内最大求解 100x100 矩阵

有人可以帮帮我吗?无需导入包!! `

【问题讨论】:

    标签: python matrix time sum submatrix


    【解决方案1】:

    为了速度,您真的很想使用NumPy,这将比基础 Python 快得多,并且可以为矩阵提供更简洁的代码。从您的小示例中,您可以在不同的轴上使用 numpy.cumsum() 两次:

    import numpy as np
    
    arr = np.array([[1,1,2],[2,3,4]])
    out = arr.cumsum(axis=1).cumsum(axis=0)
    
    print(out)
    

    给予:

    array([[ 1,  2,  4],
           [ 3,  7, 13]], dtype=int32)
    

    旁注:在 Windows 上,默认的 int 类型是 32 位,cumsum() 可能会在大矩阵/大数字上静默溢出,因此如果在 Windows 上,您可能需要 arr = np.array([[1,1,2],[2,3,4]]).astype(np.int64)

    时间安排:

    arr = np.arange(10000).reshape(100, 100)
    %timeit out = arr.cumsum(axis=1).cumsum(axis=0)
    56.3 µs ± 4.96 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    

    因此,比您的要求快数千倍。

    【讨论】:

    • 感谢您的回答,但我无法导入任何包,对不起。
    • @EdoardoGallo 你将很难在 python 中制作可扩展的东西,必须遍历 10000 个项目。两次。我现在得走了,如果有机会的话,我今晚晚些时候再去。
    • 我想过递归,但我对 python 很陌生,我不知道该怎么做。但是谢谢!
    • @EdoardoGallo 我想我们需要设置一个累加器并转置数组,但在我有机会尝试之前有人可能会对其进行排序,我们会看到
    【解决方案2】:

    我们可以使用 dp 矩阵计算所有子矩阵的总和。在此我们计算给定矩阵的前缀和。我们创建一个新矩阵并存储前缀和,然后使用公式

    tlx:左上 x 坐标
    tly:左上 y 坐标
    brx: 右下 x 坐标
    bry: 右下y坐标

    (tlx, tly) 和 (brx, bry) 之间的总和:

    sum += dp[brx][bry] - dp[tlx - 1][bry] - dp[brx][tly - 1] + dp[tlx - 1][tly - 1];
    

    让我解释一下: 我们首先有一个给定的矩阵假设 Arr :

    #define n 3
    int row(n), col(n), sum(0);
    int arr[row][col] = {{1, 2, 3},
                         {4, 5, 6},
                         {7, 8, 9}};
    

    然后我们将创建一个大小大一号的前缀矩阵,并将所有值初始填充为 0。

    int dp[row + 1][col + 1];
    memset(dp, 0, sizeof(dp));
    

    然后将通过以下方式创建我们的前缀矩阵:

    1. 将第一行从我们的 arr 复制到 dp

      for (int j = 1; j < col + 1; j++){dp[1][j] = arr[0][j - 1];}
      
    2. 运行一个循环,将 dp 的第一行的值添加到 arr 的第二列,并将其存储在 dp 的第二列中

      for (int i = 2; i < row + 1; i++)
       {
           for (int j = 1; j < col + 1; j++)
           {
               dp[i][j] += dp[i - 1][j] + arr[i - 1][j - 1];
           }
       }
      
    3. 重复这个过程,直到你填满完整的 dp。

    4. 然后通过 col 运行一个循环,其中我们将每个 col 添加到它们的下一个 col 并保存它,就像我们创建前缀数组一样。

      for (int i = 0; i < row + 1; i++) {
      for (int j = 1; j < row + 1; j++)
           {
               dp[i][j] += dp[i][j - 1];
           }
       }
      
    5. 现在你的 dp(前缀矩阵准备好了)。

       arr : {{1, 2, 3},
             {4, 5, 6},
             {7, 8, 9}};
       dp : 0 0 0 0 
            0 1 3 6 
            0 5 12 21 
            0 12 27 45 
      

    我们为 dp 数组增加了一个大小,并将值 0 放在第一行和 col 中,因为这样我们可以更容易地使用我在上面提到的公式。 我们将矩阵划分为矩形,并在左上位置和右下位置的帮助下计算给定矩阵中的出现次数。 现在我们只需要从左上角到右下角遍历每个位置,并不断添加总和,然后打印出来。

    for (int tlx = 1; tlx < row + 1; tlx++){
      for (int tly = 1; tly < col + 1; tly++){
        for (int brx = tlx; brx < row + 1; brx++){
         for (int bry = tly; bry < col + 1; bry++){
           sum += (dp[brx][bry]) - (dp[tlx - 1][bry]) - (dp[brx][tly - 1]) +   (dp[tlx - 1][tly - 1]);
        }
      }
    }
    } 
    

    输出:500

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-20
      • 2022-01-03
      • 1970-01-01
      • 2016-07-25
      • 1970-01-01
      • 2019-03-02
      • 1970-01-01
      相关资源
      最近更新 更多