总目录
这一系列的文章旨在探讨如何最小化使用循环语句,尽最大可能使用Java Stream来完成有趣的与数学相关的运算。
之前的文章讲解了如何查找到素数,对素数而言,其中还有一个孪生素数的问题非常有趣,美籍中国数学家张益唐在前几年,在其五十多岁“高龄”完成了以往只能由二、三十岁年轻数学家才能完成的孪生素数性质证明,而震惊了世界。
所谓“孪生素数”是指,类似3和5,11和13,17和19这样的,两个同时为素数,这两个数只相差2的数字对
难点
- 使用Java Stream来处理这个问题的一个难点就是,Java数据流是无状态的,它没有顺序,没有前后,只有一个个单独出现的数据,从而使得查找有特定性质的单个数据时可能会很容易,但要想根据前一个数据来查看下一个数据就要很困难
- 对于这个问题,可以使用Stream的生成器来解决,生成器,生成的数据就是有天然顺序的,但是根据前一个值,来查找下一个值却是一个坎啊
判断素数的流代码
IntPredicate intPredicate = v -> {
if (v == 2 || v == 3 || v == 5) return true;
if (v % 2 == 0) return false;
int sqrtValue = Double.valueOf(Math.sqrt(v)).intValue();
return !(IntStream.iterate(3, oddValue -> oddValue + 2).limit(sqrtValue).parallel()
.filter(v1 -> v1 <= sqrtValue).anyMatch(v1 -> v % v1 == 0));
};
- 这是一个使用lambda表达式来返回一个IntPredicate接口的实例,这个接口的实现代码,是为了判断传入的参数是不是素数。如果是返回
true
,如果不是返回false
- 这段代码内容的详细解释可以参看之前的文章,这里就不再赘述了
如何让流可以感知上一次生成的值内容
- 孪生素数,首先要知道上一个值如果是素数,然后再判断当前的值是不是素数,如果都是就是孪生素数
- Java数据流生成器,是典型的熊瞎子,生成一个数,就抛弃一个数,只管生产,其他一概不再过问
- 所以通过单个流是根本没有办法知道上一个生成值的内容的
- 既然一个流不行,那就用两个流,stream1和stream2,这两个流每生成一个数据,就开始判断是不是相差为2的素数,如果是,妥妥的素数对就生成了
- 那么这两个数据流的内容就是关键了,首先,生成数据流,内容是奇数(偶数肯定不是素数)流,再将其筛选为只含有素数,内容如下
1 | 2 | 3 | 4 | 5 | 6 | 7 | …… | |
---|---|---|---|---|---|---|---|---|
stream1 | 2 | 3 | 5 | 7 | 11 | 13 | 17 | |
stream2 | 3 | 5 | 7 | 11 | 13 | 17 | 19 |
- 表格中第一行,是生成数列的顺序号,由于是生成器生成,所以有这个天然的顺序号
- 将相同顺序号的两个值,组成数据对,判断是不是相差为2
- 接下来的难点就是如何取得同样顺序号中的两个流中的值了
- 解决这个问题的方案就是遍历流中的数据了
iterator
主代码
private void getTwinPrimeWithTwoStream() {
IntStream intStream1 = IntStream.concat(
IntStream.of(2, 3, 5),
IntStream.iterate(7, i -> i + 2).filter(intPredicate));
IntStream intStream2 = IntStream.concat(
IntStream.of(3, 5),
IntStream.iterate(7, i -> i + 2).filter(intPredicate));
PrimitiveIterator.OfInt iterator1 = intStream1.iterator();
PrimitiveIterator.OfInt iterator2 = intStream2.iterator();
int count = 0;
while (iterator1.hasNext()) {
if (iterator2.hasNext()) {
int v1 = iterator1.next();
int v2 = iterator2.next();
if ((v2 - v1) == 2) {
System.out.printf("%,5d:%,d---%,d\n", ++count, v1, v2);
}
}
}
}
- 在生成两个数据流前,是使用硬编程的方式的把2、3、5硬插入到流中,非如此,无法准确判断出是是素数,这是
intPredicate
这个函数式接口实现中的缺陷,因为只有这三个数,所以没必要为了这三个数而把整个逻辑大改变 - 上述代码生成的无限流,也就是如果任其运行,是可以把64位(与计算机位数有关)长度的整数中全部素数对查找出来
- 如果限制其长度,加入在两个流中加入
limit(长度值)
即可,具体代码写法可以参见之前文章 - 通过代码运行的效果,可以知道这两个流的生成是在另外的线程中,因为while语句的执行,是根本不受阻碍的
- 这个代码的缺憾就是使用了while语句,这也是遍历数据流的,目前个人已知的唯一方法,如果有更好方法,还望不吝赐教
- 这是用两个流来生成素数对,下一篇再写如何用一个流来生成素数对