私は Stream API の使用方法の詳細を学ぼうとしています。私が自分自身に与えた課題の 1 つは、無限を取りDoubleStream
、合計を計算しようとするメソッドを作成しようとすることでした (収束すると仮定します)。つまり、メソッドを書きたいのです
public static double infiniteSum(DoubleStream ds) { ... }
次のようなもので呼び出すことができます
double sum = infiniteSum(IntStream.iterate(1, (i -> i + 1))
.mapToDouble(n -> 1 / ((double)n * n)));
合計 (1 + 1/2 2 + 1/3 2 + ... ) = ζ(2) = π 2 /6 を取得します。
これを古い方法で行うための私の大雑把な方法:
public static void yeOldeWaye() {
double sum = 0;
for (double n = 1; ; n++) {
double term = 1 / (n * n);
if (Math.abs(term) <= 1e-12 * Math.abs(sum)) {
break;
}
sum += term;
}
System.out.println(sum);
}
これにより、5 桁まで正確な結果が得られます。
以下を使用して、ハッキングされた方法でメソッドを実装できますiterator()
。
public static double infiniteSum1(DoubleStream ds) {
double sum = 0;
PrimitiveIterator.OfDouble it = ds.iterator();
while (true) {
double term = it.next();
if (Math.abs(term) <= 1e-12 * Math.abs(sum)) {
break;
}
sum += term;
}
return sum;
}
しかし、それは古い方法に戻ったような気がします。私は、ストリームを意図した方法で使用する方法などを探していました。
これにより、正しい結果が得られます。
private static class DoubleAccumulator {
public double sum;
public DoubleAccumulator() {
sum = 0;
}
}
public static double infiniteSum(DoubleStream ds) {
DoubleAccumulator summer = ds.limit(800000).collect
(DoubleAccumulator::new,
(s, d) -> s.sum += d,
(s1, s2) -> s1.sum += s2.sum);
return summer.sum;
}
しかし、たまたま、古い方法では 800000 語近くを使用していたことがわかりました。ストリームに制限を設けると、目的が達成できなくなります。問題は、 を使用する以外にストリームを遮断する方法が見当たらないことですlimit()
。つまり、何個のタームがあるかを事前に知っておく必要があります。ストリームで見ているものに基づいて計算された条件に基づいてストリームを停止する方法がわかりません。
これは機能しません:
public static double infiniteSum(DoubleStream ds) {
DoubleAccumulator summer = ds.collect
(DoubleAccumulator::new,
(s, d) -> { if (Math.abs(d) <= 1e-12 * Math.abs(s.sum)) {
ds.close(); // AAACK
} else
s.sum += d;
},
(s1, s2) -> s1.sum += s2.sum);
return summer.sum;
}
トレースは、最後の用語が表示されたときに何かが発生したことを示していますが、何も良いことはありません: あるケースでは、計算は停止しましたが、プログラムはまだハングしていました。オラクル。
それで、私が探しているようなことを達成する方法はありますか?
(注: 今のところ、シリアル ストリームを想定しています。しかし、これは、並列処理を機能させる方法を理解すれば、並列処理の恩恵を受けることができる種類の問題だと思います。)