使用Strepe乘以2 double [] [] Matrices

时间:2016-01-13 18:32:00

标签: java math java-8 java-stream

我想知道使用流的多个2 double[][]数组矩阵的最紧凑和有效的方法是什么。该方法应遵循矩阵乘法规则,如下所示: http://www.mathwarehouse.com/algebra/matrix/multiply-matrix.php

这是使用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[][] md1 = { { 4d, 8d }, { 0d, 2d }, { 1d, 6d } };
    double[][] md2 = { { 5d }, { 9d } };

3 个答案:

答案 0 :(得分:7)

更紧凑和可读的解决方案是在第一个矩阵的行上创建一个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));
}

这将计算m1 * m2,结果将显示在result中。对于每行的乘法,我们不能创建具有第二矩阵的Arrays.stream的流,因为当我们需要在列上的流时,这将在行上创建流。为了抵消这种情况,我们只需回过头来使用IntStream索引。

答案 1 :(得分:1)

您可以使用三个嵌套的 IntStream 将两个矩阵相乘。外部流迭代第一个矩阵的行,内部流迭代第二个矩阵的列以构建结果矩阵。最里面的流获取结果矩阵条目。每个条目是将第一个矩阵的第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

答案 2 :(得分:0)

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

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);
    };
}