Java 8 矩阵 * 向量乘法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34519952/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java 8 matrix * vector multiplication
提问by Ole
I'm wondering if there is a more condensed way of doing the following in Java 8 with streams:
我想知道是否有更简洁的方式在 Java 8 中使用流执行以下操作:
public static double[] multiply(double[][] matrix, double[] vector) {
int rows = matrix.length;
int columns = matrix[0].length;
double[] result = new double[rows];
for (int row = 0; row < rows; row++) {
double sum = 0;
for (int column = 0; column < columns; column++) {
sum += matrix[row][column]
* vector[column];
}
result[row] = sum;
}
return result;
}
Making an Edit. I received a very good answer, however the performance is about 10X slower than the old implementation, so I'm adding the test code here in case anyone wants to investigate it:
进行编辑。我收到了一个很好的答案,但是性能比旧的实现慢了大约 10 倍,所以我在这里添加了测试代码,以防有人想调查它:
@Test
public void profile() {
long start;
long stop;
int tenmillion = 10000000;
double[] vector = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
double[][] matrix = new double[tenmillion][10];
for (int i = 0; i < tenmillion; i++) {
matrix[i] = vector.clone();
}
start = System.currentTimeMillis();
multiply(matrix, vector);
stop = System.currentTimeMillis();
}
回答by Tunaki
A direct way using Stream would be the following:
使用 Stream 的直接方法如下:
public static double[] multiply(double[][] matrix, double[] vector) {
return Arrays.stream(matrix)
.mapToDouble(row ->
IntStream.range(0, row.length)
.mapToDouble(col -> row[col] * vector[col])
.sum()
).toArray();
}
This creates a Stream of each row of the matrix (Stream<double[]>
), then maps each row to the double value resulting of calculating the product with the vector
array.
这将创建矩阵 ( Stream<double[]>
)每一行的 Stream ,然后将每一行映射到使用vector
数组计算乘积的双精度值。
We have to use a Stream over the indexes to calculate the product because there are unfortunately no built-in facility to zip two Streams together.
我们必须在索引上使用 Stream 来计算乘积,因为遗憾的是没有内置工具将两个 Stream 压缩在一起。
回答by TriCore
The way you are measuring the performance is not very reliable to measure performance and its usually not a good idea to write the micro-benchmarks manually. For example, while compiling the code, JVM may choose to change the order of execution, and the start and stop variables might not be getting assigned where you expect them to be assigned and consequently will give unexpected results in your measurements. It is also very important to warm-up JVM as well to let JIT compiler make all the optimizations. GC can also play a very big role in introducing variations in the throughput and response time of your application. I would strongly recommend to use specialized tools such as JMH and Caliper for micro-benchmarking.
您衡量性能的方式对于衡量性能并不是很可靠,手动编写微基准通常不是一个好主意。例如,在编译代码时,JVM 可能会选择更改执行顺序,并且启动和停止变量可能不会在您期望的位置分配,因此会在您的测量中产生意外的结果。预热 JVM 以及让 JIT 编译器进行所有优化也非常重要。GC 还可以在引入应用程序吞吐量和响应时间变化方面发挥非常重要的作用。我强烈建议使用专门的工具,例如 JMH 和 Caliper 进行微基准测试。
I also wrote some benchmarking code with JVM warmup, random data set and higher number of iterations. It turns out that Java 8 streams is giving better results.
我还编写了一些带有 JVM 预热、随机数据集和更多迭代次数的基准测试代码。事实证明,Java 8 流提供了更好的结果。
/**
*
*/
public class MatrixMultiplicationBenchmark {
private static AtomicLong start = new AtomicLong();
private static AtomicLong stop = new AtomicLong();
private static Random random = new Random();
/**
* Main method that warms-up each implementation and then runs the benchmark.
*
* @param args main class args
*/
public static void main(String[] args) {
// Warming up with more iterations and smaller data set
System.out.println("Warming up...");
IntStream.range(0, 10_000_000).forEach(i -> run(10, MatrixMultiplicationBenchmark::multiplyWithStreams));
IntStream.range(0, 10_000_000).forEach(i -> run(10, MatrixMultiplicationBenchmark::multiplyWithForLoops));
// Running with less iterations and larger data set
startWatch("Running MatrixMultiplicationBenchmark::multiplyWithForLoops...");
IntStream.range(0, 10).forEach(i -> run(10_000_000, MatrixMultiplicationBenchmark::multiplyWithForLoops));
endWatch("MatrixMultiplicationBenchmark::multiplyWithForLoops");
startWatch("Running MatrixMultiplicationBenchmark::multiplyWithStreams...");
IntStream.range(0, 10).forEach(i -> run(10_000_000, MatrixMultiplicationBenchmark::multiplyWithStreams));
endWatch("MatrixMultiplicationBenchmark::multiplyWithStreams");
}
/**
* Creates the random matrix and vector and applies them in the given implementation as BiFunction object.
*
* @param multiplyImpl implementation to use.
*/
public static void run(int size, BiFunction<double[][], double[], double[]> multiplyImpl) {
// creating random matrix and vector
double[][] matrix = new double[size][10];
double[] vector = random.doubles(10, 0.0, 10.0).toArray();
IntStream.range(0, size).forEach(i -> matrix[i] = random.doubles(10, 0.0, 10.0).toArray());
// applying matrix and vector to the given implementation. Returned value should not be ignored in test cases.
double[] result = multiplyImpl.apply(matrix, vector);
}
/**
* Multiplies the given vector and matrix using Java 8 streams.
*
* @param matrix the matrix
* @param vector the vector to multiply
*
* @return result after multiplication.
*/
public static double[] multiplyWithStreams(final double[][] matrix, final double[] vector) {
final int rows = matrix.length;
final int columns = matrix[0].length;
return IntStream.range(0, rows)
.mapToDouble(row -> IntStream.range(0, columns)
.mapToDouble(col -> matrix[row][col] * vector[col])
.sum()).toArray();
}
/**
* Multiplies the given vector and matrix using vanilla for loops.
*
* @param matrix the matrix
* @param vector the vector to multiply
*
* @return result after multiplication.
*/
public static double[] multiplyWithForLoops(double[][] matrix, double[] vector) {
int rows = matrix.length;
int columns = matrix[0].length;
double[] result = new double[rows];
for (int row = 0; row < rows; row++) {
double sum = 0;
for (int column = 0; column < columns; column++) {
sum += matrix[row][column] * vector[column];
}
result[row] = sum;
}
return result;
}
private static void startWatch(String label) {
System.out.println(label);
start.set(System.currentTimeMillis());
}
private static void endWatch(String label) {
stop.set(System.currentTimeMillis());
System.out.println(label + " took " + ((stop.longValue() - start.longValue()) / 1000) + "s");
}
}
Here is the output
这是输出
Warming up...
Running MatrixMultiplicationBenchmark::multiplyWithForLoops...
MatrixMultiplicationBenchmark::multiplyWithForLoops took 100s
Running MatrixMultiplicationBenchmark::multiplyWithStreams...
MatrixMultiplicationBenchmark::multiplyWithStreams took 89s