【问题标题】:How to multiply 2 double[][] matrices using streams?如何使用流将 2 个 double[][] 矩阵相乘?
【发布时间】:2016-04-18 21:09:26
【问题描述】:

我想知道使用流对多个 2 个double[][] 数组矩阵最紧凑和最有效的方法是什么。该方法应遵循矩阵乘法规则,如下所示:

Matrix Multiplication: How to Multiply Two Matrices Together

这是使用 for 循环的一种方法(this 是第一个矩阵):

final int nRows = this.getRowDimension();
final int nCols = m.getColumnDimension();
final int nSum = this.getColumnDimension();

final double[][] outData = new double[nRows][nCols];
// Will hold a column of "m".
final double[] mCol = new double[nSum];
final double[][] mData = m.data;

// Multiply.
for (int col = 0; col < nCols; col++) {
    // Copy all elements of column "col" of "m" so that
    // will be in contiguous memory.
    for (int mRow = 0; mRow < nSum; mRow++) {
        mCol[mRow] = mData[mRow][col];
    }

    for (int row = 0; row < nRows; row++) {
        final double[] dataRow = data[row];
        double sum = 0;
        for (int i = 0; i < nSum; i++) {
            sum += dataRow[i] * mCol[i];
        }
        outData[row][col] = sum;
    }
}

该程序应符合以下测试数据:

double[][] md1 = {{4d, 8d}, {0d, 2d}, {1d, 6d}};
double[][] md2 = {{5d, 2d, 5d, 5d}, {9d, 4d, 5d, 5d}};

double[][] mb1 = {{4d, 8d}, {0d, 2d}, {1d, 6d}};
double[][] mb2 = {{5d}, {9d}};

【问题讨论】:

    标签: java math matrix java-stream matrix-multiplication


    【解决方案1】:

    我创建了一个 BiFunction,它使用 IntStream.range() 执行乘法运算。如果有人有更紧凑的东西,我很想看看。这里是:

    public static BiFunction<ArrayMatrix, ArrayMatrix, ArrayMatrix> multiply(boolean parallel) {
        return (m1, m2) -> {
            // checkMultiplicationCompatible(m1, m2);
            final int m1Rows = m1.getRowDimension();
            final int m2Rows = m2.getRowDimension();
            final int m1Cols = m1.getColumnDimension();
            final int m2Cols = m2.getColumnDimension();
    
            double[][] a1 = m1.getData();
            double[][] a2 = m2.getData();
    
            final double[][] result = new double[m1Rows][m2Cols];
    
            // Buffer for the tranpose of each md2 column
            final double[] transpose = new double[m1Rows];
    
            range(0, m2Cols).forEach(m2Col -> {
                range(0, m2Rows).forEach(m2Row -> {
                    transpose[m2Row] = a2[m2Row][m2Col];
                });
                range(0, m1Rows).forEach(row -> {
                    final double[] dataRow = a1[row];
                    double sum = 0;
                    for (int m1Col = 0; m1Col < m1Cols; m1Col++) {
                        sum += dataRow[m1Col] * transpose[m1Col];
                    }
                    result[row][m2Col] = sum;
                });
            });
            return new ArrayMatrix(result, false);
        };
    }
    

    【讨论】:

      【解决方案2】:

      更紧凑和可读的解决方案是在第一个矩阵的行上创建一个 Stream,将每一行映射到将其与第二个矩阵列相乘的结果,并将其收集回 double[][]

      public static void main(String[] args) {
          double[][] m1 = {{4, 8}, {0, 2}, {1, 6}};
          double[][] m2 = {{5, 2}, {9, 4}};
      
          double[][] result = Arrays.stream(m1)
                  .map(r -> IntStream.range(0, m2[0].length)
                          .mapToDouble(i -> IntStream.range(0, m2.length)
                                  .mapToDouble(j -> r[j] * m2[j][i]).sum())
                          .toArray())
                  .toArray(double[][]::new);
      
          System.out.println(Arrays.deepToString(result));
          // [[92.0, 40.0], [18.0, 8.0], [59.0, 26.0]]
      }
      

      这将计算m1 * m2,结果将在result 中。对于每一行的乘法,我们不能用第二个矩阵的Arrays.stream 创建一个流,因为当我们需要列上的流时,这会在行上创建一个流。为了解决这个问题,我们只需返回在索引上使用 IntStream

      【讨论】:

        【解决方案3】:

        您可以使用三个嵌套的IntStreams 来将两个矩阵相乘。外部流迭代第一个矩阵的行,内部流迭代第二个矩阵的列以构建结果矩阵。最里面的流获得结果矩阵条目。每个entry第一个矩阵的第i行乘以j的乘积之和第二个矩阵的>-th 列

        /**
         * Matrix multiplication
         *
         * @param m rows of 'a' matrix
         * @param n columns of 'a' matrix
         *          and rows of 'b' matrix
         * @param p columns of 'b' matrix
         * @param a first matrix 'm×n'
         * @param b second matrix 'n×p'
         * @return result matrix 'm×p'
         */
        public static double[][] matrixMultiplication(
                int m, int n, int p, double[][] a, double[][] b) {
            return IntStream.range(0, m)
                    .mapToObj(i -> IntStream.range(0, p)
                            .mapToDouble(j -> IntStream.range(0, n)
                                    .mapToDouble(k -> a[i][k] * b[k][j])
                                    .sum())
                            .toArray())
                    .toArray(double[][]::new);
        }
        

        // test
        public static void main(String[] args) {
            double[][] md1 = {{4d, 8d}, {0d, 2d}, {1d, 6d}};
            double[][] md2 = {{5d, 2d, 5d, 5d}, {9d, 4d, 5d, 5d}};
            double[][] md3 = matrixMultiplication(3, 2, 4, md1, md2);
        
            Arrays.stream(md3).map(Arrays::toString).forEach(System.out::println);
            //[92.0, 40.0, 60.0, 60.0]
            //[18.0, 8.0, 10.0, 10.0]
            //[59.0, 26.0, 35.0, 35.0]
        
            //// //// //// //// //// //// //// ////
        
            double[][] mb1 = {{4d, 8d}, {0d, 2d}, {1d, 6d}};
            double[][] mb2 = {{5d}, {9d}};
            double[][] mb3 = matrixMultiplication(3, 2, 1, mb1, mb2);
        
            Arrays.stream(mb3).map(Arrays::toString).forEach(System.out::println);
            //[92.0]
            //[18.0]
            //[59.0]
        }
        

        另见:Parallelized Matrix Multiplication

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-04-26
          • 1970-01-01
          • 2021-12-26
          • 2020-07-18
          • 1970-01-01
          • 2019-10-13
          • 1970-01-01
          相关资源
          最近更新 更多