赞
踩
欧拉(Euler)筛法是用于找到从 1 1 1开始,到给定的最大数之间的所有质数的一种筛法,其时间复杂度是 O ( n ) O(n) O(n)。其中欧拉筛法有效地避免了埃拉托斯特尼(Eratosthenes)筛法中重复的筛选,保证了每个数只筛选一次,成功地降低了时间复杂度。
埃拉托斯特尼(Eratosthenes)筛法是要得到自然数 n n n以内的全部质数,必须把不大于 n \sqrt{n} n 的所有质数的倍数剔除,剩下的就是质数。
这是因为如果是 n n n合数,那么它一定有一个不大于 n \sqrt{n} n 的质因子,所以过了 n \sqrt{n} n 就不用再更新了。
具体算法为:
从 2 ∼ ⌊ n ⌋ 2 \sim \lfloor \sqrt{n} \rfloor 2∼⌊n ⌋遍历,将质数记录在数组中;
如果是质数的话,将所有这个数的倍数记录为合数;
对 ⌊ n ⌋ + 1 \lfloor \sqrt{n} \rfloor + 1 ⌊n ⌋+1到 n n n进行遍历,是质数就加入数组中。
完整代码如下:
const int MAXN = 10000; int cnt; //记录总共有多少个质数 int Prime[MAXN]; //记录所有质数 int NotPrime[MAXN]; //打标记 void Eratosthenes_Prime(int n) { cnt = 0; memset(Prime, 0, sizeof(Prime)); memset(NotPrime, 0, sizeof(NotPrime)); for (int i = 2; i <= (int)sqrt(n + 0.5); ++i) { /*如果是质数,则记录下来*/ if (!NotPrime[i]) Prime[cnt++] = i; /*将后面质数i的倍数都记录成合数*/ for (int j = 2; i * j <= n; ++j) NotPrime[i * j] = 1; } /*后面的用前面的质数已经筛完,直接记录*/ for (int i = (int)sqrt(n + 0.5) + 1; i <= n; ++i) if (!NotPrime[i]) Prime[cnt++] = i; }
但是我们会发现,这样有一些数被筛了多次,比如 30 = 2 × 15 = 3 × 10 = 5 × 6 30=2 \times 15=3 \times 10=5 \times 6 30=2×15=3×10=5×6,这时候,从 2 ∼ n 2 \sim n 2∼n遍历的时间复杂度是 n n n,内层循环为 2 2 2到 n / i n / i n/i, i i i可以从 2 2 2取到 ⌊ n ⌋ \lfloor \sqrt{n} \rfloor ⌊n ⌋,所以时间复杂度为 log log n \log \log n loglogn,所以总的时间复杂度为 O ( n log log n ) O(n \log \log n) O(nloglogn),产生了时间上的浪费,欧拉筛法有效地避免了其中重复的筛选。
欧拉筛法的思想是每个合数只被它最小的质数筛掉,比如 30 30 30,只被 2 2 2筛掉,而在 3 3 3和 5 5 5的时候,不去判断 30 30 30是不是合数,所以到底怎么做呢?
如果 30 30 30被 2 2 2筛掉,可以不去考虑在 2 2 2的时候去筛 30 30 30,可以在 15 15 15的时候去筛 30 30 30,现在我们考虑的是,这个 15 15 15筛到什么时候停止,不再继续筛数,答案应该是到 15 15 15中包含的最小的质因子时停止。
这是因为,对于每一个数 m = p 1 α 1 × p 2 α 2 × ⋯ × p k α k m = p_1 ^ {\alpha_1} \times p_2 ^ {\alpha_2} \times \cdots \times p_k ^ {\alpha_k} m=p1α1×p2α2×⋯×pkαk(其中, p 1 < p 2 < ⋯ < p k p_1 < p_2 < \cdots < p_k p1<p2<⋯<pk, α 1 , α 2 , ⋯ , α k ⩾ 1 \alpha_1, \alpha_2, \cdots, \alpha_k \geqslant 1 α1,α2,⋯,αk⩾1, k ⩾ 1 k \geqslant 1 k⩾1),如果筛到 m × p 1 m \times p_1 m×p1之后继续向下筛,不妨设紧接着 p 1 p_1 p1之后的质数是 p 0 p_0 p0,即 p 0 > p 1 p_0 > p_1 p0>p1那么就会有
m × p 0 = p 1 α 1 × p 2 α 2 × ⋯ × p k α k × p 0 = p 1 × p 1 α 1 − 1 × p 0 × p 2 α 2 × ⋯ × p k α k m \times p_0 = p_1 ^ {\alpha_1} \times p_2 ^ {\alpha_2} \times \cdots \times p_k ^ {\alpha_k} \times p_0 = p_1 \times p_1 ^ {\alpha_1 - 1} \times p_0 \times p_2 ^ {\alpha_2} \times \cdots \times p_k ^ {\alpha_k} m×p0=p1α1×p2α2×⋯×pkαk×p0=p1×p1α1−1×p0×p2α2×⋯×pkαk
这说明 m × p 0 m \times p_0 m×p0是可以由 p 1 p_1 p1筛掉的,不需要再用 p 0 p_0 p0去筛了。
又每一个合数 m = p 1 α 1 × p 2 α 2 × ⋯ × p k α k m = p_1 ^ {\alpha_1} \times p_2 ^ {\alpha_2} \times \cdots \times p_k ^ {\alpha_k} m=p1α1×p2α2×⋯×pkαk(其中, p 1 < p 2 < ⋯ < p k p_1 < p_2 < \cdots < p_k p1<p2<⋯<pk, α 1 , α 2 , ⋯ , α k ⩾ 1 \alpha_1, \alpha_2, \cdots, \alpha_k \geqslant 1 α1,α2,⋯,αk⩾1, k ⩾ 2 k \geqslant 2 k⩾2)一定会被其最小的质因子 p 1 p_1 p1(即 p 1 α 1 − 1 × p 2 α 2 × ⋯ × p k α k p_1 ^ {\alpha_1 - 1} \times p_2 ^ {\alpha_2} \times \cdots \times p_k ^ {\alpha_k} p1α1−1×p2α2×⋯×pkαk)筛掉,所以这种筛法满足充分性,即一定能将所有的合数都筛掉,所以欧拉筛法是一个时间复杂度为 O ( n ) O(n) O(n)的算法。
所以最后的算法是:
对于每个数,如果是质数,就保存下来;
对于所有在约定的最大范围内的数,将这个数和之前的质因子(一直到这个数所包含的最小的质因子,之后便不再向下执行)相乘的数记录为合数。
完整代码如下:
const int MAXN = 10000; int cnt; //记录总共有多少个质数 int Prime[MAXN]; //记录所有质数 int NotPrime[MAXN]; //打标记 void Euler_Prime(int n) { cnt = 0; memset(Prime, 0, sizeof(Prime)); memset(NotPrime, 0, sizeof(NotPrime)); for (int i = 2; i <= n; ++i) { /*如果是质数,就记录下来*/ if (!NotPrime[i]) Prime[cnt++] = i; /*将合数筛去*/ for (int j = 0; j < cnt; ++j) { /*超过约定的最大数就不考虑了*/ if (i * Prime[j] > n) break; /*对于不超过最大的数,记录为合数*/ NotPrime[i * Prime[j]] = 1; /*超过p1的就不考虑了,因为在别处一定筛过了*/ if (i % Prime[j] == 0) break; } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。