Eratosthenes筛法和Euler筛法

写在前面

前段时间一直在准备期末考试,很长时间没有打理自己的博客,还好从这周开始,就是三周的小学期,时间还算富裕,争取找到之前的节奏,继续坚持下去!

这两种素数的筛选方法,第一种方法在之前的博客中使用过,但是只是顺便的提了一下,这里加了一个Euler筛选法,相对于Eratosthenes筛选法,时间复杂度更低一些


主要的参考文献:《线性筛法与积性函数》贾志鹏

Eratosthenes筛法

Eratosthenes筛选法用到的主要思想:当一个素数被发现时,那么是这个素数倍数的数一定不再是素数,运用这个思路就可以得到时间复杂度为O(N log(log(N)))的算法

void Prim(int n)
{
    //Eratosthenes筛选法
    memset(check,false,sizeof(check));
    int tot = 0;
    for(int i = 2;i  <= n;i ++)
        if(!check[i])
        {
            prim[tot++] = i;
            for(int j = 2*i;j <= n;j += i)
                check[j] = true;
        }
}

Euler筛法

上面的Eratosthenes筛选法,我们发现一个非素数可能会有很多次被判断,比如12,可以有2*6排除,3*4排除

那么我们为了去除这种重复,我们规定,只能使用一个数的最小质因数来排除这个数字,那么12就只能由2*6排除,24只能由2*12排除掉

那么我们怎么实现上面的要求呢?先看程序,后面解释这个内容

void Euler_prim(int n)
{
    //欧拉筛选法     避免上面筛选法中的重复筛选
    memset(check,false,sizeof(check));
    int tot = 0;
    for(int i = 2;i <= n;i ++)
    {
        if(!check[i]) prim[tot ++] = i;
        for(int j = 0;j < tot;j ++)    //遍历已经找到的素数
        {
            if(i * prim[j] > n) break;   //后面相乘已经超出 n 的范围,没有查找的必要了
            check[i * prim[j]] = true;    //表示这个数字不是素数
            if(i % prim[j] == 0) break;   
        }
    }
}

上面的if(i % prim[j] == 0 ) break;   是完成上面要求的重要条件

我们首先知道一个合数一定可以表示成素数的乘积,那么也一定可以表示成他的最小质因数乘以另外一个数,已知prim数组中的素数是依次递增的,当i满足i%prim[j] ==  0的时候,假如我们继续执行,那么下一个要判断为不是质数的数为:prim[j+1]*i   ,但是我们发现这个数的最小质因数一定是prim[j] ,因为这里的i满足i%prim[j] == 0 ,prim[j]是他的最小质因数,所以这里就和我们上面的要求有矛盾,当我们遇到后面x满足:prim[j] * x = prim[j+1]*i时,又会重新判断一次这个数字

也就是说:当我们遇到上面这个条件就要break  ,时间复杂度变为O(N)

写在后面

这个算法真的是理解了很长的时间,当然上面的理解是我个人的理解,如果有理解不当的地方,欢迎大家在评论区留言评论。

猜你喜欢

转载自blog.csdn.net/li1615882553/article/details/80902068