【数论】质数筛选法(埃拉托色尼筛法及线性筛法)

质数是指在大于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 prime[MAXN],ct=0; //存储找到的质数
int v[MAXN];         //记录0~MAXN是否为质数
 
void fprime(int b) //线性筛法找质数
{
    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;  //标记不为质数的数
      f(i%prime[j]==0) break;  //当遇到最小的质数是i的因数时,break
     }
   }
}
 

相关题目:洛谷 P1217 [USACO1.5]回文质数 Prime Palindromes

推荐:https://www.cnblogs.com/1625--H/p/9824516.html 该篇也讲的很详细

猜你喜欢

转载自www.cnblogs.com/oneonone/p/12451711.html