0.前言
写篇博客凑数doge
1.正文
首先是朴素算法,直接爆栗枚举,如果除得尽,就是合数,反之是素数。
一个小小的优化:枚举范围从2(因为所有正整数数都是1的倍数)到
n
\sqrt{n}
n
即可(因为如果有比
n
\sqrt{n}
n更大的因数,那该因数一定能和之前一个比
n
\sqrt{n}
n
小的n的因数配对)。
int pd(int x){
for (int i = 2; i <= sqrt(x); i++)
if (x%i == 0) return 0;
return 1;
}
上述说法是有缺陷的,因为每个数都是从2枚举到 n \sqrt{n} n,如果题目数据范围大,很有可能会超时。
所以接下来有一种更快的算法:埃氏筛。
对于每一个合数,一定存在一个m|n,其中m是n的最小素因子。利用这一特性,对每一个素数,枚举ta的倍数,将ta们都标记成合数,之后不再理会。
void pd(int x, int prime[], int &cnt){
for (int i = 2; i <= x; i++){
if (prime[i]) for (int j = i*i; j <= x; j += i) prime[j] = 0;
}
}
但是埃氏筛依然有缺陷,就是在标记过程中会重复标记,比如,2的倍数中有18,3的倍数中也有18,那么18就会被重复标记,导致效率下降。
那么另一种复杂度更优的做法欧拉筛(又称线性筛)来了。
欧拉筛:只用该合数的最小质数因子筛去这个数,所以一个数最多被筛掉一次,复杂度较前者更优。对于每一个当前数,枚举之前找到的质数,枚举当前数的素数倍,标记为合数。这样保证了每个数只被ta最小的素因子筛一次(因为大的素数会比小的素数慢找到)。欧拉筛只能筛到1e8。
const int MAXN = 1e6+7;
bool vis[MAXN];
int cnt, prime[MAXN];
void init(){
memset(vis, true, sizeof(vis));
vis[0] = vis[1] = false;
}
void pd(){
init();
for (int i = 2; i <= MAXN; i++){
if(vis[i]) prime[++cnt] = i;
for (int j = 1; j<=cnt && prime[j]*i<=MAXN; j++){
vis[prime[j]*i] = false;
if (i%prime[j] == 0) break;
}
}
}
一些模板题
进阶
2.结语
慢慢复习,慢慢恢复,慢慢变强。