素数(质数)是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。素数被广泛用于密码学、汽车变速箱齿轮设计、害虫的生物生长周期与杀虫剂使用之间的关系、导弹和鱼雷等领域上,具有重要意义。本文就来介绍求素数的一种方法:筛法


在初学编程时,我们解决问题的想法应该都是定义法。按照素数的定义——除了1和它本身以外不再有其他因数的大于1的自然数,我们可以这样判断一个整数x是否为素数:

首先,x(x>1)满足不能被1和x以外的其他数整除,即我们可以遍历2~x-1,看是否有整数能整除x,这种方法称为试商法。为此我们可以写出最初版的代码:

int IsPrime(int x)
{int i, flag = 1;if (x <= 1)//若x<=1,则不是素数flag = 0;for (i=2; i<x && flag; i++){if (x%i == 0)//若x能被2~x-1中的整数整除,则不是素数flag = 0;}return flag;//若是素数则返回1,否则返回0
}

但是当x较大时,循环的次数较多,效率低下。实际上,只要在2~x-1中发现了任意一个整数能够整除x,则可立刻判断x不是素数,此时已经可以跳出循环,不再进行重复无意义的工作,即:

int IsPrime(int x)
{int i, flag = 1;if (x <= 1)//若x<=1,则不是素数flag = 0;for (i=2; i<x && flag; i++){if (x%i == 0)//若x能被2~x-1中的整数整除,则不是素数{flag = 0;break;//只要找到第一个,即可跳出循环}}return flag;//若是素数则返回1,否则返回0
}

实际上,只需用2~sqrt(x)之间的整数去试商看是否能被整除即可(这里不给出数学证明,有兴趣的朋友可以自行查阅资料),为此我们可以把循环条件改为i<=sqrt(x),此时若x不是素数,循环次数将大大减少,可以进一步改进我们的代码。

int IsPrime(int x)
{int i, flag = 1;int squareRoot = sqrt(x);if (x <= 1)//若x<=1,则不是素数flag = 0;for (i=2; i<=squareRoot && flag; i++){if (x%i == 0)//若x能被2~sqrt(x)中的整数整除,则不是素数{flag = 0;break;//只要找到第一个,即可跳出循环}}return flag;//若是素数则返回1,否则返回0
}

若我们要实现求100以内的所有素数,可在主函数中调用IsPrime()函数如下:

int main()
{int i;for (i=1; i<=100; i++){if (IsPrime(i))printf("%d\n", i);}return 0;
}

但是试想,如果我们要实现求素数的范围较大(如100000以内),那么在遍历1~100000的循环中,每一次循环都要调用一次IsPrime()函数,且在函数中还要遍历2~sqrt(i),这样将会使程序的效率变得异常低下。事实上,我们完全可以找出较少的数(范围为2~sqrt(100000))后,把这些数的倍数全部筛掉,这样就省去了很多的判断步骤,这种方法称为筛法。这其实就是试商法中的算法原理:若2~sqrt(x)中有整数能够整除x,那么x一定不是素数,也就是若x不是素数,则一定存在某个数位于2~sqrt(x)中(记为i),x是i的倍数

那么如何实现这个算法呢?我们可以考虑用一个数组a来存储1~N中的所有数,并先把0与1筛去,即初始化数组a,使a[0]=0, a[1]=0, a[2]=2, a[3]=3, ......, a[N]=N;接着对i=2, 3, ......, sqrt(N)分别做:“筛掉a中的所有a[i]的倍数”,即当i+1<=j<=N时,若a[j]是a[i]的倍数,使a[j]=0;最后输出数组中余下的数(a[i]!=0的数)

据此,我们可编写代码如下:

int i, j, a[N+1] = {0};//假设N为常量
for (i=2; i<=N; i++)
{a[i] = i;//初始化a[2]~a[N]
}
for (i=2; i<=sqrt(N); i++)
{if (a[i]!=0)//跳过已被筛掉的数{for (j=i+1; j<=N; j++){if (a[j]!=0 && a[j]%a[i]==0)//a[j]!=0的目的是跳过已经被筛掉的数{a[j] = 0;}}}
}
for (i=0; i<N; i++)
{if (a[i])//如果a[i]!=0,则a[i]是素数{printf("%d\n", a[i]);}
}

而在这种方法中,我们发现,当输出素数时,既可以输出i,也可以输出a[i],那么就说明了有一个值是多余的。其实,我们完全可以使用a[i]当作标记变量,在一开始时把数组a初始化为0,让j遍历2~N,当我们找到非素数则使a[j]变为1,代表筛掉了非素数,最后只需输出元素a[j]=0的下标j即可。据此我们可以优化以上代码:

int i, j, a[N+1] = {0};//假设N为常量
a[0] = 1;
a[1] = 1;//0和1不是素数
for (i=2; i<=sqrt(N); i++)
{if (a[i]!=1)//跳过已被筛掉的数{for (j=i+1; j<=N; j++){if (a[j]!=1 && j%i==0)//a[j]!=1的目的是跳过已经被筛掉的数{a[j] = 1;}}}
}
for (i=0; i<N; i++)
{if (!a[i])//如果a[i]==0,则a[i]是素数{printf("%d\n", i);}
}

这样我们就可以省去把数组a初始化为a[2]=2,......,a[N]=N的一步了,从而减少了一个循环,提高了程序运行的效率。

在这里,我们判断j是否为i的倍数时,采用的时让j从i+1遍历到N,判断j对i求余是否为0的方法。这样做其实也是不必要的,因为相比于i的倍数来说,不是i的倍数的数要多得多。因此我们可以让j从2开始,直到i*j>N,这样我们就能直接找到i的倍数i*j,并让a[i*j]=1。

