关于欧式筛和埃氏筛

筛质数的问题是给定一个整数N,统计或求出1-N之间所有的质数,称为质数的筛选问题

一.埃式筛

埃式筛的原理是任意一个整数的倍数都是合数。那么我们扫描每个数,将他所有小于N的倍数标记为合数。这样一直扫描,一旦扫描到的数没有被标记则这个数必然没有其他因子,这个数就为质数。

模拟过程如下:

2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,...

2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,...

2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,...

2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,...

2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,...

加粗的地方表示遍历到这个点,如果没有被标记则为质数。红色为标记。

关于埃氏筛的优化:

我们可以看到在上面的模拟过程中 先是2将他的所有倍数标记(4,6,8,10,12)但是当进行到3记3的倍数的时候我们会惊奇的发现,3同样会标记6与12,这样意味 2与3进行了一些重复标记。

我们可以发现当扫描到i的时候,小于i²的i的倍数必然在之前就被比i小的数扫描过了。因为,i前面的数在乘2,乘3这样倍增的时候必然乘过i,则i必然不是小于i²的i的倍数的最小因子,便会出现重复标记,我们可以直接从i²后面的i的倍数进行标记。这就是埃氏筛的优化

代码实现(这里只提供优化后的版本):

const int N =1e6+10;
int st[N];          //判断i是否被删除过(是否为合数),是合数st[i]为1
int h[N],tt;        //h[N]装所有小于n的质数,tt指向末尾元素加一

int n;              //给定一个整数n,求1-n一共有多少个质数

void get_primes(int n)    
{
    for(int i=2;i<=n;i++)    //从2到n遍历整个序列(注意1不是质数)
    {
        if(st[i]) continue;    //如果被标记,则直接进行下一次遍历
        h[tt++]=i;
        for(int j=i;j<=n/i;j++) st[j*i]=1; //写成j<=n/i而不是j*i<=n主要是怕j*i会爆int.
                                           //将所有i的j次倍都标记为合数                                                                    //tt为序列中一共有多少个质数
    }
}

时间复杂度:

O(NloglogN) 基本就是N后面添了一个常数,接近于线性

二.欧式筛(又称线性筛法,因为他的复杂度就是线性而不是接近)

在上面的埃氏筛中,2从4开始标记合数,3从9开始标记合数。我们会发现虽然免去了重复标记6,但最后这两个数还是免不了重复标记一个数:12 。究其根本原因是2是12的质因子但同样3也是12的质因子,那么有没有一种方法让每个数都被他的最小质因子标记。这就是接下来的欧式筛。

思路就是将所有质数放入一个数组里。i从2到遍历,将i与每个小于i的质数相乘筛去他们的乘积,直到i等于那个质数或那个质数是i的质因子的时候停下,这样子必然满足合数是由最小质因子筛去的(因为每个合数其实都可以看成一个很小的质数和一个数相乘得到)

代码实现:

const int N =1e6+10;
int st[N];          //判断i是否被删除过(是否为合数),是合数st[i]为1
int h[N],tt;        //h[N]装所有小于n的质数,tt指向末尾元素加一
int n;

void get_primes(int n)
{
  for(int i=2;i<=n;i++)         //线式筛是遍历n中所有元素,每次都让n乘上所有小于他的质数,来筛去合数
  {
      if(!st[i]) h[tt++]=i;     //只要不为合数则入队
      for(int j=0;h[j]<=n/i;j++)
      {
          st[h[j]*i]=1;
          if(i%h[j]==0) break;  //当i==h[j]或者h[j]是i的最小质因子的时候结束当前循环,进行下一个i
      }
  }
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾墨乄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值