专栏文章
埃氏筛的优化——Wheel Factorization 学习笔记
算法·理论参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @mir2d1by
- 此快照首次捕获于
- 2025/12/04 14:37 3 个月前
- 此快照最后确认于
- 2025/12/04 14:37 3 个月前
Wheel Factorization
埃氏筛可以在 的时间复杂度内筛出 内的所有质数;线性筛可以进一步做到 。
Wheel Factorization 就是对埃氏筛的一种优化,可以在低于线性的时间内筛出所有质数。
Algorithm
主要思想:分块筛选。
还是考虑如何减少重复标记的次数。
设立一个阈值 ,先用线性筛筛出 的质数。
考虑一个结论:
记 表示 是否与 互质,那么对于正整数 有 。
那么对于每个 ,若 不与 互质,则形如 的数肯定都不与 互质,这些数一定是合数,之后筛的过程中不需要考虑。
接下来对值域分块,每 个为一段,分成 。依次考虑每段(第一段已经筛过),从上面分析知道只需考虑每个区间剩下的 个数。类似埃筛,枚举之前区间得到的(不被 包含的)质数 ,从 开始筛这个区间,这样就跳过了所有不与 互质的数,且每个被跳到的数只会被最小的那个质因子筛一次。
于是时间复杂度为 ,空间复杂度取决于质数个数和块长,。
考虑让 尽量小,质因子次数 没有意义,且让 中包含质因子的数量更多。对于 范围的筛质数,取 较优,此时 略大于 。
Code
实现的若干小优化:
- bitset 代替 bool 数组,空间 ,时间常数减小,并且方便清空;手写 bitset 更快。
- 在做除了第一段的区间,枚举 时可以只枚举奇数倍,偶数倍显然为合数。
// Problem: PRIMES2 - Printing some primes (Hard)
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/SP6488
// Memory Limit: 1 MB
// Time Limit: 2280 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
const int N=5.1e7+5;
const int B=510510; // 2*3*5*7*11*13*17
const int PB=92160;
const int SZ=1959;
inline void write(int x){
static char buf[20];
static int len=-1;
if(x<0)putchar('-'),x=-x;
do buf[++len]=x%10,x/=10;while(x);
while(len>=0)putchar(buf[len--]+48);
}
int pr[N],cnt;
bitset<B+5> vs;
int od[B+5],idx;
void init(int n){
vs.set(1);
for(int i=2;i<=B;++i){
if(!vs[i]) pr[++cnt]=i;
for(int j=1;j<=cnt&&i*pr[j]<=B;++j){
vs.set(i*pr[j]);
if(i%pr[j]==0) break;
}
}
for(int i=1;i<=B;++i){
if((i&1)&&(i%3)&&(i%5)&&(i%7)&&(i%11)&&(i%13)&&(i%17))
od[++idx]=i;
}
vs.reset();
for(int i=2;i<=SZ;++i){
int r=i*B,l=r-B+1;
if(i==SZ) r=n;
vs.reset();
for(int j=8;j<=cnt&&pr[j]*pr[j]<=r;++j){
int u=pr[j]*max(pr[j],(l-1)/pr[j]+1);
if(!(u&1)) u+=pr[j];
u-=l-1;
while(u<=B) vs.set(u),u+=(pr[j]<<1);
}
for(int j=1;j<=idx;++j) if(!vs[od[j]]) pr[++cnt]=od[j]+l-1;
}
}
signed main(){
int n(1e9); init(n);
for(int i=1;i<=cnt&&pr[i]<=n;i+=500) write(pr[i]),puts("");
return 0;
}
手写 bitset:
CPPtypedef unsigned long long ull;
ull vs[(B>>6)+5];
#define set(x) vs[x>>6]|=(1ull<<(x&63)) // vs.set(x)
#define ask(x) (vs[x>>6]>>(x&63)&1) // vs[x]
// 清空直接 memset
练习:SP6489(极度卡常)
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...