C语言丨筛法求素数(质数)
素数(质数)是指在大于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语言丨筛法求素数(质数)相关推荐
- 筛法求素数c 语言,位筛法求素数,有段代码看不懂,有大佬可以来说一下
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 就是BITArray[ (i -3)/ CHAR_BIT ]其中i从0开始,那下标不就为负了?而指向的又是哪个数据?下面是完整代码. #include # ...
- 筛法求素数(C语言/C++)
什么是素数 定义 在大于1的自然数中,除了1和它本身以外不再有其他因数的数称为质数. C语言实现判断素数 int prime(int x) {for(int i=2;i*i<=x;i++){if ...
- 线性筛法求素数c语言,[算法]素数筛法(埃氏筛法线性筛法)
一.素数筛的定义 给定一个整数n,求出[1,n]之间的所有质数(素数),这样的问题为素数筛(素数的筛选问题). 二.埃氏筛法(Eratosthenes筛法) 埃氏筛法又叫做Eratosthenes筛法 ...
- 一般筛法求素数+快速线性筛法求素数
一般筛法求素数+快速线性筛法求素数 标签: 正则表达式算法优化扩展c 2010-08-22 01:28 28738人阅读 评论(8) 收藏 举报 分类: 算法学习资料(5) 版权声明:本文为博主原 ...
- 一般筛法和快速线性筛法求素数 求素数的一点总结
素数总是一个比较常涉及到的内容,掌握求素数的方法是一项基本功. 基本原则就是题目如果只需要判断少量数字是否为素数,直接枚举因子2 ..N^(0.5) ,看看能否整除N. 如果需要判断的次数较多,则先用 ...
- 普通素数 筛法求素数 二次筛法求素数 MillerRabin素数测试【模板】
素数和合数共同的性质: 1.a > 1是合数,当且仅当a = b * c,其中1 < b < a,1 < c < a. 2.合数必有素数因子. 3.如果d > 1, ...
- CodeForce 236B Easy Number Challenge(筛法求素数 + 整数因式分解)
题目链接:http://codeforces.com/problemset/problem/236/B Easy Number Challenge time limit per test 2 seco ...
- 1042: 筛法求素数
1042: 筛法求素数 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 1387 Solved: 918 [Submit][Status][Web B ...
- 素数筛法求素数(线性时间)
摘自:http://blog.csdn.net/once_hnu/article/details/6302283 逆向思维的典型应用! 1)普通方法判断素数:O(n*sqrt(n)) 2)筛法求素数: ...
- 数论 判断素数:普通素数判别 线性筛 二次筛法求素数 米勒拉宾素数检验
普通的素数判断法 当我们要判断一个数字是否是素数的时候,往往会直接看这个数字模1到这个数字的根号,看有没有等于零的,从而判断这个数字是不是素数,这样做的时间复杂度为O(sqrt(n)) bool is ...
最新文章
- Elk5安装X-Pack
- Android中文输入的新选择——搜狗拼音输入法
- js 为什么0.1+0.2不等于0.3
- python中split_python中split()和split(' ')的区别
- 后台服务系统之dubbo架构
- 从输入 URL 到页面加载完的过程中都发生了什么事情?
- stata 线性回归分析基本操作
- 百行征信大揭秘,字段中间找关系
- BlackMamba:C2后渗透框架
- greensock下载_使用GreenSock构建可拖动的画布外菜单
- 2018西门子逻辑控制赛项6部10层电梯PLC
- 计算机更新bios,GIGABYTE How to Reflash VGA BIOS
- EasyDarwin开源云平台接入海康威视EasyCamera摄像机之快照获取与上传
- node 后台重定向_Node.js如何实现重定向
- uva424	Integer Inquiry
- HRT:使用Huge Pages进行低延迟优化
- 利用高德api定位当前位置
- 三星Galaxy之父×××网秦,网秦安全能否脱胎换骨?
- 程序员、架构师、技术经理、技术总监、CTO,怎么定位?
- 【编程不良人】SpringSecurity实战学习笔记07---授权