ajb*_*ajb 5 java java-8 java-stream
我正在尝试学习使用Stream API的细节,我给自己的一个任务是尝试编写一个无限的方法DoubleStream并尝试计算总和(假设它收敛).也就是说,我想写一个方法
public static double infiniteSum(DoubleStream ds) { ... }
Run Code Online (Sandbox Code Playgroud)
我可以用类似的东西打电话
double sum = infiniteSum(IntStream.iterate(1, (i -> i + 1))
.mapToDouble(n -> 1 / ((double)n * n)));
Run Code Online (Sandbox Code Playgroud)
得到的总和(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);
}
Run Code Online (Sandbox Code Playgroud)
这给了我一个精确到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;
}
Run Code Online (Sandbox Code Playgroud)
但这只是感觉就像恢复旧的方式,我正在寻找一种方法,使用更多的方式使用流,或其他东西.
这会产生正确的结果:
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;
}
Run Code Online (Sandbox Code Playgroud)
但我碰巧知道旧的方法使用了将近80万个术语,并且对流的限制违背了我的目的.问题是我没有看到除了使用之外切断流的方法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;
}
Run Code Online (Sandbox Code Playgroud)
跟踪表明在看到最后一个术语时会发生某些事情,但没有什么好处:在一种情况下,计算停止但程序仍然挂起,而在另一种情况下,它给了我一个可爱的小崩溃转储,我要报告给甲骨文.
那么有没有办法完成我正在寻找的那种东西?
(注意:我现在正在假设串行流.但是我认为这是一种可以从并行性中受益的问题,一旦我弄清楚它是如何工作的.)
如果终止条件和 \xe2\x80\x99s 结果之间存在这样的依赖关系Collector,则使用可变状态是不可避免的。但只要您不需要\xe2\x80\x99t 并行执行,实现就可以很简单:
class MutableDouble {\n double sum;\n}\nMutableDouble sumHolder=new MutableDouble();\nDoubleStream ds=IntStream.iterate(1, (i -> i + 1))\n .mapToDouble(n -> 1 / ((double)n * n));\nds.peek(term -> sumHolder.sum+=term)\n .filter(term -> Math.abs(term)<1e-12*Math.abs(sumHolder.sum))\n .findFirst();\nSystem.out.println(sumHolder.sum);\nRun Code Online (Sandbox Code Playgroud)\n\n由于它会在比较之前进行总结,因此与您原来的逻辑并不完全相同,而是类似于:
\n\ndouble sum = 0;\nfor (double n = 1; ; n++) {\n double term = 1 / (n * n);\n sum += term;\n if (Math.abs(term) <= 1e-12 * Math.abs(sum)) {\n break;\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n如果你坚持原来的逻辑,它必须稍微复杂一些:
\n\nclass MutableDouble {\n double sum, next;\n void add(double d) {\n sum=next;\n next+=d;\n }\n}\nMutableDouble sumHolder=new MutableDouble();\nDoubleStream ds=IntStream.iterate(1, (i -> i + 1))\n .mapToDouble(n -> 1 / ((double)n * n));\nds.peek(sumHolder::add)\n .filter(term -> Math.abs(term)<1e-12*Math.abs(sumHolder.sum))\n .findFirst();\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
912 次 |
| 最近记录: |