线性筛素数、欧拉函数

社会主义新天地 提交于 2021-01-12 04:38:38

判断一个数n是否是素数,众所周知可以用O(sqrt(n))的方法。

但是如果要求很多个数,这个方法就不太好了。(比如所有小于n的数,复杂度就是O(n1.5)。)

 

埃拉托斯特尼筛法,大家都听说过。从2到n,去掉每个数的倍数,剩下来的就是质数。

不过这个方法会重复删除,比如6是2、3的倍数,会被删2次,因子越多,删的次数就越多。

 

改进之后的线性筛保证每个数只被最小的质因子删,所以是O(n)的。

#include<cstdio>
#include<cstring>
#define MAXN 100005
#define MAXL 1299710
int prime[MAXN];
int check[MAXL];

int tot = 0;
memset(check, 0, sizeof(check));
for (int i = 2; i < MAXL; ++i)
{
  if (!check[i])
  {
    prime[tot++] = i;
  }
  for (int j = 0; j < tot; ++j)
  {
    if (i * prime[j] > MAXL)
    {
      break;
    }
    check[i*prime[j]] = 1;
    if (i % prime[j] == 0)
    {
      break;
    }
  }
}
View Code

 

tot是计数用的,prime保存质数,check是判断是否是质数。

1.任意一个合数 A = p1p2...pn,(其中p1<=p2<=...<=pn) ,i=p2p3...pn时会删掉。

2.A只会被p1删掉。若i是prime[j]的倍数,A不会被p[j+1]删掉,当i=A/p[j+1]时,i%p[j+1]==0,break。如果不退出,A就被p[j+1]删了。

可以看出,这个方法需要额外的prime数组。而埃氏筛不必要。

 

 


 

顺便可以求欧拉函数

#include<cstdio>
#include<cstring>
#define MAXN 100005
#define MAXL 1299710
int prime[MAXN];
int check[MAXL];
int phi[MAXL];
int tot = 0;
phi[1] = 1;
memset(check, 0, sizeof(check));
for (int i = 2; i < MAXL; ++i)
{
  if (!check[i])
  {
    prime[tot++] = i;
    phi[i] = i - 1;
  }
  for (int j = 0; j < tot; ++j)
  {
    if (i * prime[j] > MAXL)
    {
      break;
    }
    check[i*prime[j]] = 1;
    if (i % prime[j] == 0)
    {
      phi[i*prime[j]] = phi[i] * prime[j];
      break;
    }else
    {
      phi[i*prime[j]] = phi[i] * (prime[j]-1);
    }
  }
}
View Code

 

n为质数,phi(n)=n-1

n为合数,进行质因数分解。$\large n = p_1^{k_1}\times p_2^{k_2}\times ... \times p_n^{k_n}$

$$\large \varphi(n) = n \prod\limits_{i = 1} ^ {n} \frac{p_i - 1}{p_i}$$

筛掉n=i*prime[j]时,求$\varphi(n)$,筛和求是同步的,也是O(n)。

设p1是n的最小质因子,n'=n/p1。

若n'%p1=0,即k1>1,n'含有n的所有质因子,则有

 $$\large \begin{align} \varphi(n)&= n \prod\limits_{i = 1} ^ {n} \frac{p_i - 1}{p_i} \\ &= p_1 \times n' \prod\limits_{i = 1} ^ {n} \frac{p_i - 1}{p_i} \\ &= p_1 \times \varphi(n') \\ \end{align}$$

若n'%p1≠0,即k1=1,n'与p1互质,有

$$\large \begin{align} \varphi(n)&= n \prod_{i = 1} ^ {n} \frac{p_i - 1}{p_i} \\ &= p_1 \times n' \times \frac{p_1-1}{p_1} \prod\limits_{i = 2} ^ {n} \frac{p_i - 1}{p_i} \\ &= (p_1-1) \times \varphi(n') \\ \end{align}$$

 (这也证明了欧拉函数是积性函数)

 

ref:

代码全是抄这里的。

欧拉函数的公式全是抄这里的。

 

 

延伸:

只筛奇数。

我推荐这个算法! 易于理解。 只算奇数部分,时空效率都还不错!  
half=SIZE/2;   
int sn = (int) sqrt(SIZE);   
for (i = 0; i < half; i++)   
   p[i] = true;// 初始化全部奇数为素数。p[0]对应3,即p[i]对应2*i+3   
for (i = 0; i < sn; i++) {      
if(p[i])//如果 i+i+3 是素数  
{       //每次j+=k,2*(j+k)+3=(2*j+3)+2k,也就是在上一个筛的数上+2k
    for(k=i+i+3, j=k*i+k+i; j < half; j+=k)   
    // 筛法起点是 p[i]所对应素数的平方 k^2                                          
    // k^2在 p 中的位置是 k*i+k+i  
    //    下标 i         k*i+k+i  
    //对应数值 k=i+i+3   k^2           
       p[j]=false;   
}   
}   
//素数都存放在 p 数组中,p[i]=true代表 i+i+2 是素数。  
//举例,3是素数,按3*3,3*5,3*7...的次序筛选,因为只保存奇数,所以不用删3*4,3*6.... 
View Code

来自:https://blog.csdn.net/dinosoft/article/details/5829550

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!