当前位置:   article > 正文

欧拉筛法

欧拉筛法

欧拉(Euler)筛法是用于找到从 1 1 1开始,到给定的最大数之间的所有质数的一种筛法,其时间复杂度 O ( n ) O(n) O(n)。其中欧拉筛法有效地避免了埃拉托斯特尼(Eratosthenes)筛法中重复的筛选,保证了每个数只筛选一次,成功地降低了时间复杂度。

一、埃拉托斯特尼(Eratosthenes)筛法

埃拉托斯特尼(Eratosthenes)筛法是要得到自然数 n n n以内的全部质数,必须把不大于 n \sqrt{n} n 的所有质数的倍数剔除,剩下的就是质数。

这是因为如果是 n n n合数,那么它一定有一个不大于 n \sqrt{n} n 的质因子,所以过了 n \sqrt{n} n 就不用再更新了。

具体算法为:

  1. 2 ∼ ⌊ n ⌋ 2 \sim \lfloor \sqrt{n} \rfloor 2n 遍历,将质数记录在数组中;

  2. 如果是质数的话,将所有这个数的倍数记录为合数;

  3. ⌊ 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

但是我们会发现,这样有一些数被筛了多次,比如 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 2n遍历的时间复杂度是 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),产生了时间上的浪费,欧拉筛法有效地避免了其中重复的筛选。

二、欧拉(Euler)筛法

欧拉筛法的思想是每个合数只被它最小的质数筛掉,比如 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,,αk1 k ⩾ 1 k \geqslant 1 k1),如果筛到 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α11×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,,αk1 k ⩾ 2 k \geqslant 2 k2)一定会被其最小的质因子 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α11×p2α2××pkαk)筛掉,所以这种筛法满足充分性,即一定能将所有的合数都筛掉,所以欧拉筛法是一个时间复杂度为 O ( n ) O(n) O(n)的算法。

所以最后的算法是:

  1. 对于每个数,如果是质数,就保存下来;

  2. 对于所有在约定的最大范围内的数,将这个数和之前的质因子(一直到这个数所包含的最小的质因子,之后便不再向下执行)相乘的数记录为合数。

完整代码如下:

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;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/947507
推荐阅读
相关标签
  

闽ICP备14008679号