质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
在编程中,有时我们会遇到要找出一个整数范围内所有质数的情况,如果该范围很大,逐个判定该范围内所有数是否为质数(找出它是否能被大于1小于自己的整数整除)就会浪费大量时间,造成TLE。
在这时,我们就需要质数筛选法,直接筛出一个整数范围内的所有质数。
1.埃拉托色尼筛法
时间复杂度 O(nlogn)
希腊学者埃拉托色尼(公元前275-公元前193,第一个算出地球周长的人)提出的质数筛选法充分利用了质数的特性,非常好理解。
基本原理:任意整数 x 的倍数 2x,3x,... 等都不是质数 。
例:找出1-50内所有的质数
第一步:2为质数,标记4,6,8,10……50为合数
第二步:3为质数,标记6,9,12,15……48为合数
第三步:4已被标记为合数,标记8,12,16……48为合数
第四步:5为质数,标记10,15,20……50为合数
(从小到大执行到某个数时,若它未被标记,它即为质数)
……
不断进行下去,我们就可以标记出1-50所有的质数和合数。十分简单,所以我不提供代码实现。
2.线性筛法
时间复杂度 O(logn)
在上面的方法我们发现,有很多重复标记的现象。例如12既会被2又会被3标记,根本原因是没有找到唯一产生12的方式。
基本原理:每个合数必为它的最小质因子和一个最大因数(除它本身)的乘积。(质数的最小质因子为它本身,最大因数(除它本身)为1)
我们用这个最大因数(除它本身)把合数筛掉,避免重复。
也是从2开始筛选,每一步,将当前数与已经找出来的质数中小于等于它的最小质因子的相乘,把乘积作为合数筛掉。
例:找出1-50内所有的质数
第一步:2为质数,标记4为合数
第二步:3为质数,标记6为合数
第三步:4为合数,标记8为合数
第四步:5为质数,标记10,15为合数
(从小到大执行到某个数时,若它未被标记,它即为质数)
……
线性筛法函数的代码实现:
int v[MAXN]; //记录0~MAXN是否为质数
{
for(int i=2;i<=b;i++) //找到2到b内的所有质数
{
if(v[i]==0) //v[i]没被筛过,v[i]为质数
prime[++ct]=i; //储存第cnt个质数
for(int j=1;j<=ct;j++) //遍历小于i的所有质数
{
if(i*prime[j]>b) break; //无需判断比b大的数是质数还是偶数
v[i*prime[j]] = 1; //标记不为质数的数
}
}
}
相关题目:洛谷 P1217 [USACO1.5]回文质数 Prime Palindromes
推荐:https://www.cnblogs.com/1625--H/p/9824516.html 该篇也讲的很详细