如何使用流将 2 个 double[][] 矩阵相乘?
How to multiply 2 double[][] matrices using streams?
我想知道使用流对多个 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}};
我创建了一个使用 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);
};
}
一个更紧凑和可读的解决方案是在第一个矩阵的行上创建一个流,将每一行映射到它与第二个矩阵列相乘的结果,并将其收集回 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
of the second matrix since this would create a Stream over the rows when we need a Stream over the columns. To counteract that, we simply go back to using an IntStream
的 Stream。
您可以使用三个嵌套的 IntStream
来将两个矩阵相乘。外部流迭代 第一个矩阵 的行,内部流迭代 第二个矩阵 的列以构建 结果矩阵。最里面的流获取结果矩阵的entries。每个项是第一个矩阵的第i行相乘得到的乘积之和j 第二个矩阵的第 j 列:
/**
* 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]
}
另请参阅:
我想知道使用流对多个 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}};
我创建了一个使用 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);
};
}
一个更紧凑和可读的解决方案是在第一个矩阵的行上创建一个流,将每一行映射到它与第二个矩阵列相乘的结果,并将其收集回 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
of the second matrix since this would create a Stream over the rows when we need a Stream over the columns. To counteract that, we simply go back to using an IntStream
的 Stream。
您可以使用三个嵌套的 IntStream
来将两个矩阵相乘。外部流迭代 第一个矩阵 的行,内部流迭代 第二个矩阵 的列以构建 结果矩阵。最里面的流获取结果矩阵的entries。每个项是第一个矩阵的第i行相乘得到的乘积之和j 第二个矩阵的第 j 列:
/**
* 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]
}
另请参阅: