Java Stream来写算法05——筛选法找质数(素数)

16 篇文章 0 订阅
16 篇文章 0 订阅

总目录

今天使用的方法不再是上篇文章中的遍历法,遍历法非常清晰,非常容易理解,但架不住效率慢,所以介绍一下埃拉托斯特尼的筛选法,方法的本质是把在某一范围内素数的倍数去除掉,剩下的就是新的素数,比如想找出100以内的素数,只需要先找出10以内的素数,然后用10以内的素数与100以内全部的数进行除法运算,如果发现有一个可以整除,就筛掉这个数,经过这样一番操作,没被筛掉的数就是素数了。
发点小感慨,这个埃拉托斯特尼Eratosthenes,是距今二千多年的希腊大师,最让人大跌眼镜的操作就是率先计算出地球的周长(不可思议的是他们那时候就认为地球是球形了),另外开挂的表现就是绘制早期地图。这些希腊先哲创造的辉煌因为战争,知识的传承被中断了,但幸好,阿拉伯人把他们的著作翻译成了自己的文献,而欧洲经历千年的中世纪黑暗后,在文艺复兴期间又把阿拉伯人的文献再翻译过来,这才为西方科学文明的飞跃式发展插上腾飞的翅膀。仔细想一想,如果这期间有一个小小的环节出现纰漏,现代文明根本不复存在,人类将不过是只知道生存的动物而已。所以人类真的是上天极其眷顾的宠儿啊,除了心怀感恩,回报人生的幸运,可能这才是人之所以为人的意义了。

代码思路

  • 例如,指定在100的范围内查找全部素数,那么首先要在10以内把全部素数找出来,所以先要用代码生成这样一个数据流
  • 将上面的生成的数据流做为从10到100数字范围内,筛选素数的除数,如果发现有一个是可以整除的,就是把这个数去掉,遍历每一个数后,结果就是把素数保留下来了
  • 比如:10以内的素数为:2,3,5,7,然后从10开始,与这四个素数相除,发现只要与其中一个素数可以整除,就筛掉

生成指定范围查找素数代码

private IntStream getPrimeStream(int ceilValue) {
    return IntStream.rangeClosed(2, ceilValue).filter(v -> {
        if (v == 2 || v == 3 || v == 5) return true;
        // 这三个素数特别,如果不单独列出来,分分钟告诉你不是素数
        if (v % 2 == 0) return false;// 是2的倍数,肯定不是素数
        int sqrtValue = Double.valueOf(Math.sqrt(v)).intValue();
        return !(IntStream.iterate(3, oddValue -> oddValue + 2).limit(sqrtValue)
            .filter(v1 -> v1 <= sqrtValue).anyMatch(v1 -> v % v1 == 0));
    });
}
  • 上面代码中,生成数列时,使用了rangeClosed, 这样生成的数列可以准确设定范围,使用iterate生成的数列,必须要使用limit方法来限制其长度,否则会永远生成新的数
  • 从第6行开始,就是判断素数的遍历法算法了,求出基础素数还是要用遍历法的
  • 而求一个数是否是素数,只要从3开始遍历直到这个数的开根号值的整数部分的奇数即可,又可以大大减少运算数据的量
  • 为什么是从3开始,因为之前的条件,已经把偶数筛选出去了,因为没有偶数,所以在遍历时,不需要把偶数也遍历,所以生成的数列是从3开始的奇数
  • IntStream.iterate(3, oddValue -> oddValue + 2)就生成了这样的奇数数列
  • 为什么又加了一个filter呢,因为数列中只有奇数,所以数列中就会有大于sqrtValue的值,这样的值不必参与运算,就过滤掉

筛选法代码

private void filterEratosthenes(int range) {
    int sqrtValue = Double.valueOf(Math.sqrt(range)).intValue();
    int[] ints = IntStream.concat(getPrimeStream(sqrtValue), IntStream.rangeClosed(sqrtValue + 1, range)
            .filter(v -> !(getPrimeStream(sqrtValue).anyMatch(vp -> v % vp == 0)))).toArray();
    System.out.println(ints.length);
}
  • 这其中的逻辑比较简单了,就把第一部分生成的数列,逐个与包含每个数的数列进行除法运算,可以整除就筛选掉
  • 这其中使用的两数据流的合并,为什么要合并呢?
  • 比如,计算100的全部素数,首先通过第一部分代码,把10以内的素数都求出来,那么在使用筛选法时,就不必从头开始,而是只需要从11开始查找到100中间的素数,并将其生成一个新的数列
  • 这样一来,就需要把两个数列合并起来,需要注意的是,数列不可复用,所以在代码里调用了两遍第一部分代码
  • 两部分代码中没有使用任何循环语句
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值