Java 8矩阵*向量乘法

Java 8矩阵*向量乘法,java,matrix,java-8,java-stream,multiplication,Java,Matrix,Java 8,Java Stream,Multiplication,我想知道在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

我想知道在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;
}
公共静态双[]乘(双[]矩阵,双[]向量){
int行=矩阵长度;
int columns=矩阵[0]。长度;
double[]结果=新的double[行];
对于(int row=0;row
进行编辑。我得到了一个非常好的答案,但是性能比旧的实现慢了大约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();
 }
@测试
公开资料{
长起点;
长停;
整数千万=10000000;
双[]向量={1,2,3,4,5,6,7,8,9,10};
double[]矩阵=新的double[1千万][10];
对于(int i=0;i<1千万;i++){
矩阵[i]=vector.clone();
}
start=System.currentTimeMillis();
乘(矩阵、向量);
停止=System.currentTimeMillis();
}

使用流的直接方法如下:

公共静态双[]乘(双[]矩阵,双[]向量){
返回数组.stream(矩阵)
.mapToDouble(行->IntStream.range(0,行.长度)
.mapToDouble(列->行[col]*向量[col])
.sum())
.toArray();
}
这将为矩阵的每一行创建一个流(
),然后将每一行映射到使用
向量
数组计算乘积所得的双倍值


我们必须使用索引上的数据流来计算产品,因为不幸的是,没有内置的工具将两个数据流压缩在一起。

测量性能的方法对于测量性能不是非常可靠,手动编写微基准通常不是一个好主意。例如,在编译代码时,JVM可能会选择更改执行顺序,并且启动和停止变量可能不会被分配到您希望分配的位置,因此会在您的度量中产生意外的结果。预热JVM也非常重要,让JIT编译器进行所有优化。GC还可以在引入应用程序的吞吐量和响应时间的变化方面扮演非常重要的角色。我强烈建议使用专业工具,如JMH和卡尺进行微观基准测试

我还编写了一些关于JVM预热、随机数据集和更高迭代次数的基准测试代码。事实证明,Java8流提供了更好的结果

/**
 *
 */
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");
    }
}

嗨,图纳基-谢谢-这很有效!不过,我对这场演出有点惊讶。对于一个有1000万行的矩阵,它需要10倍长的时间才能完成…所以我想我会坚持使用“旧”循环。谢谢。嗨,非常感谢你的反馈。这非常令人鼓舞。有那么一会儿,我想我必须坚持香草口味:)。我还得再玩一会儿。再次感谢!更好的方法是使用像JMH这样的基准框架,它负责所有热身阶段。
Warming up...
Running MatrixMultiplicationBenchmark::multiplyWithForLoops...
MatrixMultiplicationBenchmark::multiplyWithForLoops took 100s
Running MatrixMultiplicationBenchmark::multiplyWithStreams...
MatrixMultiplicationBenchmark::multiplyWithStreams took 89s