【问题标题】:Divide and Conquer Matrix Multiplication分治矩阵乘法
【发布时间】:2011-06-18 07:56:32
【问题描述】:

我无法让分而治之的矩阵乘法工作。据我了解,您将大小为 nxn 的矩阵拆分为象限(每个象限为 n/2),然后执行以下操作:

C11 = A11⋅ B11 + A12 ⋅ B21   
C12 = A11⋅ B12 + A12 ⋅ B22  
C21 = A21 ⋅ B11 + A22 ⋅ B21  
C22 = A21 ⋅ B12 + A22 ⋅ B22  

我的分而治之的输出非常大,我无法解决问题,因为我不太擅长递归。

示例输出:

原始矩阵A:

4 0 4 3   
5 4 0 4   
4 0 4 0  
4 1 1 1 

A×A

经典:

44 3 35 15  
56 20 24 35  
32 0 32 12  
29 5 21 17  

分而治之:

992 24 632 408  
1600 272 720 1232   
512 0 512 384  
460 17 405 497  

有人能告诉我分而治之我做错了什么吗?我所有的矩阵都是int[][],经典方法是传统的3 for循环矩阵乘法

【问题讨论】:

  • 为什么要这样做矩阵乘法?如果您对原始性能感兴趣,那么可以使用数字库,我相信它们会比您在合理时间内自己编写的更快。如果您有兴趣了解数值计算,我将从循环平铺(维基百科有一篇文章)开始,而不是递归解决方案。

标签: algorithm math matrix multiplication


【解决方案1】:

您以错误的方式递归调用divideAndConquer。您的函数所做的是对矩阵进行平方。为了使分治矩阵乘法起作用,它需要能够将两个可能不同的矩阵相乘。

它应该看起来像这样:

private static int[][] divideAndConquer(int[][] matrixA, int[][] matrixB){
    if (matrixA.length == 2){
         //calculate and return base case
    }
    else {
        //make a11, b11, a12, b12 etc. by dividing a and b into quarters      
        int[][] c11 = addMatrix(divideAndConquer(a11,b11),divideAndConquer(a12,b21));
        int[][] c12 = addMatrix(divideAndConquer(a11,b12),divideAndConquer(a12,b22));
        int[][] c21 = addMatrix(divideAndConquer(a21,b11),divideAndConquer(a22,b21));
        int[][] c22 = addMatrix(divideAndConquer(a21,b12),divideAndConquer(a22,b22));
        //combine result quarters into one result matrix and return
    }
}

【讨论】:

    【解决方案2】:

    一些调试方法可以尝试:

    • 尝试一些非常简单的测试矩阵作为输入(例如,全零,一个或几个策略矩阵)。您可能会在“失败”中看到一种模式,该模式会告诉您错误在哪里。

    • 确保您的“经典”方法为您提供正确的答案。对于小矩阵,可以使用 Woflram Alpha 在线测试答案:http://www.wolframalpha.com/examples/Matrices.html

    • 要调试递归:在函数的入口和出口处添加 printf() 语句,包括调用参数。运行测试矩阵,将输出写入日志文件,然后使用文本编辑器打开日志文件。逐步检查每个案例,在编辑器中写下你的笔记,确保它在每个步骤中都能正常工作。添加更多 printf() 语句并在需要时再次运行。

    祝作业顺利!

    【讨论】:

    • 我的经典方法确实给了我正确的答案。我将尝试制作一个全为 1 而不是 0 的矩阵,因为我怀疑 0 的矩阵是否会起作用,因为与 0 相加或相乘将为 0。
    • 是的,一个全零的矩阵会给你零。但是添加一些战略性的(比如所有在一列或一行或对角线中)会给你一些更好的测试。
    【解决方案3】:

    谁能告诉我分而治之我做错了什么?

    是的:

       int[][] a = divideAndConquer(topLeft);
       int[][] b = divideAndConquer(topRight);
       int[][] c = divideAndConquer(bottomLeft);
       int[][] d = divideAndConquer(bottomRight);
    
       int[][] c11 = addMatrix(classical(a,a),classical(b,c));
       int[][] c12 = addMatrix(classical(a,b),classical(b,d));
       int[][] c21 = addMatrix(classical(c,a),classical(d,c));
       int[][] c22 = addMatrix(classical(c,b),classical(d,d));
    

    你在这里经历了一个额外的乘法步骤:你不应该同时调用divideAndConquer()classical()

    你实际上在做的是:

    C11 = (A11^2)⋅(B11^2) + (A12^2)⋅(B21^2)
    C12 = (A11^2)⋅(B12^2) + (A12^2)⋅(B22^2)
    C21 = (A21^2)⋅(B11^2) + (A22^2)⋅(B21^2)
    C22 = (A21^2)⋅(B12^2) + (A22^2)⋅(B22^2)
    

    这是不正确的。

    1. 首先,删除 divideAndConquer() 调用,并将 a/b/c/d 替换为 topLeft/topRight/etc。 看看它是否给你正确的结果。

    2. 您的divideAndConquer() 方法需要一对输入参数,因此您可以使用A*B。一旦你得到这个工作,摆脱对classical()的调用,并改用divideAndConquer()。 (或将它们保存为长度不是 2 的倍数的矩阵。)

    【讨论】:

      【解决方案4】:

      您可能会发现Strassen's algorithm 上的 Wiki 文章很有帮助。

      【讨论】:

      • 接下来我将实现 Strassens 算法,但我也需要分而治之。
      猜你喜欢
      • 2019-03-13
      • 2020-06-12
      • 2020-04-11
      • 2017-12-05
      • 2016-05-01
      • 2012-03-04
      • 2017-07-16
      • 1970-01-01
      • 2013-07-25
      相关资源
      最近更新 更多