int i, j, a[N+1] = {0};//假设N为常量
a[0] = 1;
a[1] = 1;//0和1不是素数
for (i=2; i<=sqrt(N); i++)
{if (a[i]!=1)//跳过已被筛掉的数{j = 2;while (i*j<=N){a[i*j] = 1;j++;}}
}
for (i=0; i<N; i++)
{if (!a[i])//如果a[i]==0,则a[i]是素数{printf("%d\n", i);}
}

这就是代码经优化的最终版本啦!


筛法求素数的核心是依次筛掉2~sqrt(N)中素数的倍数,直到a中仅剩下素数为止,这种方法巧用数组,利用数组作为标志变量,是一种效率较高的求素数的算法。


参考文献:

[1]百度百科.

C语言丨筛法求素数(质数)相关推荐

  1. 筛法求素数c 语言,位筛法求素数,有段代码看不懂,有大佬可以来说一下

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 就是BITArray[ (i -3)/ CHAR_BIT ]其中i从0开始,那下标不就为负了?而指向的又是哪个数据?下面是完整代码. #include # ...

  2. 筛法求素数(C语言/C++)

    什么是素数 定义 在大于1的自然数中,除了1和它本身以外不再有其他因数的数称为质数. C语言实现判断素数 int prime(int x) {for(int i=2;i*i<=x;i++){if ...

  3. 线性筛法求素数c语言,[算法]素数筛法(埃氏筛法线性筛法)

    一.素数筛的定义 给定一个整数n,求出[1,n]之间的所有质数(素数),这样的问题为素数筛(素数的筛选问题). 二.埃氏筛法(Eratosthenes筛法) 埃氏筛法又叫做Eratosthenes筛法 ...

  4. 一般筛法求素数+快速线性筛法求素数

    一般筛法求素数+快速线性筛法求素数 标签: 正则表达式算法优化扩展c 2010-08-22 01:28 28738人阅读 评论(8) 收藏 举报  分类: 算法学习资料(5)  版权声明:本文为博主原 ...

  5. 一般筛法和快速线性筛法求素数 求素数的一点总结

    素数总是一个比较常涉及到的内容,掌握求素数的方法是一项基本功. 基本原则就是题目如果只需要判断少量数字是否为素数,直接枚举因子2 ..N^(0.5) ,看看能否整除N. 如果需要判断的次数较多,则先用 ...

  6. 普通素数 筛法求素数 二次筛法求素数 MillerRabin素数测试【模板】

    素数和合数共同的性质: 1.a > 1是合数,当且仅当a = b * c,其中1 < b < a,1 < c < a. 2.合数必有素数因子. 3.如果d > 1, ...

  7. CodeForce 236B Easy Number Challenge(筛法求素数 + 整数因式分解)

    题目链接:http://codeforces.com/problemset/problem/236/B Easy Number Challenge time limit per test 2 seco ...

  8. 1042: 筛法求素数

    1042: 筛法求素数 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 1387  Solved: 918 [Submit][Status][Web B ...

  9. 素数筛法求素数(线性时间)

    摘自:http://blog.csdn.net/once_hnu/article/details/6302283 逆向思维的典型应用! 1)普通方法判断素数:O(n*sqrt(n)) 2)筛法求素数: ...

  10. 数论 判断素数:普通素数判别 线性筛 二次筛法求素数 米勒拉宾素数检验

    普通的素数判断法 当我们要判断一个数字是否是素数的时候,往往会直接看这个数字模1到这个数字的根号,看有没有等于零的,从而判断这个数字是不是素数,这样做的时间复杂度为O(sqrt(n)) bool is ...

最新文章

  1. Elk5安装X-Pack
  2. Android中文输入的新选择——搜狗拼音输入法
  3. js 为什么0.1+0.2不等于0.3
  4. python中split_python中split()和split(' ')的区别
  5. 后台服务系统之dubbo架构
  6. 从输入 URL 到页面加载完的过程中都发生了什么事情?
  7. stata 线性回归分析基本操作
  8. 百行征信大揭秘,字段中间找关系
  9. BlackMamba:C2后渗透框架
  10. greensock下载_使用GreenSock构建可拖动的画布外菜单
  11. 2018西门子逻辑控制赛项6部10层电梯PLC
  12. 计算机更新bios,GIGABYTE How to Reflash VGA BIOS
  13. EasyDarwin开源云平台接入海康威视EasyCamera摄像机之快照获取与上传
  14. node 后台重定向_Node.js如何实现重定向
  15. uva424 Integer Inquiry
  16. HRT:使用Huge Pages进行低延迟优化
  17. 利用高德api定位当前位置
  18. 三星Galaxy之父×××网秦,网秦安全能否脱胎换骨?
  19. 程序员、架构师、技术经理、技术总监、CTO,怎么定位?
  20. 【编程不良人】SpringSecurity实战学习笔记07---授权

热门文章

  1. 修改HBuilder的livereload添加自定义响应模块
  2. HTML 日期格式转换
  3. 程序员的macOS系列:精选Mac App
  4. 车型代号对照表_车型代号对照表_相关文章专题_写写帮文库
  5. cad里面f命令用不了_南方CASS软件里CAD命令不能使用怎么办
  6. 拦截器与过滤器的区别
  7. wps小写金额转大写快捷键_WPS轻松办公—将数字转换中文大写的两种方法
  8. 记录一次CenterOS7中xmrig挖矿病毒的排查测试
  9. 使用 Lvs + Nginx 集群搭建高并发架构
  10. Windows 下使用苹果鼠标、键盘