素数筛(埃氏筛,区间筛,欧拉筛)

先了解一下素数筛是什么

  • 素数筛是一个可以快速生成素数表的算法

埃氏筛:

素数的倍数是合数
将所有的合数标记了,那剩下没有标记的便是素数

0 1 2 3 4 5 6 7 8 9 10 11 12 13
都不是 都不是 素数 素数 合数 素数 合数 素数 合数 合数 合数 素数 合数 素数

在表中我们可以看出从2开始,2是最小的素数,但2的倍数都是合数,此时没有被标记的数字中,最小的是3,但是它不能被更小的数字整除了(因为比它小的数字2的倍数已经标记完),所以它也是素数,3的倍数是合数,所以再次标记
每次标记完倍数后,剩下没有被标记的数字中最小的那个数一定也是素数

代码如下

const int N=1e7+1;
int prime[N],cnt=0,vis[N];
void get_prime()
{
    memset(vis,1,sizeof(vis));
    vis[0]=vis[1]=0;
    for (int i=2;i<=N;i++)
    {
        if (vis[i])
        {
            prime[++cnt]=i;
            for (int j=2;j*i<=N;j++)
                vis[j*i]=0;
     	}
   }
}

优化一下上方代码

const int N=1e7+1;
int prime[N],cnt=0,vis[N];
void get_prime()
{
    memset(vis,1,sizeof(vis));
    vis[0]=vis[1]=0;
    for (int i=2;i<=N;i++)
    {
     	if (vis[i])
        	for (int j=i*i;j<=N;j+=i)
            		vis[j]=0;
   }
}
 

区间筛

当题目数据较大时可以采用区间筛
区间[a, b)指的是所有满足a≤x 在素性判定这一小节中已经讲过,b以内的合数的最小质因数-定不超过√b。如果有√b以内的素数表的话,就可以把埃氏筛法运用在[a, b)上了。也就是说,先分别做好[2,√b)的表和[a, b)的表,然后从[2,√b )的表中筛得素数的同时,也将其倍数从[a, b)的表中划去,最后剩下的就是区间[a, b)内的素数了。


以上摘自白皮书,好的,我承认太懒了,我不想重打一遍代码了。。。。。


欧拉筛

  • 在埃氏筛的基础上,让每个合数都只被它的最小质因子筛选一次,可以达到不重复的目的,用来减少时间复杂度

贴个代码

int prim[N],n,num,vis[N];
void prime()
{
 	for(int i=2;i<=n;i++)
  	{
    		if(!vis[i])
      			prim[num++]=i;
    		for(int j=0;j<n/i;j++)
 	   	{
      			vis[i*prim[j]]=1;
       			if(i%prim[j]==0) 
       				break;
 		}
 	}
} 

其中 if(i%prim[j]==0) 是欧拉筛的关键,因为这样就可以使每个合数只被最小质因子筛选一次了,举个栗子,比如现在i=1,素数表中存入了2,3,5,7,11,i2=24标记一次,但是同时12%2= =0,那就退出循环,因为123可以用18*2代替,如果此时(12)筛选了一次,那(18)还得再筛选一次,所以if避免了大量循环


此时我们就可以去做周任务中的POJ的2689了
题目大概意思是,L——R中找出相邻素数最大和最小的距离(如果存在的话),但L,R的数据较大,所以需要用到区间筛

你可能感兴趣的:(素数筛(埃氏筛,区间筛,欧拉筛))