之前线性筛写的有点乱,重新整理一下,又看不懂的地方又改了改
前言
筛法是数论中的一个重要的思想,这篇文章介绍埃氏筛和它的优化欧拉筛.
思想
首先我们需要知道什么是素数和合数
我上高一才刚分清
- 合数: 可以被
和它自己及其他数整除的. - 素数(质数): 只能被它自己和
整除的.
对于一个数(除了
这个命题显然成立.
例如
他大于
我们的算法就是把非
核心代码
void sieve () { composite[0] = true; //0和1都是合数 composite[1] = true; for (register int i = 2; i <= n; i++) //一直筛到n for (register int j = i + i; j <= n; j += i) //如果一个数是合数,那么它的倍数也一定是合数 composite[j] = true; //标记合数 }
食用方法(以洛谷P3383 【模板】线性筛素数)为例
#include <algorithm> #include <cstdio> #include <iostream> #define maxn 10000005 using namespace std; bool composite[maxn] = {}; int num = 0, n, m; void sieve () { composite[0] = true; //0和1都是合数 composite[1] = true; for (register int i = 2; i <= n; i++) //一直筛到n for (register int j = i + i; j <= n; j += i) //如果一个数是合数,那么它的倍数也一定是合数 composite[j] = true; //标记合数 } int main () { ios::sync_with_stdio (false); //关同步 cin.tie (0); cout.tie (0); cin >> n >> m; sieve (); //筛一遍 int k; while (m--) { cin >> k; if (composite[k]) cout << "No" << endl; else cout << "Yes" << endl; } return 0; }
欧拉筛二三事
其实没什么辣,欧拉筛只是埃筛的一个小优化。
我们分析一下埃筛的代码:
void sieve () { composite[0] = true; //0和1都是素数 composite[1] = true; for (register int i = 2; i <= n; i++) //一直筛到n for (register int j = i + i; j <= n; j += i) //如果一个数是素数,那么它的倍数也一定是素数 composite[j] = true; //标记素数 }
注意这里:
for (register int j = i + i; j <= n; j += i) //如果一个数是素数,那么它的倍数也一定是素数 composite[j] = true; //标记素数
这段代码在无脑的筛掉元素,为什么称它“无脑”呢?
对于同一个元素,埃筛可能会将它筛掉好多次。
埃氏筛时间复杂度:
而接下来要介绍的欧拉筛的时间复杂度可以达到
算法思想
这里的欧拉筛全部以筛素数为例
对于一个合数,欧拉筛会确保它只被筛一次。
我们必须明确一下几点:
- 任何合数都可以表示为多个素数之积(唯一分解定理)
- 每个合数必有一个最小质因数
我们只需找出每个数的最小质因数即可。
先放一下代码,后面继续讲。
代码
void eulerSieve() { composite[0] = true; composite[1] = true; for (register int i = 2; i <= n; i++) { if (!composite[i]) //是素数的话,我们就把它加入列表中 ans[++num] = i; for (register int j = 1; j <= num && i * ans[j] <= n; j++) //i * ans[j] <= n 判边界 { composite[i * ans[j]] = true; if (i % ans[j] == 0) // important!!! break; } } }
由于一个素数乘上了一个其他的数就成了合数,而欧拉筛是要确保每个合数都只被筛一次,而且只能被它的最小质因数筛掉。我们把素数都存在ans数组里。
我们反过来想,如果这里不退出的话,接下来会把
Comments 1 条评论
写这么牛逼卧槽