在Java lambda中使用两个流来计算协方差

假设我有两个double数组。 我一直在尝试使用Java 8中的Stream。我想我已经理解了主要的想法,但后来我意识到我不确定如何同时操作两个Streams。

例如,我想计算两个数组的协方差。

public class foo { public static double mean(double[] xs) { return Arrays.stream(xs).average().getAsDouble(); } public static void main(String[] args) { double[] xs = {1, 2, 3, 4, 5, 6, 7, 8, 9}; double[] ys = {1517.93, 1757.78, 1981.1, 2215.73, 2942.66, 3558.32, 4063.91, 4521.16, 5101.76, 5234.12}; System.out.println("Mean of xs: " + mean(xs)); double xs_sumDeviation = Arrays.stream(xs) .boxed() .mapToDouble(d -> d.doubleValue() - mean(xs)) .sum(); // Covariance double covXY = Arrays.stream(xs, ys) .mapToDouble(x,y -> { double numerator = (x-mean(xs)* (y-mean(ys); double denominator = Math.sqrt((x-mean(xs)* (x-mean(xs)); return numerator / denominator; }) .sum(); } } 

谢谢你的建议。

尝试1。

 public static double covariance(double[] xs, double[] ys) { double xmean = mean(xs); double ymean = mean(ys); double numerator = IntStream.range(0, Math.min(xs.length, ys.length)) .parallel() .mapToDouble(i -> (xs[i] - xmean) * (ys[i] - ymean)) .sum(); double denominator = Math.sqrt(IntStream.range(0, xs.length) .parallel() .mapToDouble(i -> (xs[i] - xmean) * (xs[i] - xmean)) .sum()); return numerator / denominator; 

在其他编程语言中,存在某种类型的zip函数,它需要几个迭代 ,并返回一个迭代器 ,它聚合来自每个迭代的元素。 例如,请参阅Python库中的函数zip 。

尽管可以在Java中创建类似的函数,但很难以这种方式实现它,它支持高效的并行执行。 但是,Java中有一种常用的模式,有点不同。 在您的情况下,它可能看起来如下:

 public static double covariance(double[] xs, double[] ys) { double xmean = mean(xs); double ymean = mean(ys); return IntStream.range(0, Math.min(xs.length, ys.length)) .parallel() .mapToDouble(i -> { double numerator = (xs[i] - xmean) * (ys[i] - ymean); double denominator = ...; return numerator / denominator; }) .sum(); } 

您可以使用所有索引创建一个IntStream ,而不是组合两个流,并通过索引访问不同集合的元素。 只要集合支持随机访问操作,这种方法就能很好地工作。

其他FP语言具有zip操作,它从两个元素流中产生一对对 。 这在Java中尚未提供。

但是,对于数组,您可以轻松创建数组索引流,让函数关闭数组,并通过index参数访问它们。

我也应该警告你,在这一行

 .mapToDouble(d -> d.doubleValue() - mean(xs)) 

你正在使你的程序复杂度为O(n 2 ),因为在每一步都会重新计算mean 。 你应该预先计算并关闭lambda中的结果。