描述
小Ho:小Hi,上次我学会了如何检测一个数是否是质数。于是我又有了一个新的问题,我如何去快速得求解[1,N]这个区间内素数的个数呢?
小Hi:你自己有什么想法么?
小Ho:有!我一开始的想法是,自然我们已经知道了如何快速判定一个数是否是质数,那么我就直接将[1,N]之间每一个数判定一次,就可以得到结果。但我发现这个方法太笨了。
小Hi:确实呢,虽然我们已经通过快速素数检测将每一次判定的时间复杂度降低,但是N个数字的话,总的时间复杂度依旧很高。
小Ho:是的,所以后来我改变了我的算法。我发现如果一个数p是质数的话,那么它的倍数一定都是质数。所以我建立了一个布尔类型的数组 isPrime,初始化都为true。我从2开始枚举,当我找到一个isPrime[p]仍然为true时,可以确定p一定是一个质数。接着我再将N以内 所有p的倍数全部设定为isPrime[p*i]=false。
写成伪代码为:
isPrime[] = true primeCount = 0 For i = 2 .. N If isPrime[i] Then primeCount = primeCount + 1 multiple = 2 While (i * multiple ≤ N) isPrime[i * multiple] = false multiple = multiple + 1 End While End If End For
小Hi:小Ho你用的这个算法叫做Eratosthenes筛法,是一种非常古老的质数筛选算法。其时间复杂度为O(n log log n)。但是这个算法有一个冗余的地方:比如合数10,在枚举2的时候我们判定了一次,在枚举5的时候我们又判定了一次。因此使得其时间复杂度比O(n)要 高。
小Ho:那有没有什么办法可以避免啊?
小Hi:当然有了,一个改进的方法叫做Eular筛法,其时间复杂度是O(n)的。
输入
第1行:1个正整数n,表示数字的个数,2≤n≤1,000,000。
输出
第1行:1个整数,表示从1到n中质数的个数
- 样例输入
-
9
- 样例输出
-
4
思路{
前天晚自习时看刘汝佳紫书看见一种玄学复杂度的log筛法(然而我并不会证明。。。。。)
居然比正解线性筛快了14ms @zjo。。。。。。
}
呐线性筛1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<vector> 6 #include<queue> 7 #include<ctime> 8 #include<cmath> 9 #include<map> 10 #include<set> 11 #define MAXX 1000001 12 using namespace std; 13 int in[MAXX],ans,n; 14 bool ha[MAXX]; 15 int main(){ 16 scanf("%d",&n); 17 for(int i=2;i<=n;++i){ 18 if(!ha[i])in[++ans]=i; 19 for(int j=1;j<=ans;++j){ 20 if(in[j]*i>n)break; 21 ha[in[j]*i]=true; 22 if(!(i%in[j]))break; 23 } 24 }printf("%d",ans); 25 return 0; 26 }
呐我神奇的刘汝佳log筛
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<vector> 6 #include<queue> 7 #include<ctime> 8 #include<cmath> 9 #include<map> 10 #include<set> 11 #define MAXX 1000001 12 using namespace std; 13 long long n,m,ans; 14 bool in[MAXX]; 15 int main(){ 16 scanf("%lld",&n); 17 for(long long i=2;i<=n;++i)if(!in[i]){ 18 ans++; 19 for(long long j=i*i;j<=n;j+=i)in[j]=true; 20 }printf("%lld",ans); 21 return 0; 22 }