素数的筛法(欧拉筛法)及其改进
欧拉筛法的算法分析:
1.使用一个prime[N]存下当前素数
2.使用一个on[N]数组标志i 是不是素数。
3.每一个素数都将其当前的i倍数标记为非素数(凡是有因子的数一定不是素数)。
处理函数
void init(){
on[0] = on[1] = 1;
//0和1不是素数
for(int i = 2; i <= n; ++i){
if(!on[i]) prime[++cnt] = i;
素数存入栈中
for(int j = 1; j <= cnt && i * prime[j] <= n; ++j){
on[i * prime[j]] = 1;
标记为不是素数
if(i % prime[j] == 0) break;
此处是精髓,如果当前的i能够被整除,那么这个数之前一定已经被处理过了,
(欧拉筛法的关键)
}
}
return ;
}
欧拉筛法的关键
i % prime[j] == 0 这一步到底有什么用?
答:保证每个数只被它的最小质因子筛一次。(比较难以理解)
我们假设 i % prime[j] = 0成立,那么设k = i / prime[j] 则prime[j]*k = i成立
那么对于下一个需要筛选的 i * prime[j+1]=prime[j]kprime[j+1]
我们令m = k *prime[j+1]> i
因此当i增大时,k * prime[j+1]肯定会被筛出来的,因此我们这一次就不用处理了
进而我们就保证了每一个非素数只被它的最小质因子筛选一次。
例题
【题目描述】
求1,2,⋯,N 中素数的个数。
【输入输出格式】
【输入】:
一个整数N。
【输出】:
1 个整数,表示素数的个数。
注意:
• 对于40% 的数据,1≤ N ≤10 6
• 对于80% 的数据,1≤ N ≤10 7
• 对于100% 的数据 1≤ N ≤10 8
【算法分析】
这么大的数据如果真的要让普通判断方法,那么一定会超时(O(n2)).那么就需要一个黑科技来解决——欧拉筛法。
【代码实现】
#include<bits/stdc++.h>
using namespace std;
const int N = 100000100;
int cnt, n;
int prime[N];
bool on[N];
int main()
{
scanf("%d", &n);
on[0] = on[1] = 1;
//0和1不是素数
for(int i = 2; i <= n; ++i){
if(!on[i]) prime[++cnt] = i;
//素数存入栈中
for(int j = 1; j <= cnt && i * prime[j] <= n; ++j){
on[i * prime[j]] = 1;
//标记为不是素数
if(i % prime[j] == 0) break;
//此处是精髓,如果当前的i能够被整除,那么这个数之前一定已经被处理过了,然后直接跳过。(欧拉筛法的关键)
}
}
long long tot = 0;
for(int i = 2; i <= n; ++i)
if(!on[i]) tot++;
printf("%lld\n", tot); //计算数量
//不过作者此处还有一个思路,就是由于存进去时候是有序的,所以使用一下low_bound()[上一篇博客有使用方法],或者二分查找也可以的。
return 0;
}
总结
上述代码中on数组和prime数组均为准确的
(on[] == 1 表示不是素数 on[] ==0表示是素数)
------------------------------------------------------------------------------------------------
2021年后续(欧拉筛法的优化 O(n / 2))
此处只给出处理的函数代码
#include<bits/stdc++.h>
using namespace std;
int ct, prime[100010];
int on[100010];
void pre(int n){
memset(on, 1, sizeof(on));
on[0] = on[1] = on[4] = 0;
on[2] = 1; prime[++ ct] = 2;
for(int i = 3; i <= n; i += 2){
if(on[i]) prime[++ ct] = i;
for(int j = 1; j <=ct && prime[j] * i <= n; ++ j){
on[prime[j] * i] = 0;
if(i % prime[j] == 0) break;
}
}
return ;
}
int main(){
int n;
scanf("%d", &n);
pre(n);
printf("%d", ct);
return 0;
}
要注意优化后的代码中on数组对于偶数的标记不准确,但是prime数组依旧准确。
(所有的素数中只有2一个偶数)