java - 用于转置 double[][] 矩阵的紧凑流表达式

标签 java math java-8 java-stream

我想用最紧凑、最高效的表达式转置一个 double[][] 矩阵。现在我有这个:

public static Function<double[][], double[][]> transpose() {
    return (m) -> {
        final int rows = m.length;
        final int columns = m[0].length;
        double[][] transpose = new double[columns][rows];
        range(0, rows).forEach(r -> {
            range(0, columns).forEach(c -> {
                transpose[c][r] = m[r][c];
            });
        });
        return transpose;
    };
}

想法?

最佳答案

你可以:

public static UnaryOperator<double[][]> transpose() {
    return m -> {
        return range(0, m[0].length).mapToObj(r ->
            range(0, m.length).mapToDouble(c -> m[c][r]).toArray()
        ).toArray(double[][]::new);
    };
}

此代码不使用forEach但更喜欢mapToObjmapToDouble用于将每一行映射到它们的转置。我也改了Function<double[][], double[][]>UnaryOperator<double[][]>因为返回类型是相同的。

但是,它可能不会比 assylias 中的简单 for 循环更有效。的答案。

示例代码:

public static void main(String[] args) {
    double[][] m = { { 2, 3 }, { 1, 2 }, { -1, 1 } };
    double[][] tm = transpose().apply(m);
    System.out.println(Arrays.deepToString(tm)); // prints [[2.0, 1.0, -1.0], [3.0, 2.0, 1.0]]
}
<小时/>

我已经实现了一个 JMH 基准测试,比较了上面的代码、for 循环版本以及上面并行运行的代码。所有三种方法均使用大小为 100、1000 和 3000 的随机方阵进行调用。结果是,对于小矩阵,for循环版本速度更快,但对于更大的矩阵,并行流解决方案在性能方面确实更好(Windows 10、JDK 1.8.0_66、i5-3230M @ 2.60 GHz):

Benchmark                           (matrixSize)  Mode  Cnt    Score    Error  Units
StreamTest.forLoopTranspose                  100  avgt   30    0,026 ±  0,001  ms/op
StreamTest.forLoopTranspose                 1000  avgt   30   14,653 ±  0,205  ms/op
StreamTest.forLoopTranspose                 3000  avgt   30  222,212 ± 11,449  ms/op
StreamTest.parallelStreamTranspose           100  avgt   30    0,113 ±  0,007  ms/op
StreamTest.parallelStreamTranspose          1000  avgt   30    7,960 ±  0,207  ms/op
StreamTest.parallelStreamTranspose          3000  avgt   30  122,587 ±  7,100  ms/op
StreamTest.streamTranspose                   100  avgt   30    0,040 ±  0,003  ms/op
StreamTest.streamTranspose                  1000  avgt   30   14,059 ±  0,444  ms/op
StreamTest.streamTranspose                  3000  avgt   30  216,741 ±  5,738  ms/op

基准代码:

@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(3)
public class StreamTest {

    private static final UnaryOperator<double[][]> streamTranspose() {
        return m -> {
            return range(0, m[0].length).mapToObj(r ->
                range(0, m.length).mapToDouble(c -> m[c][r]).toArray()
            ).toArray(double[][]::new);
        };
    }

    private static final UnaryOperator<double[][]> parallelStreamTranspose() {
        return m -> {
            return range(0, m[0].length).parallel().mapToObj(r ->
                range(0, m.length).parallel().mapToDouble(c -> m[c][r]).toArray()
            ).toArray(double[][]::new);
        };
    }

    private static final Function<double[][], double[][]> forLoopTranspose() {
        return m -> {
            final int rows = m.length;
            final int columns = m[0].length;
            double[][] transpose = new double[columns][rows];
            for (int r = 0; r < rows; r++)
                  for (int c = 0; c < columns; c++)
                    transpose[c][r] = m[r][c];
            return transpose;
        };
    }

    @State(Scope.Benchmark)
    public static class MatrixContainer {

        @Param({ "100", "1000", "3000" })
        private int matrixSize;

        private double[][] matrix;

        @Setup(Level.Iteration)
        public void setUp() {
            ThreadLocalRandom random = ThreadLocalRandom.current();
            matrix = random.doubles(matrixSize).mapToObj(i -> random.doubles(matrixSize).toArray()).toArray(double[][]::new);
        }

    }

    @Benchmark
    public double[][] streamTranspose(MatrixContainer c) {
        return streamTranspose().apply(c.matrix);
    }

    @Benchmark
    public double[][] parallelStreamTranspose(MatrixContainer c) {
        return parallelStreamTranspose().apply(c.matrix);
    }

    @Benchmark
    public double[][] forLoopTranspose(MatrixContainer c) {
        return forLoopTranspose().apply(c.matrix);
    }

}

关于java - 用于转置 double[][] 矩阵的紧凑流表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34861469/

相关文章:

java - 如何让服务器在单击按钮时向客户端发送消息

java - 解析格式错误的 json 字符串

Java 将字符串变量作为回调函数传递

java - 从并行流中收集结果

java - 实现功能接口(interface)以使用 InputStreams

java - 这应该很简单,在介绍编程后有寒假,但遇到了问题

java - 非法状态异常 : Cannot find changelog location: class path resource (liquibase)

matlab - (MATLAB) 线性方程的所有整数正解

Swift 负平方根

c++ - 从 Scilab 迁移到 C++