数论——质数筛法
一、埃拉托斯特尼(Eratosthenes)筛法
算法思想:
要得到自然数n以内的全部素数,必须把不大于 的所有素数的倍数剔除,剩下的就是素数。给出要筛数值的范围n,找出以内的素数。先用2去筛,即把2留下,把2的倍数剔除掉;再用下一个质数,也就是3筛,把3留下,把3的倍数剔除掉;接下去用下一个质数5筛,把5留下,把5的倍数剔除掉;不断重复下去......。
模板题链接:筛质数
代码实现:时间复杂度O(nloglogn)
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <cstdio> 5 const int N = 10000000+10; 6 int p[N]; 7 8 void prime(int n) 9 { 10 p[1]=1; 11 for(int i=2;i<=n;i++) 12 { 13 if(p[i])continue; 14 for(int j=2;j<=n/i;j++)p[i*j]=1; 15 } 16 } 17 18 int main() 19 { 20 int n;scanf("%d",&n); 21 prime(n); 22 int ans=0; 23 for(int i=1;i<=n;i++) 24 if(!p[i])ans++; 25 printf("%d\n",ans); 26 return 0; 27 }
二、欧拉(Euler)筛法
欧拉筛法弥补了埃氏筛法会重复筛去合数的缺陷,而保证了每个合数只会被其最小质因子筛去且只会被筛去一次,在筛出质数的同时还得出了每个数的最小质因子,一举两得,是数论中非常常用也非常重要的一个算法。
先上代码:时间复杂度O(n)
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <cstdio> 5 const int N = 10000000+10; 6 7 int primes[N],cnt; 8 int vis[N]; 9 10 void Euler_prime(int n) 11 { 12 for(int i=2;i<=n;i++) 13 { 14 if(!vis[i])primes[++cnt]=i; 15 for(int j=1;primes[j]<=n/i;j++) 16 { 17 vis[primes[j]*i]=1; 18 if(i%primes[j]==0)break; 19 } 20 } 21 } 22 23 int main() 24 { 25 int n;scanf("%d",&n); 26 Euler_prime(n); 27 28 printf("%d\n",cnt); 29 return 0; 30 }
算法理解:
上述第18行代码保证了primes[j]一定小于等于i的所有质因子(因为如果primes[j]枚举到i的某个质因子时便会跳出枚举的循环),故primes[j]是primes[j]*i的最小质因子。
下面证明:所有合数只会被其最小质因子筛去,且只会被筛一次。
对于任意一个合数k,可以表示成k=p*(k/p),p是k的最小质因子。由于p≤(k/p)的所有质因子(k/p就相当于上述代码中的i),所以只有当i枚举到k/p时k才会被筛去,而枚举到其他任何数皆不会筛去k。