最近看《java核心技术》看到集合章节,在最后位集(BitSet)部分给出了一个示例程序,使用了埃拉托色尼筛选法(Eratosthenes Sieve)求自然数2~n范围的所有素数

代码如下:

import java.util.BitSet;public class Sieve {public static void main(String[] args) {int n = 2000000;long start = System.currentTimeMillis();BitSet bitSet = new BitSet(n+1);    //开辟2000001位的位集空间,从0位开始int count = 0;    //记录素数个数int i;for(i=2; i<=n; i++) {bitSet.set(i);    //将所有位置“开”状态(置1)}i = 2;while(i*i <= n) {        //从i=2开始遍历小于等于根号n所有整数if(bitSet.get(i)) {    //如果是素数count++;       //count增加int k = 2 * i;     //k为素数i的倍数,所以为合数,初始化为2倍while(k <= n) {bitSet.clear(k);    //清除位集中的第k位,置为“关”状态(置0)k += i;    //k倍增}}i++;    //i递增,最终增至大于根号n的最小整数}while(i <= n) {if(bitSet.get(i)) {count++;}i++;}long end = System.currentTimeMillis();System.out.println(count + " primes");System.out.println((end - start) + " milliseconds");}}

这个程序给定的n是2000000,结果并没有打印出所有素数,只是得出素数的个数为148933。

书中稍微提及了算法的基本操作:遍历一个拥有200万个位的位集,首先将所有的位都置为“开”状态,然后将已知素数的倍数所对应的位都置为“关”状态,经过这个操作保留下来的位所对应的就是素数。

作为一个码渣,在看过这个描述和代码之后,基本处于黑人问号脸状态。于是本着刨(zuan)根(niu)问(jiao)底(jian)的精神,决定仔细研究研究,好在最后终于大概明白了算法的基本思想,记录于此以备忘。

首先介绍两个很简单的预备点:

(1)一个合数 n 必有一个因子 i ≤ √n(根号n)

对于在程序中如何判断自然数 n(n > 2)是否为素数,自然而然想到的是枚举法:对 n 进行除法遍历运算,除数为小于 n 大于等于2的自然数,即用 n 依次除以 2、3、4、...、n-1,如果每一次除法都不能整除(有余数),则说明 n 为素数;否则 n 为合数,能整除的除数即为 n 的一个因子。

通过以上步骤可以看出,在程序中要判断自然数 n(n > 2)是否为素数,需要进行 n-2 次除法运算。对此可以进行优化,使除法运算次数减少。

现假设 n 为合数,有i_1 * i_2 = n,则 i_1 和 i_2 为 n 的两个因子,进而有i_1 * i_2 = √n * √n,则必有i_1 ≤ √n或者i_2 ≤ √n,即合数 n 必有一个因子小于等于√n。所以只要能够在√n 范围内找到 n 的一个因子,就可以判断出 n 为合数而非素数。据此进行优化即可将判断 n 是否为素数的除法运算次数减少至 [√n] -1 次([√n] 为小于√n的最大整数),只需用 n 依次除以2、3、4、... [√n] ,判断是否能够整除即可。

(2)一个合数必能分解为全部是素数的因子

假设合数 n 可以分解出非素数因子(合数因子),那么这个非素数因子当然还可以继续分解,直到所有因子全部为素数。

即 n 可以表示为全部是素数因子的乘积:n = i_1 * i_2 * i_3 * ... * i_j ,其中i_1、i_2、i_3、... 、i_j全部为素数。

综合以上两点可以得出:一个合数 n 必定有一个素数因子 i ≤ √n

========以下进入本算法主题==============

对于求自然数2~n 范围的所有素数,即等于筛掉区间[2, n]的所有合数。由上述结论一个合数 n 必定有一个素数因子 i ≤ √n ,可得出:区间[2, n]的任一合数m,必有一个素数因子 i_m ≤ √n。

将这个小于等于 √n 的素数因子 i_m 作为切入点,使 i_m 成倍增加(2*i_m, 3*i_m, ...),并限定i_m倍增的结果小于等于n,则必定能得到对应的在区间[2, n]的合数m。由于素数因子i_m ≤ √n,只要遍历2~[√n]中的素数,使这些素数倍增,就能得到区间[2, n]中的所有合数。筛去这些合数,剩下的就是自然数2~n范围的所有素数了。

以上就是埃拉托色尼筛选法(Eratosthenes Sieve)的基本思想。

可能有人会疑惑:又该如何遍历2~[√n]中的素数呢,因为这需要先找出来2~[√n]范围内的素数,之后才能进行遍历。

仔细想一想,其实这是个递归问题。因为找出2~[√n]范围内的素数,这个问题与初始问题——找出2~n范围的素数,是一致的,依然可以使用本算法,只不过将n换成了[√n],查找区间变小了,如此递归下去,最终问题将演变成找出2~2或2~3范围的素数,答案就显而易见了。

以上可以说是属于逆向分析,查找区间逐渐缩小以简化问题;以下进行正向分析,从第一个素数2开始,逐渐扩大区间。

以下进行正向分析,从第一个素数2开始推导,逐渐扩大查找区间,以验证本算法
1、对于第一个素数2 = √4,区间[2, 4]范围的所有合数必有素数因子小于等于2。将2倍增,增大一倍即得到以素数2为因子的合数4,然后筛除掉合数4,则在2~4范围只剩下了素数2、3。
2、对于在第1步中得到的2~4范围的素数2、3,有 3 = √9,区间[2, 9]范围的所有合数必有素数因子小于等于3。对素数2、3进行倍增:将素数2分别增大1倍、2倍、3倍可得到合数4、6、8,于是筛除4、6、8;将素数3分别增大1倍、2倍可得到合数6、9,于是筛除6、9。经过对第一步中得到的2~4范围剩余的素数进行遍历倍增并筛除其结果的操作,可知到2~9范围剩下的全部是素数:2、3、5、7。
3、同样按照本算法,遍历倍增第2步中得到的2~9范围的所有素数2、3、5、7,并筛除掉相应的合数,可将区间扩大为[2, 49],得到2~49范围的所有素数。因此,不断按照这种步骤运算,就可以得到任意范围的所有素数。
以上即为从第一个素数2开始,不断扩大素数区间的正向推导过程。

=========以下结合java代码来分析本算法的实现过程========
由于示例java代码是在集合章节,所以使用了位集(BitSet)。2~n为查找范围,这里n=2000000。在程序一开始构造了一个2000001位的位集,每一位的索引即对应一个数:第0位对应0,第1位对应1,...,第2000000位对应2000000。位集的所有位都被初始化为“开”状态(置1),“开”状态则表示当前位对应的数为素数,反之,“关”状态则说明当前位对应的数位合数。变量count用来记录素数的个数。
程序中变量 i 就表示2~n 范围的合数必存在的小于等于√n 的因子,初始化为第一个素数2。因此,在第一个while循环的判定条件中限定 i * i <= n,并在循环中递增。在第一个while循环内部,如果bitSet.get(i) 得到第 i 位为“开”状态,则count 增加1。变量 k 即为对因子 i 进行倍增所得到的合数,初始时先乘以2增加一倍,然后在内部的while循环中对 k 不断倍增并判断得到的合数是否小于等于n,符合条件则清除在位集中相应的第k位,置为“关”的状态(置0),这就相当于把2~ i * i 范围的合数全部筛除掉了,剩下的则全部为素数,如此就保证了 i 在递增的过程中,只有素数索引位的bitSet.get(i) 返回的是“开”状态结果。
在 i 递增到 i * i > n 的时候,第一个while循环退出,整个2~n范围的筛选过程结束,在位集中只有素数索引位保留为“开”状态,合数索引位全部被置为了“关”状态。最后再遍历剩下的 i ~ n 范围的素数,就得到了 2~n 范围的素数个数结果,有需要的话也可以把相应的素数保存或者打印。

以上就是对 埃拉托色尼筛选法(Eratosthenes Sieve)求素数算法思想的简单介绍,至于算法的复杂度问题,本人并不太擅长,就不在此献丑了。
(完)

本篇是自己对此算法的一个理解和记录,同时也是第一次写算法分析的文章,肯定有诸多问题未能察觉,诚恳接受各种指导和批评建议,并在以后努力改善。

埃拉托色尼筛选法(Eratosthenes Sieve)分析相关推荐

  1. 找质数算法之埃拉托色尼筛选法(Sieve of Eratosthenes算法)

    一.算法原理 一个合数总是可以分解成若干个质数的乘积,那么如果把质数(最初只知道2是质数)的倍数都去掉,那么剩下的就是质数了. 二.步骤 (1)先把1删除(1既不是质数也不是合数) (2)读取队列中当 ...

  2. 埃拉托色尼筛选法------筛选质数

    前戏:本篇介绍一种特定数据范围内统计该段数据内所有质数的高效算法,埃拉托色尼筛选法. 正文: 1.埃拉托色尼筛选法: 埃拉托色尼筛选法(the Sieve of Eratosthenes)简称埃氏筛法 ...

  3. Java 埃拉托色尼筛选法

    埃拉托色尼筛选法 埃拉托色尼筛选法 概念 步骤 优化 代码 埃拉托色尼筛选法 概念 埃拉托色尼筛选法(the Sieve of Eratosthenes)简称埃氏筛法,是古希腊数学家埃拉托色尼提出的一 ...

  4. Java实现埃拉托色尼筛选法

    1 问题描述 Compute the Greatest Common Divisor of Two Integers using Sieve of Eratosthenes. 翻译:使用埃拉托色尼筛选 ...

  5. c语言埃拉托色尼筛选法数组,埃拉托色尼筛选法 算法

    埃拉托色尼筛选法 埃拉托色尼选筛法(the Sieve of Eratosthenes)简称埃氏筛法,是古希腊数学家埃拉托色尼(Eratosthenes 274B.C.-194B.C.)提出的一种筛选 ...

  6. 素数处理-埃拉托色尼筛选法(埃式筛)

    素数处理-埃拉托色尼筛选法(埃式筛) 埃拉托色尼筛选法(The Sieve of Eratosthenes) 继欧拉筛之后,我今天补的一篇博客.名字太长了emm.简称就是埃式筛法. 埃筛只能解决1e7 ...

  7. Day5:计数质数(埃拉托色尼筛选法)

    leetcode地址:https://leetcode-cn.com/problems/count-primes/ Day5:计数质数 一. 问题背景: 统计所有小于非负整数 n 的质数的数量. 二. ...

  8. JavaScript实现sieveOfEratosthenes埃拉托色尼筛选法算法(附完整源码)

    JavaScript实现sieveOfEratosthenes埃拉托色尼筛选法算法(附完整源码) sieveOfEratosthenes.js完整源代码 sieveOfEratosthenes.js完 ...

  9. 埃拉托色尼筛选法 C++实现

    在公元前3世纪,古希腊天文学家埃拉托色尼发现了一种找出不大于n的所有自然数中的素数的算法,即埃拉托色尼筛选法. 具体筛选步骤: 这种算法首先需要按顺序写出2到n中所有的数. 然后把第一个元素画圈,表示 ...

最新文章

  1. 使用Pixel Bender Toolkit制作特效——多像素采样(Part 4)
  2. 36万类别、1800万图像,国内机构创建全球最大人脸数据集
  3. iOS Hacker Xcode7免登录开发者账号打包ipa
  4. 迷宫搜索问题最短路_【算法常用模板】总结(更新中)
  5. 在中国,真正达到月收入1万以上的有多少
  6. python的认识_Python学习之认识python
  7. 计算机病毒怎么做图片解说,【虎子_游戏解说】计算机病毒防范的实施方法
  8. headfirst设计模式(2)—观察者模式
  9. apache sentry
  10. Mad Libs游戏:熟悉python编程环境,基本输入输出
  11. npoi excel导入html数据库,C#_.NetFramework_Web项目_NPOI_EXCEL数据导入
  12. python 量化模型_量化策略 | Python tqsdk — GhostTrader模型!(附代码)
  13. 【图像分割】基于直觉模糊C均值聚类实现图像分割IFCMmatlab代码
  14. img文件制作linux启动u盘,制作Linux的U盘启动盘
  15. app内嵌h5一键加QQ群
  16. loadrunner icrosoft Visual c++2005 sp1 提示命令行选项语法错误,键入“命令/?”
  17. django arya插件对数据库操作使用,reverse发娘解析url的使用
  18. OVERLAPPED I/O 异步APC
  19. 机器学习笔记十九:正则化思想
  20. python爬取抖音粉丝数据_爬取抖音粉丝数据1(作品、喜欢、ID 、关注) 完整源代码...

热门文章

  1. 结合泛函极值_泛函极值及变分法讲义.doc
  2. python-计算字符个数
  3. [转] Delaunay三角剖分理论知识
  4. 树莓派学习(一) 如何 关机 重启
  5. 如何UNI-APP中使用iconfont彩色图标
  6. css 画一条水平直线和垂直竖线
  7. 零基础学Python———求一个字符串的每个字符重新组合排列python排列组合的数学运算(递归法)
  8. (转)XShell的安装和使用
  9. 按键控制c51单片机驱动unl2003控制步进电机正反转停止及程序调速-萌新入门
  10. 酒趣与酒情:聊聊酒吧和酒馆的不同