给定一个正整数 n ,请你求出 1∼n 中质数的个数。
输入格式
共一行,包含整数 n 。
输出格式
共一行,包含一个整数,表示 1∼n 中质数的个数。
数据范围
1≤n≤106
输入样例:
8
输出样例:
4
一、朴素筛法
对于每个数,筛掉他的所有倍数
复杂度:2要筛n / 2 次,3要筛n / 3次,所以一共要筛n / 2 + n / 3 + …n / n,也就是 n * (1 / 2 + 1 / 3 + … + 1 / n)次,后面求和是调和级数,当 n 足够大时,等于lnn + c,c是常数,所以复杂度为 logn * n.
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
bool st[N];
int prime[N];
int n,ans = 0;
void get_prime(int n)
{
for(int i = 2; i <= n ; i ++ )
{
if(!st[i])
{
prime[ans ++ ] = i;
}
for(int j = 2 * i; j <= n; j += i )
st[j] = true;
}
}
int main()
{
cin>>n;
get_prime(n);
cout<<ans;
return 0;
}
二、埃氏筛法
对于上面的朴素筛法,可以发现不需要对每个数都进行筛倍数,只需要对质数筛即可,因为任何一个合数都会先被他的最小质因子筛掉。所以只需将筛倍数的循环放到 if 里面即可。
复杂度:质数定理:1 - n 中质数的数量是 n / lnn 个,所以埃氏筛法的复杂度为 n * loglog n,很接近线性了。
#include<bits/stdc++.h>//°£ÊÏɸ·¨
using namespace std;
const int N = 1000010;
int p[N],cnt;
bool st[N];
int main()
{
int n;
cin>>n;
for(int i = 2; i <= n; i ++ )
{
if(!st[i])
{
p[++ cnt] = i;
for(int j = i * 2; j <= n; j += i)
st[j] = true;
}
}
cout<<cnt;
return 0;
}
三、线性筛
线性筛法的核心是:n 只会被最小质因子筛掉,复杂度 n 。
例如 在埃氏筛中 6 会被 2 和 3 都筛一遍,增加了复杂度
代码中两种情况的解释:
当 i % pj == 0 时,pj 一定是 i 的最小质因子,因此 pj 一定是 pj * i 的最小质因子。
当 i % pj != 0 时,pj 一定小于 i 的最小质因子,因此 pj 一定是 pj * i 的最小质因子。
#include<bits/stdc++.h>//ÏßÐÔɸ·¨
using namespace std;
const int N = 1000010;
int primes[N],n,cnt;
bool st[N];
void get_primes(int n)
{
for(int i = 2; i <= n; i ++ )
{
if(!st[i])
primes[cnt ++ ] = i;
for(int j = 0; primes[j] * i <= n; j ++ )
{
st[primes[j] * i] = true;
if(i % primes[j] == 0)//pj是i的最小质因子了,j不能往后走了 2 3 6 j不能走到3
break;
}
}
}
int main()
{
cin>>n;
get_primes(n);
cout<<cnt;
return 0;
}