素数个数 <埃式筛 && 欧拉筛>
求 1 ~ 1e7 以内素数的个数
最普通做法(非常超时
int n;
bool judge(int x)
{
if(x == 1) return false;
for(int i = 2; i < x; i++)
{
if(x % i == 0) return false;
}
else return true;
}
int main()
{
cin >> n;
int count = 0;
for(int i = 2; i < n; i++)
{
if(judge(i)) count++;
}
cout << count;
}
普通做法(超时
//与上面不同的是将judge函数中的搜索范围减少
bool judge(int x)
{
if(x == 1) return false;
for(int i = 2; i <= x / i; i++) //循环中的结束条件更改了
{
if(x % i == 0) return false;
}
else return true;
}
//更改方法1: i <= sqrt(x) sqrt函数速度慢,每次循环都会计算一次;开根号可能有精度误差
//更改方法2: int num = sqrt(x) i <= num 这样只用计算一次sqrt(x)
//更改方法3: i * i <= x 不用sqrt也没有精度误差,但是当i特别大时,i * i可能会溢出变为负数
//更改方法4: i <= x / i 用除法速度可能较慢,但是数据很大时建议使用
埃式筛法(快
核心
筛去得到的素数的倍数 -> 合数
实现筛去
用一个bool类型的数组
或者 bitset
(会慢几十ms)来存储每个值是否是素数,当前素数的倍数则是true,后续的遍历只有满足false的条件(即未被筛去)才能进行
不足
一个元素可能会重复筛去,因为它同时是几个素数的倍数
例如
i = 2时,会筛掉4,6,8,10,12
,14....
i = 3时,会筛掉9,12
....
这里12就筛重复了(这是已经改进了一点的结果,没改进之前,int j = i * 2,重复筛掉的就更多,例如6)
- 利用了3个for,但是
重复筛去的数据更少
,1e6的数据平均在15ms
const int N = 1e7 + 10;
bool judge[N]; //bitset<N> judge; 初始化都为false
int n;
int main()
{
cin >> n;
int count = 0;
for(int i = 2; i <= n / i; i++)
{
if(!judge[i])
for(int j = i * i; j < n; j += i) judge[j] = true; //j = i * 2 稍微慢了一点
}
for(int i = 2; i <= n; i++)
{
if(!judge[i]) count++;
}
cout << count;
}
- 利用2个for,但是重复筛去的元素更多,1e6的数据平均在15ms(时间波动有点大)
const int N = 1e7 + 10;
bool judge[N]; //bitset<N> judge; 初始化都为false
int n;
int main()
{
cin >> n;
int count = 0;
for(int i = 2; i <= n; i++)
{
if(!judge[i])
{
count++;
for(int j = i * 2; j <= n; j += i) judge[j] = true; //这里相比上面,int j 不能初始化为i * i,会溢出;并且当 i > sqrt(n) 时仍然会筛掉元素,但是这些元素肯定已经被小于sqrt(n)的数给筛过了
}
}
cout << count;
}
欧式筛法(最快
核心
合数 = 最小的素数 * 另一个数
,只有prime[j]是最小的质因数时i才能被筛掉
例如 24 = 3 * 8 = 3 * 2 * 2 * 2 = 2 * 12,要在另一个数为12时才能筛掉,不然就会重复
1e6数据的时间<10ms
核心代码
if(i % prime[j] == 0) break;
代码实现
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int prime[N];
bool judge[N];
int main()
{
int n = 1e6, k = 0;
for(int i = 2; i <= n; i++)
{
if(!judge[i]) prime[k++] = i;
for(int j = 0; j < k; j++)
{
if(i * prime[j] > n) break;
judge[i * prime[j]] = true;
if(i % prime[j] == 0) break;
}//建议多列一串数字寻找原理,或者记下来
}
cout << k;
}